vapor / fluent-kit

Swift ORM (queries, models, and relations) for NoSQL and SQL databases
MIT License
218 stars 116 forks source link

Enable looking up database names of multiple model key paths at once #494

Closed gwynne closed 2 years ago

gwynne commented 2 years ago

This loosens the requirement on the value of a KeyPath passed to Fields.path(for:) to AnyQueryableProperty, which has no associated types or Self requirements, and can thus be treated as an existential (e.g. any AnyQueryableProperty). While existentials are best avoided when possible, in this case, the relaxed definition allows usefully operating on the resolved database field names of an arbitrary set of key paths without the use of variadic generics.

As this is a very confusing and opaque description of the behavior, here is an example of a helper method which was impossible to write before:

extension SchemaBuilder {
    func fields<M: FluentKit.Model>(from _: M.Type = M.self, _ keypaths: KeyPath<M, AnyQueryableProperty>...) -> Self {
        return keypaths.reduce(self) { builder, keypath in
            // N.B.: Despite the name and array type, `Fields.path(for:)` never returns more than one `FieldKey` in practice.
            // Implementing `figureOutDataType(for:)` is left as an exercise for the reader.
            return builder.field(M.path(for: keypath).first!, figureOutDataType(for: keypath))
        }
    }
}

// Usage would be something like:
database.schema(M.schema)
    .fields(\.$id, \.$foo, \.$bar, \$.baz)
    .create()

Previously, such a helper could only have taken a single KeyPath parameter. This is admittedly a contrived example (and by design very far from a complete "automatic migration" implementation), and it's a fairly small convenience to call .fields(a,b,c,d) versus .field(a).field(b).field(c).field(d), but it's nonetheless useful. The generic constraint should never have been so strict in any event. (And field keys should not be wrapped in arrays all the time, but that's a PR for another day.)

Note: While technically the relaxed constraint is a "new" public API, the change is small enough and the difference invisible enough that I've marked this semver-patch; even for me, there's such a thing as being too pedantic about the rules 🙂.

VaporBot commented 2 years ago

These changes are now available in 1.23.2