winsmith
winsmith

Reputation: 21622

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?

Upvotes: 6

Views: 2170

Answers (2)

0xTim
0xTim

Reputation: 5620

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

Upvotes: 18

winsmith
winsmith

Reputation: 21622

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.

Upvotes: 2

Related Questions