Ga naar inhoud

Schema

Fluent's schema API stelt u in staat om uw database schema programmatisch aan te maken en te updaten. Het wordt vaak gebruikt in combinatie met migraties om de database voor te bereiden voor gebruik met modellen.

// Een voorbeeld van Fluent's schema API
try await database.schema("planets")
    .id()
    .field("name", .string, .required)
    .field("star_id", .uuid, .required, .references("stars", "id"))
    .create()

Om een SchemaBuilder te maken, gebruik je de schema methode op database. Geef de naam op van de tabel of collectie die je wilt wijzigen. Als je het schema van een model wijzigt, zorg er dan voor dat deze naam overeenkomt met het [schema] van het model (model.md#schema).

Acties

De schema-API ondersteunt het maken, bijwerken en verwijderen van schema's. Elke actie ondersteunt een subset van de beschikbare methoden van de API.

Create

Het aanroepen van create() creëert een nieuwe tabel of collectie in de database. Alle methodes voor het definiëren van nieuwe velden en constraints worden ondersteund. Methodes voor updates of deletes worden genegeerd.

// Een voorbeeld van schema creatie.
try await database.schema("planets")
    .id()
    .field("name", .string, .required)
    .create()

Als een tabel of collectie met de gekozen naam al bestaat, zal er een foutmelding worden gegeven. Om dit te negeren, gebruik .ignoreExisting().

Update

Door update() aan te roepen wordt een bestaande tabel of collectie in de database bijgewerkt. Alle methodes voor het maken, updaten en verwijderen van velden en constraints worden ondersteund.

// Een voorbeeld schema update.
try await database.schema("planets")
    .unique(on: "name")
    .deleteField("star_id")
    .update()

Delete

Het oproepen van delete() verwijdert een bestaande tabel of collectie uit de database. Er worden geen extra methodes ondersteund.

// Een voorbeeld van schema verwijdering.
database.schema("planets").delete()

Field

Velden kunnen worden toegevoegd bij het maken of bijwerken van een schema.

// Voegt een nieuw veld toe
.field("name", .string, .required)

De eerste parameter is de naam van het veld. Deze moet overeenkomen met de sleutel die gebruikt wordt op de bijbehorende model-eigenschap. De tweede parameter is het data type van het veld. Tenslotte kunnen nul of meer constraints worden toegevoegd.

Data Type

Ondersteunde velddatatypes staan hieronder vermeld.

DataType Swift Type
.string String
.int{8,16,32,64} Int{8,16,32,64}
.uint{8,16,32,64} UInt{8,16,32,64}
.bool Bool
.datetime Date (aanbevolen)
.time Date (zonder dag, maand en jaar)
.date Date (het tijdstip weglaten)
.float Float
.double Double
.data Data
.uuid UUID
.dictionary Zie dictionary
.array Zie array
.enum Zie enum

Field Constraint

Ondersteunde veldbeperkingen staan hieronder vermeld.

FieldConstraint Beschrijving
.required Staat nil waarden niet toe.
.references Vereist dat de waarde van dit veld overeenkomt met een waarde in het schema waarnaar wordt verwezen. Zie foreign key
.identifier Duidt de primaire sleutel aan. Zie identifier

Identifier

Als je model een standaard @ID eigenschap gebruikt, kun je de id() helper gebruiken om het veld aan te maken. Dit gebruikt de speciale .id veld sleutel en UUID waarde type.

// Voegt veld toe voor standaard identifier.
.id()

Voor aangepaste identifier-types moet u het veld handmatig specificeren.

// Voegt veld toe voor aangepaste identifier.
.field("id", .int, .identifier(auto: true))

De identifier constraint mag gebruikt worden op een enkel veld en duidt de primaire sleutel aan. De auto vlag bepaalt of de database deze waarde automatisch moet genereren of niet.

Update Field

Je kunt het datatype van een veld bijwerken met updateField.

// Updates van het veld naar `double` datatype.
.updateField("age", .double)

Zie geavanceerd voor meer informatie over geavanceerde schema updates.

Delete Field

U kunt een veld uit een schema verwijderen met deleteField.

// Verwijdert het veld "leeftijd".
.deleteField("age")

Constraint

Constraints kunnen worden toegevoegd bij het maken of bijwerken van een schema. In tegenstelling tot field constraints, kunnen constraints op het hoogste niveau meerdere velden beïnvloeden.

Unique

Een unieke beperking vereist dat er geen duplicaten zijn in één of meer velden.

// Duplicaat e-mailadressen verbieden.
.unique(on: "email")

Indien meerdere velden worden beperkt, moet de specifieke combinatie van de waarde van elk veld uniek zijn.

// Verbied gebruikers met dezelfde volledige naam.
.unique(on: "first_name", "last_name")

Om een unieke beperking te verwijderen, gebruik deleteUnique.

// Verwijdert duplicaat e-mail beperking.
.deleteUnique(on: "email")

Constraint Name

Fluent genereert standaard unieke constraint namen. Het kan echter zijn dat je een aangepaste constraint naam wilt doorgeven. Je kunt dit doen met de name parameter.

// Duplicaat e-mailadressen verbieden.
.unique(on: "email", name: "no_duplicate_emails")

Om een benoemde constraint te verwijderen, moet je deleteConstraint(name:) gebruiken.

// Verwijdert duplicaat e-mail beperking.
.deleteConstraint(name: "no_duplicate_emails")

Foreign Key

Bij "Foreign key"-restricties moet de waarde van een veld overeenkomen met een van de waarden in het veld waarnaar wordt verwezen. Dit is nuttig om te voorkomen dat ongeldige gegevens worden opgeslagen. Foreign key constraints kunnen worden toegevoegd als veld of als top-level constraint.

Om een foreign key constraint aan een veld toe te voegen, gebruik .references.

// Voorbeeld van het toevoegen van een veld foreign key constraint.
.field("star_id", .uuid, .required, .references("stars", "id"))

De bovenstaande beperking vereist dat alle waarden in het veld "star_id" overeenkomen met een van de waarden in het veld "id" van Star.

Dezelfde beperking kan worden toegevoegd als een beperking van het hoogste niveau met foreignKey.

// Voorbeeld van het toevoegen van een top-level foreign key constraint.
.foreignKey("star_id", references: "stars", "id")

In tegenstelling tot veldrestricties, kunnen top-level constraints worden toegevoegd in een schema-update. Ze kunnen ook named zijn.

Foreign key constraints ondersteunen optionele onDelete en onUpdate acties.

ForeignKeyAction Beschrijving
.noAction Voorkomt vreemde sleutel schendingen (standaard).
.restrict Zelfde als .noAction.
.cascade Propagageert verwijderingen via vreemde sleutels.
.setNull Stelt het veld in op null als de referentie verbroken is.
.setDefault Stelt het veld in op standaard indien de referentie gebroken is.

Hieronder staat een voorbeeld waarbij foreign key acties worden gebruikt.

// Voorbeeld van het toevoegen van een top-level foreign key constraint.
.foreignKey("star_id", references: "stars", "id", onDelete: .cascade)

Waarschuwing

Vreemde sleutel acties gebeuren alleen in de database, buiten Fluent om. Dit betekent dat zaken als model middleware en soft-delete mogelijk niet correct werken.

Dictionary

Het dictionary datatype is in staat om geneste dictionary waarden op te slaan. Dit omvat structs die voldoen aan Codable en Swift dictionaries met een Codable waarde.

Opmerking

Fluent's SQL database drivers slaan geneste woordenboeken op in JSON kolommen.

Neem de volgende Codable struct.

struct Pet: Codable {
    var name: String
    var age: Int
}

Omdat deze Pet struct Codable is, kan het worden opgeslagen in een @Field.

@Field(key: "pet")
var pet: Pet

Dit veld kan worden opgeslagen met behulp van het datatype .dictionary(of:).

.field("pet", .dictionary, .required)

Omdat Codable types heterogene dictionaries zijn, specificeren we de of parameter niet.

Als de dictionary waarden homogeen waren, bijvoorbeeld [String: Int], dan zou de of parameter het waardetype specificeren.

.field("numbers", .dictionary(of: .int), .required)

Dictionary sleutels moeten altijd strings zijn.

Array

Het array data type is in staat om geneste arrays op te slaan. Dit omvat Swift arrays die Codable waarden bevatten en Codable types die een ongesleutelde container gebruiken.

Neem het volgende @Field dat een array van strings opslaat.

@Field(key: "tags")
var tags: [String]

Dit veld kan worden opgeslagen met het datatype .array(van:).

.field("tags", .array(of: .string), .required)

Omdat de array homogeen is, specificeren we de of parameter.

Codable Swift Arrays zullen altijd een homogene waarde type hebben. Aangepaste Codable types die heterogene waarden serialiseren naar ongesleutelde containers zijn de uitzondering en moeten het .array data type gebruiken.

Enum

Het enum datatype is in staat om string-gebaseerde Swift enums in de eigen taal op te slaan. Native database enums bieden een extra laag van type veiligheid aan je database en kunnen performanter zijn dan raw enums.

Om een native database enum te definiëren, gebruik de enum methode op Database. Gebruik case om elk geval van de enum te definiëren.

// An example of enum creation.
database.enum("planet_type")
    .case("smallRocky")
    .case("gasGiant")
    .case("dwarf")
    .create()

Als een enum is aangemaakt, kunt u de read() methode gebruiken om een datatype te genereren voor uw schema veld.

// Een voorbeeld van het lezen van een enum en het gebruiken ervan om een nieuw veld te definiëren.
database.enum("planet_type").read().flatMap { planetType in
    database.schema("planets")
        .field("type", planetType, .required)
        .update()
}

// Of

let planetType = try await database.enum("planet_type").read()
try await database.schema("planets")
    .field("type", planetType, .required)
    .update()

Om een enum te updaten, roep update() op. Uit bestaande enums kunnen zaken worden verwijderd.

// Een voorbeeld van een enum update.
database.enum("planet_type")
    .deleteCase("gasGiant")
    .update()

Om een enum te verwijderen, roep delete().

// Een voorbeeld van het wissen van een enum.
database.enum("planet_type").delete()

Model Coupling

Schema's bouwen is doelbewust losgekoppeld van modellen. In tegenstelling tot query-building, maakt schema-building geen gebruik van sleutelpaden en is volledig stringly typed. Dit is belangrijk omdat schemadefinities, vooral die welke voor migraties zijn geschreven, mogelijk naar modeleigenschappen moeten verwijzen die niet langer bestaan.

Kijk eens naar het volgende migratievoorbeeld om dit beter te begrijpen.

struct UserMigration: AsyncMigration {
    func prepare(on database: Database) async throws {
        try await database.schema("users")
            .field("id", .uuid, .identifier(auto: false))
            .field("name", .string, .required)
            .create()
    }

    func revert(on database: Database) async throws {
        try await database.schema("users").delete()
    }
}

Laten we aannemen dat deze migratie al is gepushed naar productie. Laten we nu eens aannemen dat we de volgende wijziging in het User model moeten aanbrengen.

- @Field(key: "name")
- var name: String
+ @Field(key: "first_name")
+ var firstName: String
+
+ @Field(key: "last_name")
+ var lastName: String

Wij kunnen de nodige aanpassingen in het databaseschema aanbrengen met de volgende migratie.

struct UserNameMigration: AsyncMigration {
    func prepare(on database: Database) async throws {
        try await database.schema("users")
            .deleteField("name")
            .field("first_name", .string)
            .field("last_name", .string)
            .update()
    }

    func revert(on database: Database) async throws {
        try await database.schema("users").delete()
    }
}

Merk op dat om deze migratie te laten werken, we tegelijkertijd moeten kunnen verwijzen naar zowel het verwijderde naam veld als de nieuwe voornaam en achternaam velden. Verder moet de originele UserMigration geldig blijven. Dit zou niet mogelijk zijn met sleutelpaden.

Instellen Model Space

Om de ruimte voor een model te definiëren, geef de ruimte door aan de schema(_:space:) bij het maken van de tabel. Bijv.

try await db.schema("planets", space: "mirror_universe")
    .id()
    // ...
    .create()