@@ -40,6 +40,42 @@ extension SQLQueryFetcher {
4040 }
4141 }
4242
43+ /// For each keypath in an arbitrary list of Fluent model keypaths, decodes the appropriate column from the result
44+ /// row (if any), returning the combined results as a tuple where the types of each item of the tuple are inferred
45+ /// from the property referenced by the corresponding keypath. If the query produced no results, `nil` is returned.
46+ /// This is identical to ``first<each Schema, each QueryAddressableProperty>(decodingColumns: repeat...)``, except
47+ /// that on that method, each KeyPath can refer to a different Schema,forcing the caller to specify the root type on
48+ /// all of them. However, it is a very common use case to specify many keypaths from the same model in a row, e.g.,
49+ /// `.first(decodingColumns: \MyModel.$foo, \MyModel.$bar, \MyModel.$baz, \MyModel.$bam)`. This quickly becomes quite
50+ /// tedious. By contrast, this method accepts only a single `Schema` type, and all KeyPaths are assumed to refer to
51+ /// it, allowing the previous example to be written as `.first(decodingColumnsOf: MyModel.self, \.$bar, \.$baz, \.$bam)`.
52+ ///
53+ /// Example:
54+ ///
55+ /// ```swift
56+ /// final class MyModel: FluentKit.Model, @unchecked Sendable {
57+ /// @ID(custom: .id) var id: Int?
58+ /// @Field(key: "field1") var field1: String
59+ /// @Parent(key: "parent_id") var parent: ParentModel
60+ /// @Enum(key: "field2") var field2: SomeEnum
61+ /// init() {}
62+ /// }
63+ ///
64+ /// let tuple/*(id, field1, parentId, field2)*/ = try await sqlDatabase.select()
65+ /// .columns(\MyModel.$id, \MyModel.$field1, \MyModel.$parent, \MyModel.$field2)
66+ /// .from(MyModel.self)
67+ /// .first(decodingColumns: \MyModel.$id, \MyModel$field1, \MyModel.$parent, \MyModel.$field2)
68+ ///
69+ /// // type(of: tuple) == (Int, String, ParentModel.IDValue, SomeEnum).self
70+ /// ```
71+ public func first< S: Schema , each P : QueryAddressableProperty > (
72+ decodingColumnsOf: S . Type , _ keypaths: repeat KeyPath < S , each P >
73+ ) async throws -> ( repeat ( each P ) . QueryablePropertyType. Value) ? {
74+ try await self . first ( ) . map {
75+ try $0. decode ( columnsOf: S . self, repeat each keypaths)
76+ }
77+ }
78+
4379 /// Allow specifying a Fluent model keypath as a column name when decoding multiple query fetcher results.
4480 public func all< M: Schema , P: QueryAddressableProperty > (
4581 decodingColumn keypath: KeyPath < M , P >
@@ -76,5 +112,41 @@ extension SQLQueryFetcher {
76112 try $0. decode ( columns: repeat each keypaths)
77113 }
78114 }
115+
116+ /// For each keypath in an arbitrary list of Fluent model keypaths, decodes the appropriate column from each result
117+ /// row, returning the combined results as an array of tuples where the types of each item of the tuple are inferred
118+ /// from the property referenced by the corresponding keypath. This is identical to
119+ /// ``all<each Schema, each QueryAddressableProperty>(decodingColumns: repeat...)``, except that on that method, each
120+ /// KeyPath can refer to a different Schema,forcing the caller to specify the root type on all of them. However, it is
121+ /// a very common use case to specify many keypaths from the same model in a row, e.g.,
122+ /// `.all(decodingColumns: \MyModel.$foo, \MyModel.$bar, \MyModel.$baz, \MyModel.$bam)`. This quickly becomes quite
123+ /// tedious. By contrast, this method accepts only a single `Schema` type, and all KeyPaths are assumed to refer to
124+ /// it, allowing the previous example to be written as `.all(decodingColumnsOf: MyModel.self, \.$bar, \.$baz, \.$bam)`.
125+ ///
126+ /// Example:
127+ ///
128+ /// ```swift
129+ /// final class MyModel: FluentKit.Model, @unchecked Sendable {
130+ /// @ID(custom: .id) var id: Int?
131+ /// @Field(key: "field1") var field1: String
132+ /// @Parent(key: "parent_id") var parent: ParentModel
133+ /// @Enum(key: "field2") var field2: SomeEnum
134+ /// init() {}
135+ /// }
136+ ///
137+ /// let tuples = try await sqlDatabase.select()
138+ /// .columns(\MyModel.$id, \MyModel.$field1, \MyModel.$parent, \MyModel.$field2)
139+ /// .from(MyModel.self)
140+ /// .all(decodingColumns: \MyModel.$id, \MyModel$field1, \MyModel.$parent, \MyModel.$field2)
141+ ///
142+ /// // type(of: tuples) == Array<(Int, String, ParentModel.IDValue, SomeEnum)>.self
143+ /// ```
144+ public func all< S: Schema , each P : QueryAddressableProperty > (
145+ decodingColumnsOf: S . Type , _ keypaths: repeat KeyPath < S , each P >
146+ ) async throws -> [ ( repeat ( each P ) . QueryablePropertyType. Value) ] {
147+ try await self . all ( ) . map {
148+ try $0. decode ( columnsOf: S . self, repeat each keypaths)
149+ }
150+ }
79151}
80152#endif
0 commit comments