Group Group Group Group Group Group Group Group Group

Optional Parent/Child relationship

#1

Hi,
I’m trying to model an optional Parent/Child relationship.
I have 2 models: an User and a Location.
Every User may belong to a Location:

// User Model
final class User: Codable {
    var id: UUID?
    var name: String
    var username: String
    var locationID: Location.ID?

    init(name: String, username: String, locationID: Location.ID? = nil) {
        self.name = name
        self.username = username
        self.locationID = locationID
    }
}

extension User {
    var location: Parent<User, Location>? {
        return parent(\.locationID)
    }
}

// Location Model
final class Location: Codable {
    var id: Int?
    var short: String
    var description: String

    init(short: String, description: String) {
        self.short = short
        self.description = description
    }
}

As you can see Usre’s locationID property is Optional<Location.ID> because a User may not have a Location.

In UsersController I have a getLocationHandler(req:) to extract Location, if any, for a particular User

func getLocationHandler(_ req: Request) throws -> Future<Location?> {
    return try req
        .parameters.next(User.self)
        .flatMap(to: Location.self) {
            user in
            user.location?.get(on: req)
    }
}

The code doesn’t compile on this line:

user.location?.get(on: req)        // Expression type `_?` is ambiguous without more context 

The message is quite cryptic to me: I think I have to explicit some types to make the things clear to the compiler, but I can’t figure out what.

Could you please help me?

Thank you, Luca

#2

I managed to compile modifying a bit the function: now it returns Future Location if User’s locationID property != nil, else 404 Not Found

func getLocationHandler(_ req: Request) throws -> Future<Location> {
    return try req
        .parameters.next(User.self)
        .flatMap(to: Location.self) {
            (user: User) -> EventLoopFuture<Location> in
            guard let location = user.location?.get(on: req) else {
                throw Abort.init(HTTPResponseStatus.notFound)
            }
        return location
    }
}

I’m not sure HTTPResponseStatus.notFound is the right error code in this case: the caller might have a hard time trying to figure out if it was the User to be not found or if the User was found, but she didn’t have a locationID.

Any suggestion?

#3

@0xtim Can you please help with this when you get a chance? Thank you - much appreciated! :]

#4

Your main problem from the initial question is that you can’t return a Future<Location?> since Vapor doesn’t know what to return if the location doesn’t exist.

In terms of the correct status code it’s up to you. You could return an empty response, but if a user can only have one location, returning 404 makes sense as well.

1 Like
#5

Thank you 0xtim for your help!