Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot create siblings relation between same models #1932

Closed
LinusGeffarth opened this issue Mar 17, 2019 · 6 comments
Closed

Cannot create siblings relation between same models #1932

LinusGeffarth opened this issue Mar 17, 2019 · 6 comments

Comments

@LinusGeffarth
Copy link

LinusGeffarth commented Mar 17, 2019

I want to create a siblings relationship between two User models, for a sort-of "friend" feature, similar to Facebook.
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.

Is this a Vapor bug, or just me doing something wrong here?

Environment

  • Vapor Framework version: 3.0.0
  • Vapor Toolbox version: 3.1.10
  • OS version: 10.14.3
@jbl8084
Copy link

jbl8084 commented Mar 17, 2019

Is MySQLPivot somehow a ModifiablePivot already?

@LinusGeffarth
Copy link
Author

LinusGeffarth commented Mar 17, 2019

Don't think so. For attaching, I had to have it conform to ModifiablePivot. @jbl8084

@grundoon
Copy link
Member

grundoon commented Mar 18, 2019

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 {
    // the friends of the user
    var friends: Siblings<User, User, UserFriendsPivot> {
        return siblings(UserFriendsPivot.leftIDKey, UserFriendsPivot.rightIDKey)
    }

    // the users who have friended the user 
    var friendOf: Siblings<User, User, UserFriendsPivot> {
        return 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.)

@LinusGeffarth
Copy link
Author

LinusGeffarth commented Mar 19, 2019

@grundoon, thank you for your answer, I was able to compile the project w/ the code you provided.
Can I use isAttached and detach, or do I have to work around those too?
If so, what's the equivalents?

@grundoon
Copy link
Member

@LinusGeffarth Off the top of my head, I believe any method that accesses through the keyed Siblings<Base, Related, Through> relations you've defined above (such as isAttached, detach and detachAll) should work right out of the box.

user.friends.isAttached(friend, on: req)
     ^^^^^^^

@LinusGeffarth
Copy link
Author

Yeah, I've found that both methods do work. Thanks for your help! @grundoon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants