|
| 1 | +/// The type-erased form of ``Property`` (see below). ``AnyProperty`` is used to |
| 2 | +/// access a model's set of Fluent properties in a fully generic fashion (with a |
| 3 | +/// little help from runtime reflection). It is generally not meaningful to conform |
| 4 | +/// to this protocol without also at least conforming to ``Property``. |
1 | 5 | public protocol AnyProperty: AnyObject {
|
2 | 6 | static var anyValueType: Any.Type { get }
|
3 | 7 | var anyValue: Any? { get }
|
4 | 8 | }
|
5 | 9 |
|
| 10 | +/// A property wrapper type conforms to this protocol to participate in Fluent's |
| 11 | +/// system for interfacing between the various properties of a model and the |
| 12 | +/// representations of those properties in a database. All properties whose |
| 13 | +/// wrappers conform to this protocol appear in Fluent's list of the data items |
| 14 | +/// which exist on a given model - whether those items contain actual data, |
| 15 | +/// such as a property representing a field in a database table, or are means to |
| 16 | +/// access other data, such a list of associated models on the far side of a |
| 17 | +/// many-to-many relation. |
6 | 18 | public protocol Property: AnyProperty {
|
7 | 19 | associatedtype Model: Fields
|
8 | 20 | associatedtype Value: Codable
|
9 | 21 | var value: Value? { get set }
|
10 | 22 | }
|
11 | 23 |
|
| 24 | +/// ``AnyProperty``'s requirements are implemented in terms of ``Property``'s |
| 25 | +/// requirements - they're the same requirements; ``Property`` is just more |
| 26 | +/// specific about the types. |
12 | 27 | extension AnyProperty where Self: Property {
|
| 28 | + /// The type-erased value of a property is the property's value. |
13 | 29 | public var anyValue: Any? {
|
14 | 30 | self.value
|
15 | 31 | }
|
16 |
| - |
| 32 | + |
| 33 | + /// The type-erased type of a property's value is the type of the property's value. |
17 | 34 | public static var anyValueType: Any.Type {
|
18 | 35 | Value.self
|
19 | 36 | }
|
20 | 37 | }
|
21 | 38 |
|
| 39 | +/// Marks a property as having "database" capability - in other words, the property |
| 40 | +/// receives output from the results of read queries, provides input to write queries, |
| 41 | +/// and/or represents one or more model fields. |
| 42 | +/// |
| 43 | +/// - Note: Most "database" properties participate in all three aspects (is/has fields, |
| 44 | +/// provides input, receives output), but certain properties only participate in |
| 45 | +/// receiving output (most notably the non-parent relation property types). Those |
| 46 | +/// properties only behave in this manner because the ability to look up the needed |
| 47 | +/// information on demand was not available in Swift until after the implementation was |
| 48 | +/// effectively complete. They should not be considered actual "database" properties. |
22 | 49 | public protocol AnyDatabaseProperty: AnyProperty {
|
23 | 50 | var keys: [FieldKey] { get }
|
24 | 51 | func input(to input: DatabaseInput)
|
25 | 52 | func output(from output: DatabaseOutput) throws
|
26 | 53 | }
|
27 | 54 |
|
| 55 | +/// Marks a property as participating in the ``Fields`` protocol's (defaulted) |
| 56 | +/// implementation of ``Decodable`` and ``Encodable``. This allows the property |
| 57 | +/// to encode and decode to and from representations other than storage in a |
| 58 | +/// database, and to act as a container if it contains any additional properties |
| 59 | +/// which also wish to participate. Just about every property type is codable. |
28 | 60 | public protocol AnyCodableProperty: AnyProperty {
|
29 | 61 | func encode(to encoder: Encoder) throws
|
30 | 62 | func decode(from decoder: Decoder) throws
|
31 | 63 | }
|
32 | 64 |
|
| 65 | +/// The type-erased form of ``QueryableProperty`` (see below). ``AnyQueryableProperty`` |
| 66 | +/// is used most often as a type-generic check for whether or not a given property |
| 67 | +/// represents an actual database field. |
33 | 68 | public protocol AnyQueryableProperty: AnyProperty {
|
| 69 | + /// Provides the database field's "path" - a nonempty list of field keys whose last |
| 70 | + /// item provides the name the field has in the database (which need not be the same |
| 71 | + /// as the name the corresponding model property has in Swift). A path containing |
| 72 | + /// more than one key theoretically describes a nested structure within the database, |
| 73 | + /// such as a field containing a complex JSON document, but at present this is not |
| 74 | + /// fully implemented by Fluent, making a multi-key path as invalid as an empty one. |
34 | 75 | var path: [FieldKey] { get }
|
| 76 | + |
| 77 | + /// If the property's current value has been set, return a description of the |
| 78 | + /// appropriate method for encoding that value into a database query. See |
| 79 | + /// ``DatabaseQuery/Value`` for more details. If the value is not set, the |
| 80 | + /// property must choose whether to request the `NULL` encoding or to return |
| 81 | + /// no value at all (whether or not this results in an error is highly context- |
| 82 | + /// dependent). |
35 | 83 | func queryableValue() -> DatabaseQuery.Value?
|
36 | 84 | }
|
37 | 85 |
|
| 86 | +/// Marks a property as being "queryable", meaning that it represents exactly one |
| 87 | +/// "real" database field (i.e. the database table will contain a "physical" field |
| 88 | +/// corresponding to the property, and it will be the only field that does so). |
38 | 89 | public protocol QueryableProperty: AnyQueryableProperty, Property {
|
| 90 | + /// Requests a description of the appropriate method of encoding a value of the |
| 91 | + /// property's wrapped type into a database query. In essence, this is the static |
| 92 | + /// version of ``AnyQueryableProperty/queryableValue()-3uzih``, except that this |
| 93 | + /// version will always have an input and thus can not return `nil`. |
| 94 | + /// |
| 95 | + /// - Warning: The existence of this method implies that any two identically-typed |
| 96 | + /// instances of a property _must_ encode their values into queries in exactly |
| 97 | + /// the same fashion, and Fluent does have code paths which proceed on that |
| 98 | + /// assumption. For example, this requirement is the primary reason that a |
| 99 | + /// ``TimestampProperty``'s format is represented as a generic type parameter |
| 100 | + /// rather than being provided to an initializer. |
39 | 101 | static func queryValue(_ value: Value) -> DatabaseQuery.Value
|
40 | 102 | }
|
41 | 103 |
|
42 | 104 | extension AnyQueryableProperty where Self: QueryableProperty {
|
| 105 | + /// By default, ``QueryableProperty``s uses ``QueryableProperty/queryValue(_:)-5df0n`` |
| 106 | + /// to provide its ``queryableValue()-4tkjo``. While it is not strictly required that |
| 107 | + /// this be the case, providing an alternative implementation risks violating the |
| 108 | + /// "identical encoding for identical property types" rule (see |
| 109 | + /// ``QueryableProperty/queryValue(_:)-5df0n``). |
43 | 110 | public func queryableValue() -> DatabaseQuery.Value? {
|
44 | 111 | return self.value.map { Self.queryValue($0) }
|
45 | 112 | }
|
46 | 113 | }
|
47 | 114 |
|
48 | 115 | extension QueryableProperty {
|
| 116 | + /// Since ``Property/Value`` conforms to ``Swift/Codable``, the default encoding for |
| 117 | + /// any ``QueryableProperty``'s value is as a query placeholder and associated parameter |
| 118 | + /// binding (bindings are sent to a database driver encoded via ``Swift/Encodable``). |
| 119 | + /// See ``DatabaseQuery/Value`` for more details on possible alternative encodings. |
49 | 120 | public static func queryValue(_ value: Value) -> DatabaseQuery.Value {
|
50 | 121 | .bind(value)
|
51 | 122 | }
|
52 | 123 | }
|
53 | 124 |
|
54 |
| -/// A property which can be addressed as a single value by a query, even if an indirection through |
55 |
| -/// a more convenient representation must be made to do so. |
56 |
| -/// |
57 |
| -/// This protocol bridges the gap between `AnyQueryableProperty` - which describes a property whose |
58 |
| -/// singular `Value` directly relates to the value stored in the database for that property - and |
59 |
| -/// the concrete relations `Parent` and `OptionalParent`, for which the notions of equality and |
60 |
| -/// identity are not interchangeable. Via `AnyQueryAddressableProperty`, both of these categories |
61 |
| -/// may be handled dynamically, rather than special-casing on the behaviors of the relations. |
62 |
| -/// |
63 |
| -/// In other words, to be "queryable" a property must be equatable, but to be "query-addressable", |
64 |
| -/// it need only be identifiable. Any queryable property is automatically query-addressable, but |
65 |
| -/// the reverse is not necessarily true. |
| 125 | +/// The type-erased form of ``QueryAddressableProperty`` (see below). Both protocols serve to |
| 126 | +/// bridge the gap between `AnyQueryableProperty` - which describes a property whose singular |
| 127 | +/// `Value` directly corresponds to the value stored in the database for that property - and |
| 128 | +/// property types whose `Value` is a derivative of or expansion upon an underlying queryable |
| 129 | +/// property. See the discussion of ``QueryAddressableProperty`` itself for additional details. |
66 | 130 | public protocol AnyQueryAddressableProperty: AnyProperty {
|
67 | 131 | var anyQueryableProperty: AnyQueryableProperty { get }
|
68 | 132 | var queryablePath: [FieldKey] { get }
|
69 | 133 | }
|
70 | 134 |
|
71 |
| -/// The type-bound version of `AnyQueryAddressableProperty`. |
| 135 | +/// Marks a property as being "query addressable", meaning that it is either itself queryable |
| 136 | +/// (``QueryableProperty`` implies ``QueryAddressableProperty``), or it represents some other |
| 137 | +/// single property that _is_ queryable. This allows properties whose purpose is to wrap or |
| 138 | +/// otherwise stand in for other properties to be handled generically without the need to |
| 139 | +/// add special case exceptions for those property types. |
| 140 | +/// |
| 141 | +/// `@Parent` is the canonical example of an addressable, non-queryable property. It provides |
| 142 | +/// the related model as its value, and contains a `@Field` property holding that model's ID. |
| 143 | +/// That underlying property means the relation can be "addressed" by a query, but the value |
| 144 | +/// type is wrong for it to be directly queryable. Providing the underlying field when the |
| 145 | +/// relation is "addressed" allows handling a model's property list (or, say, the property |
| 146 | +/// list of a ``Fields`` type being used as a composite ID value) fully generically and without |
| 147 | +/// special-casing or having to revisit the logic if additional property types come along. |
72 | 148 | public protocol QueryAddressableProperty: AnyQueryAddressableProperty, Property {
|
73 | 149 | associatedtype QueryablePropertyType: QueryableProperty where QueryablePropertyType.Model == Self.Model
|
74 | 150 | var queryableProperty: QueryablePropertyType { get }
|
|
0 commit comments