Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Fluent / Vapor 4, how do I filter a list based on a foreign key?

so I have models names Organization, User, and App. Apps and Users both belong to organizations.

final class Organization: Model, Content {
    static let schema = "organizations"

    @ID(key: .id)
    var id: UUID?

    @Field(key: "name")
    var name: String
    
    @Children(for: \.$organization)
    var users: [User]
}

final class App: Model, Content {
    static let schema = "apps"

    @ID(key: .id)
    var id: UUID?

    @Field(key: "name")
    var name: String

    @Parent(key: "organization_id")
    var organization: Organization
}


final class User: Model, Content {
    static let schema = "users"

    @ID(key: .id)
    var id: UUID?

    @Field(key: "email")
    var email: String

    @Field(key: "password_hash")
    var passwordHash: String
    
    @Parent(key: "organization_id")
    var organization: Organization
}

Now, basically what I want to do is, given a user, I want to get all the Apps that belong to that user's organization.

I can get the user from my auth system, but then all I have is this query:

let user = try req.auth.require(User.self)
return App.query(on: req.db).filter(\.$organization.id == user.$organization.id).all()

This fails with the error message Operator function '==' requires that 'UUID' conform to 'QueryableProperty'.

What should I do instead?

like image 741
winsmith Avatar asked Aug 03 '20 18:08

winsmith


2 Answers

You need to provide the key path to the projected value of a property wrapper you want to filter on, instead of the value itself. So as you discovered it needs to be:

.filter(\.$organization.$id == user.$organization.id)

The reason why you have to use the property wrapper's projected value instead of just the property itself is because the property wrapper contains the information it needs to perform the query (like the column name) which wouldn't be available with just the property

like image 129
0xTim Avatar answered Oct 11 '22 21:10

0xTim


User jimmy#2207 from the Vapor discord provided me with this answer:

let user = try req.auth.require(User.self)
        
// Only show user's orgs' apps, thanks to @jhoughjr
return App.query(on: req.db)
    .filter(\.$organization.$id == user.$organization.id)
    .all()

I don't exactly know why it works, but this is the correct combination of backslashes and dollar signs.

like image 40
winsmith Avatar answered Oct 11 '22 19:10

winsmith