Strongly type queries
miroslavkovac opened this issue · 4 comments
miroslavkovac commented
Is there a way to write strongly typed queries?
For example, instead of:
db.select()
.column("size")
.from("planets")
.where("name", .equal, "Earth")
I would like to write something like this:
db.select()
.column(\Plant.$size)
.from(Planets.self)
.where(\Planet.$name, .equal, "Earth")
I am migrating the project form Vapor 3 to Vapor 4 and couldn't find a way of doing this, while it was previously possible. Thank you.
0xTim commented
@miroslavkovac which bits aren't working?
@madsodgaard you showed something in Discord that looked like this should work
madsodgaard commented
@0xTim @miroslavkovac is right in that is not supported natively by SQL-Kit. I added some extensions to my code to help with using Fluent models in SQLKit code
This won't work if you use
@Group
in Fluent
Here are the helpers:
// From https://github.com/vapor/fluent-kit/pull/343
extension FieldKey: SQLExpression {
/// See `SQLExpression`.
public func serialize(to serializer: inout SQLSerializer) {
SQLIdentifier(self.string(for: serializer.dialect))
.serialize(to: &serializer)
}
// Converts `FieldKey` to a string.
//
// `.description` is not used here since that isn't
// _necessarily_ a SQL compatible value.
//
// SQLDialect is passed in case a specific dialect may
// need to have special values for id / aggregate.
private func string(for dialect: SQLDialect) -> String {
switch self {
case .id:
return "id"
case .aggregate:
return "aggregate"
case .prefix(let prefix, let key):
return prefix.string(for: dialect) + key.string(for: dialect)
case .string(let string):
return string
}
}
}
extension KeyPath: SQLExpression where Root: Fields, Value: AnyQueryableProperty {
/// See `SQLExpression`.
public func serialize(to serializer: inout SQLSerializer) {
Root()[keyPath: self].path.first!.serialize(to: &serializer)
}
}
extension SQLPredicateBuilder {
@discardableResult
func `where`<E>(_ lhs: SQLExpression, _ op: SQLBinaryOperator, _ rhs: E) -> Self
where E: Encodable
{
return self.where(lhs, op, SQLBind(rhs))
}
}
This allows you to write the code you specified in your issue.