skip to Main Content

Hope someone can see what I’m missing here. I’m trying to create a pivot table entry between users and tournaments, two tables in my app. As you’d expect, a user is authorized during the routing, which ends up in this method (I’ve tried creating the pivot from both sides, as the commented line indicates):

    func acceptInvitation(req: Request) -> EventLoopFuture<Response> {
        let user : User = req.auth.get(User.self)!
        let uId : UUID = user.id!
        let iId = UUID.init(uuidString: req.parameters.get("iId")!)
        return Invitation.query(on: req.db)
            .filter(.$id == iId!)
            .with(.$owner)
            .first().flatMap { i -> EventLoopFuture<Response> in
                i?.acceptances!.append((uId))
                i?.update(on: req.db)
                i!.owner.acceptances!.append(uId)
                i?.owner.update(on: req.db)
                i?.owner.$tournamentPlayers.attach(user, on: req.db)
//                user.$playerTournaments.attach(i!.owner, on: req.db)
                return req.eventLoop.future(req.redirect(to: "/")).encodeResponse(for: req)
            }
    }

The migration is fine, and postgres displays it as:

vapor-database=# d tournament-player-pivot;
        Table "public.tournament-player-pivot"
    Column    | Type | Collation | Nullable | Default 
--------------+------+-----------+----------+---------
 id           | uuid |           | not null | 
 tournamentID | uuid |           | not null | 
 playerID     | uuid |           | not null | 
Indexes:
    "tournament-player-pivot_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "tournament-player-pivot_playerID_fkey" FOREIGN KEY ("playerID") REFERENCES users(id)
    "tournament-player-pivot_tournamentID_fkey" FOREIGN KEY ("tournamentID") REFERENCES tournament(id)

in Tournament, it’s defined thusly:

    @Siblings(
      through: TournamentPlayerPivot.self,
      from: .$tournament,
      to: .$player)
    var tournamentPlayers: [User]

and the inverse, in Users is:

    @Siblings(
      through: TournamentPlayerPivot.self,
      from: .$player,
      to: .$tournament)
    var playerTournaments: [Tournament]

All of which seems to follow standard practice pretty closely. But when the method above is called (all the other code executes as expected), there’s no entry in the table.

EDIT: I omitted the class for the pivot: here that is:

import Foundation
import Fluent

final class TournamentPlayerPivot: Model {
    static let schema = "tournament-player-pivot"

    @ID
    var id: UUID?
    @Parent(key: "tournamentID")        var tournament: Tournament
    @Parent(key: "playerID")              var player: User

    init() {}

    init(
        id: UUID? = nil,
        tournament: Tournament,
        player: User
        ) throws {
            self.id = id
            self.$tournament.id = try tournament.requireID()
            self.$player.id = try player.requireID()
        }
}

2

Answers


  1. Chosen as BEST ANSWER

    @Nick: yes, that was substantially it. I've got an error related to column naming, but this was the code that got through to the point where the database was getting the message:

    func acceptInvitation(req: Request) -> EventLoopFuture<Response> {
            let user : User = req.auth.get(User.self)!
            let uId : UUID = user.id!
            let iId = UUID.init(uuidString: req.parameters.get("iId")!)
            return Invitation.query(on: req.db)
                .filter(.$id == iId!)
                .with(.$owner)
                .first().flatMap { i -> EventLoopFuture<Response> in
                    i?.acceptances!.append((uId))
                    return (i?.update(on: req.db).flatMap { _ -> EventLoopFuture<Response> in
                        i!.owner.acceptances!.append(uId)
                        return (i?.owner.update(on: req.db).flatMap {
                            (i?.owner.$tournamentPlayers.attach(user, on: req.db).flatMap { _ in
                                return req.eventLoop.future(req.redirect(to: "/")).encodeResponse(for: req)
                            })!
                        })!
                    })!
                    
                }
        }
    

    So, mostly down to the return and the getting the return values set explicitly, along with the nesting, nesting, nesting.

    Thanks for the input.


  2. The two updates and the attach may be conflicting. I would try re-structuring your code as:

    func acceptInvitation(req: Request) -> EventLoopFuture<Response> {
        let user : User = req.auth.get(User.self)!
        let uId : UUID = user.id!
        let iId = UUID.init(uuidString: req.parameters.get("iId")!)
        return Invitation.query(on: req.db)
            .filter(.$id == iId!)
            .with(.$owner)
            .first().flatMap { i -> EventLoopFuture<Response> in
                i?.acceptances!.append((uId))
                i?.update(on: req.db).flatMap { _ in
                    i!.owner.acceptances!.append(uId)
                    i?.owner.update(on: req.db).flatMap { _ in
                        i?.owner.$tournamentPlayers.attach(user, on: req.db).flatMap { _ in
                            return req.eventLoop.future(req.redirect(to: "/")).encodeResponse(for: req)
                        }
                    }
                }
            }
    }
    

    I have definitely seen missing/unchanged data in the context of multiple updates where you don’t map the future result and ‘wait’ for the earlier update(s) to happen. My guess is that something similar is happening with the attach. You may get some compiler groans and need to insert explicit return types.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search