Skip to content

Instantly share code, notes, and snippets.

@m-barthelemy
Last active June 14, 2020 03:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m-barthelemy/63ae5917383263b435cd61651dceb6e0 to your computer and use it in GitHub Desktop.
Save m-barthelemy/63ae5917383263b435cd61651dceb6e0 to your computer and use it in GitHub Desktop.
Extensions for using KeyPaths in Vapor4 Fluent migrations
import Vapor
import Fluent
// Example models
final class Organization: Model, Content {
static var schema = "organizations"
@ID (key: .id)
var id: UUID?
@Field (key: "name")
var name: String?
@Children(for: \.$organization)
var users: [User]
init() {}
}
final class User: Model, Content {
static var schema = "users"
@ID (key: .id)
var id: UUID?
@Field (key: "name")
var name: String?
@Field (key: "email")
var email: String?
@OptionalParent (key: "org_id")
var organization: Organization?
init() {}
}
// Example migrations using the generic extensions
struct CreateOrgs: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
return database.schema(Organization.schema)
.id()
.field(\Organization.$name, .string, .required)
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
return database.schema(Organization.schema).delete()
}
}
struct CreateUsers: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
return database.schema(User.schema)
.id()
.field(\User.$name, .string)
.field(\User.$email, .string, .required)
.unique(on: \User.$email)
.foreignKey(\User.$organization.$id, references: \Organization.$id, onDelete: .cascade, onUpdate: .cascade)
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
return database.schema(User.schema).delete()
}
}
// Generic extensions allowing to use KeyPaths in migrations
extension SchemaBuilder {
public func field<T, U>(
_ key: KeyPath<T, U>,
_ dataType: DatabaseSchema.DataType,
_ constraints: DatabaseSchema.FieldConstraint...
) -> Self where T: Model, U: QueryableProperty {
self.field(.definition(
name: .key(T.path(for: key).last!),
dataType: dataType,
constraints: constraints
))
}
/// Passing multiple keypaths confuses the compiler, whereas one is fine.
public func unique<T, U>(on fields: KeyPath<T, U>..., name: String? = nil) -> Self where T: Model, U: QueryableProperty {
self.constraint(.constraint(
.unique(fields: fields.map { .key(T.path(for: $0).last!) }),
name: name
))
return self
}
public func foreignKey<T, U, V, W>(
_ field: KeyPath<T, V>,
references foreignField: KeyPath<U, W>,
onDelete: DatabaseSchema.ForeignKeyAction = .noAction,
onUpdate: DatabaseSchema.ForeignKeyAction = .noAction,
name: String? = nil
) -> Self where T:Model, U: Model, V: QueryableProperty, W: QueryableProperty {
self.schema.createConstraints.append(.constraint(
.foreignKey(
[.key(T.path(for: field).last!)],
U.schema,
[.key(U.path(for: foreignField).last!)],
onDelete: onDelete,
onUpdate: onUpdate
),
name: name
))
return self
}
public func updateField<T, U>(
_ key: KeyPath<T, U>,
_ dataType: DatabaseSchema.DataType
) -> Self where T: Model, U: QueryableProperty {
self.updateField(.dataType(
name: .key(T.path(for: key).last!),
dataType: dataType
))
}
}
extension DatabaseSchema.FieldConstraint {
public static func references<T, U>(
_ field: KeyPath<T, U>,
onDelete: DatabaseSchema.ForeignKeyAction = .noAction,
onUpdate: DatabaseSchema.ForeignKeyAction = .noAction
) -> Self where T: Model, U: QueryableProperty {
.foreignKey(
T.schema,
.key(T.path(for: field).last!),
onDelete: onDelete,
onUpdate: onUpdate
)
}
}
@m-barthelemy
Copy link
Author

Updated for FluentKit 1.0.0-rc.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment