Skip to content

Class

CompositeOptionalParentProperty

Declares an optional one-to-many relation between the referenced (“parent”) model and the referencing (“child”) model, where the parent model specifies its ID with CompositeIDProperty.
@propertyWrapper @dynamicMemberLookup final class CompositeOptionalParentProperty<From, To> where From : Model, To : Model, To.IDValue : Fields

Overview

CompositeOptionalParentProperty serves the same purpose for parent models which use @CompositeID that OptionalParentProperty serves for parent models which use @ID.

Unfortunately, while the type of ID used by the child model makes no difference, limitations of Swift’s generics syntax make it impractical to support both @ID-using and @CompositeID-using models as the parent model with a single property type. A similar limitation applies in the opposite direction for ChildrenProperty and OptionalChildProperty.

Example:

Note

This example is somewhat contrived; in reality, this kind of metadata would have much more complex relationships.

final class TableMetadata: Model {
    static let schema = "table_metadata"

    final class IDValue: Fields, Hashable {
        @Field(key: "table_schema") var schema: String
        @Field(key: "table_name")   var name: String
        init() {}
        static func ==(lhs: IDValue, rhs: IDValue) -> Bool { lhs.schema == rhs.schema && lhs.name == rhs.name }
        func hash(into hasher: inout Hasher) { hasher.combine(self.schema); hasher.combine(self.name) }
    }

    @CompositeID var id: IDValue?
    // ...
}

final class ForeignKeyMetadata: Model {
    static let schema = "foreign_key_metadata"

    @ID(custom: "constraint_name") var id: String?
    @CompositeParent(prefix: "referenced") var referencedTable: TableMetadata
    @CompositeOptionalParent(prefix: "next_xref") var nextCrossReferencedTable: TableMetadata?
    // ...

    struct CreateTableMigration: AsyncMigration {
        func prepare(on database: Database) async throws {
            try await database.schema(ForeignKeyMetadata.schema)
                .field("constraint_name", .string, .required, .identifier(auto: false))
                .field("referenced_table_schema", .string, .required)
                .field("referenced_table_name", .string, .required)
                .foreignKey(["referenced_table_schema", "referenced_table_name"], references: TableMetadata.schema, ["table_schema", "table_name"])
                .field("next_xref_table_schema", .string)
                .field("next_xref_table_name", .string)
                .foreignKey(["next_xref_table_schema", "next_xref_table_name"], references: TableMetadata.schema, ["table_schema", "table_name"])
                .constraint(.sql(.check(SQLBinaryExpression( // adds a check constraint to ensure that neither field is ever NULL when the other isn't
                    left: SQLBinaryExpression(left: SQLIdentifier("next_xref_table_schema"), .is, right: SQLLiteral.null),
                    .equal,
                    right: SQLBinaryExpression(left: SQLIdentifier("next_xref_table_name"), .is, right: SQLLiteral.null)
                ))))
                // ...
                .create()
        }
    }
}

Topics

Initializers

Instance Properties

Instance Methods

Subscripts

Default Implementations

Relationships

Conforms To