Swiftpack.co - Package - vapor/fluent-kit

Github

link
Stars: 18
Help us keep the lights on

Releases

1.0.0-beta.1 - Oct 24, 2019

  • Added a new @OptionalParent relation where the child's parent ID is optional. (#78)
  • filter operators now support comparing two columns. (#79)
  • Added a new protocol ModelAlias for handling queries that join a given table more than once. (#75)
final class Match: Model {
    ...

    @Parent(key: "home_team_id")
    var homeTeam: Team

    @Parent(key: "away_team_id")
    var awayTeam: Team
}

final class Team: Model {
    ...

    @Children(from: \.$homeTeam)
    var homeMatches: [Match]

    @Children(from: \.$awayTeam)
    var awayMatches: [Match]
}

struct HomeTeam: ModelAlias {
    typealias Model = Team
    static var alias: String { "home_teams" }
}

struct AwayTeam: ModelAlias {
    typealias Model = Team
    static var alias: String { "away_teams" }
}

let matches = try Match.query(on: self.database)
    .join(HomeTeam.self, on: \Match.$homeTeam == \Team.$id)
    .join(AwayTeam.self, on: \Match.$awayTeam == \Team.$id)
    .filter(HomeTeam.self, \Team.$name == "a")
    .all().wait()

for match in matches {
    let home = try match.joined(HomeTeam.self)
    let away = try match.joined(AwayTeam.self)
    print(match.name)
    print("home: \(home.name)")
    print("away: \(away.name)")
}
  • Database can now specify an EventLoopPreference and Logger. (#84)

This is in line with changes to how Vapor 4.0.0 Beta 1 handles services.

  • SchemaBuilder.update is now implemented.
  • Fixed an issue where @Children would serialize as an empty container if not eager loaded. (#70)
  • Fixed an issue preventing the sort method from called with a field key path. (#64)
  • Added support for accessing the cached model ID from @Children relations. (#71)

1.0.0-alpha.3.1 - Sep 11, 2019

  • Fixes a bug causing @Parent.query and @Parent.get to filter on the wrong column. (#68)

Notes: Previously these methods would attempt to filter the related parent by the child's key. They now filter on the parent's key (identifier).

1.0.0-alpha.3 - Aug 14, 2019

  • @Field and @Parent properties now require a key String

Notes: The reflection logic required to automatically infer keys was complex and computation heavy. Requiring the user to supply keys simplifies a lot of the internal logic. This also allows Fluent to provide clearer error messages. Moreover, this change increases the consistency of large models where there are more likely to be label / key mismatches.

  • @Children now requires the label from:
  • New @ID property replaces @Field for model identifiers.

Notes: ID is a special field that holds additional information like whether or not the model has been saved to the database yet and a reference to the DB output for decoding joins. By limiting this property to one-per-model, Fluent reduces the overall size of models.

  • @ID property has a new Generator parameter with three options:
public enum ID.Generator {
    case user
    case random
    case database
}

Notes: This change makes Fluent's supported ID generation methods more clear. You can now explicitly choose whether you want to supply user-generated IDs, have Fluent generate random IDs, or let your database generate the ID. These options work in conjunction with Fluent's RandomGeneratable protocol and SchemaBuilder's default constraints respectively.

  • Model.ID has been renamed to Model.IDValue to avoid conflicting with the new @ID property.

  • New @Siblings property that connects two models in a many-to-many relation. This relation requires a third "pivot" model that has two @Parent properties to the models that will be related. The pivot model can have additional fields and relations if desired.

Example model with siblings relation:

final class Tag: Model {
    static let schema = "tags"

    @ID(key: "id")
    var id: Int?

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

    @Siblings(through: PlanetTag.self, from: \.$tag, to: \.$planet)
    var planets: [Planet]

    init() { }

    init(id: Int? = nil, name: String) {
        self.id = id
        self.name = name
    }
}

Example pivot model:

final class PlanetTag: Model {
    static let schema = "planet+tag"
    
    @ID(key: "id")
    var id: Int?

    @Parent(key: "planet_id")
    var planet: Planet

    @Parent(key: "tag_id")
    var tag: Tag

    init() { }

    init(planetID: Int, tagID: Int) {
        self.$planet.id = planetID
        self.$tag.id = tagID
    }
}
  • SoftDeletable and Timestampable have been replaced by a new @Timestamp property with three possible triggers.
public enum Timestamp.Trigger {
    case create
    case update
    case delete
}

Notes: This change allows for more flexibility in which timestamps each model should have and how to name them. For example, you are no longer required to have both createdAt and updatedAt timestamps. In order to make a model soft-deletable, simply add a @Timestamp property with the delete trigger.

  • QueryBuilder.withSoftDeleted has been renamed to withDeleted.

Notes: With the removal of the SoftDeletable protocol, this more concise name better matches the @Timestamp's .delete trigger name.

  • SchemaBuilder is now decoupled from Model (#59)

Example model and associated migration:

final class Planet: Model {
    static let schema = "planets"

    @ID(key: "id")
    var id: Int?

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

    @Parent(key: "galaxy_id")
    var galaxy: Galaxy

    @Siblings(through: PlanetTag.self, from: \.$planet, to: \.$tag)
    var tags: [Tag]

    init() { }

    init(id: Int? = nil, name: String, galaxyID: Galaxy.IDValue) {
        self.id = id
        self.name = name
        self.$galaxy.id = galaxyID
    }
}

struct PlanetMigration: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        return database.schema("planets")
            .field("id", .int, .identifier(auto: true))
            .field("name", .string, .required)
            .field("galaxy_id", .int, .required)
            .create()
    }

    func revert(on database: Database) -> EventLoopFuture<Void> {
        return database.schema("planets").delete()
    }
}

Notes: Decoupling migrations from models is an important step for making Fluent more reliable and predictable for production use cases and team development.

  • Model.entity and Model.name have been combined to Model.schema

Notes: entity was vague and the difference between name and entity was not clear. Combining these properties and using the name schema makes the relation to the string used by SchemaBuilder more clear.

1.0.0-alpha.2 - Aug 1, 2019

New:

  • Model has been combined with Row and makes use of Swift 5.1's property wrappers:
final class Planet: Model {
    @Field var id: Int?
    @Field var name: String
    @Parent var galaxy: Galaxy

    init() { }

    init(id: Int? = nil, name: String, galaxyID: Galaxy.ID) {
        self.id = id
        self.name = name
        self.$galaxy.id = galaxyID
    }
}

final class Galaxy: Model {
    @Field var id: Int?
    @Field var name: String
    @Children(\.$galaxy) var planets: [Planet]

    init() { }

    init(id: Int? = nil, name: String) {
        self.id = id
        self.name = name
    }
}
  • Migrations are now required for all models and auto migrations have been temporarily disabled:
struct GalaxyMigration: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        return Galaxy.schema(on: database)
            .field(\.$id, .int, .identifier(auto: true))
            .field(\.$name, .string, .required)
            .create()
    }

    func revert(on database: Database) -> EventLoopFuture<Void> {
        return Galaxy.schema(on: database).delete()
    }
}

struct PlanetMigration: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        return Planet.schema(on: database)
            .field(\.$id, .int, .identifier(auto: true))
            .field(\.$name, .string, .required)
            .field("galaxy_id", .int, .required)
            .create()
    }

    func revert(on database: Database) -> EventLoopFuture<Void> {
        return Planet.schema(on: database).delete()
    }
}

1.0.0-alpha.1.3 - Jun 12, 2019

New:

  • Implemented missing model filter operators (#37, #38)
  • Added operator filtering for joined models (#35, #38)