Search code examples
swiftrelationshipvapor

Siblings relationship between same models in Vapor


I have a User model which I want to add a friends property to. Friends, are supposed to be other Users.

I created the UserFriendsPivot:

final class UserFriendsPivot: MySQLPivot, ModifiablePivot {
    var id: Int?
    var userID: User.ID
    var friendID: User.ID

    typealias Left = User
    typealias Right = User

    static var leftIDKey: WritableKeyPath<UserFriendsPivot, Int> {
        return \.userID
    }

    static var rightIDKey: WritableKeyPath<UserFriendsPivot, Int> {
        return \.friendID
    }

    init(_ user: User, _ friend: User) throws {
        self.userID   = try user  .requireID()
        self.friendID = try friend.requireID()
    }
}

extension UserFriendsPivot: Migration {
    public static var entity: String {
        return "user_friends"
    }
}

I added the friends property to User:

var friends: Siblings<User, User, UserFriendsPivot> {
    return siblings()
}

Now, I'm seeing the following error on the line with return siblings():

Ambiguous use of 'siblings(related:through:)'

I tried to replace it with:

return siblings(related: User.self, through: UserFriendsPivot.self)

...without any luck.

I know that the two code snippets should work, because I straight-up copied them from another siblings relationship I built between Event and User that is working just fine.
The only difference I'm seeing is that I'm trying to build a relationship between the same models.

What can I do?


Solution

  • The issue here is that in a same-Model (User-User) siblings relation, Fluent cannot infer which sibling you are referring to – the sides need to be specified.

    extension User {
        // friends of this user
        var friends: Siblings<User, User, UserFriendsPivot> {
            siblings(UserFriendsPivot.leftIDKey, UserFriendsPivot.rightIDKey)
        }
    
        // users who are friends with this user
        var friendOf: Siblings<User, User, UserFriendsPivot> {
            siblings(UserFriendsPivot.rightIDKey, UserFriendsPivot.leftIDKey)
        }
    }
    

    The other same-Model consequence is that you will not be able to use the attach convenience method to add to the pivot table, and need to manually create instead:

    let pivot = try UserFriendsPivot(user, friend)
    pivot.save(on: req)
    

    (There are other approaches to work around this, I just find these straightforward ways above the easiest to use. Specifying the sides and reversing the key positions to obtain the inverse relation are the important concepts.)


    as answered by grundoon