# Modele

Modele reprezentują dane przechowywane w tabelach lub kolekcjach w twojej bazie danych. Modele mają jedno lub więcej pól, które przechowują wartości zgodne z `Codable`. Wszystkie modele mają unikalny identyfikator. Opakowania właściwości (property wrapper) są używane do oznaczania identyfikatorów, pól i relacji.

Poniżej znajduje się przykład prostego modelu z jednym polem. Zwróć uwagę, że modele nie opisują całego schematu bazy danych, takiego jak ograniczenia, indeksy i klucze obce. Schematy są definiowane w [migracjach](migration.md). Modele skupiają się na reprezentowaniu danych przechowywanych w schematach twojej bazy danych.

```swift
final class Planet: Model {
    // Name of the table or collection.
    static let schema = "planets"

    // Unique identifier for this Planet.
    @ID(key: .id)
    var id: UUID?

    // The Planet's name.
    @Field(key: "name")
    var name: String

    // Creates a new, empty Planet.
    init() { }

    // Creates a new Planet with all properties set.
    init(id: UUID? = nil, name: String) {
        self.id = id
        self.name = name
    }
}
```

## Schema

Wszystkie modele wymagają statycznej właściwości `schema`, tylko do odczytu. Ten ciąg znaków odwołuje się do nazwy tabeli lub kolekcji, którą reprezentuje ten model.

```swift
final class Planet: Model {
    // Name of the table or collection.
    static let schema = "planets"
}
```

Podczas wykonywania zapytań do tego modelu, dane będą pobierane i zapisywane w schemacie o nazwie `"planets"`.

!!! tip
    Nazwa schematu to zazwyczaj nazwa klasy w liczbie mnogiej i zapisana małymi literami.

## Identyfikator

Wszystkie modele muszą mieć właściwość `id` zdefiniowaną za pomocą opakowania właściwości `@ID`. To pole jednoznacznie identyfikuje instancje twojego modelu.

```swift
final class Planet: Model {
    // Unique identifier for this Planet.
    @ID(key: .id)
    var id: UUID?
}
```

Domyślnie właściwość `@ID` powinna używać specjalnego klucza `.id`, który przekłada się na odpowiedni klucz dla danego sterownika bazy danych. Dla SQL jest to `"id"`, a dla NoSQL `"_id"`.

`@ID` powinno być również typu `UUID`. Jest to obecnie jedyny typ wartości identyfikatora obsługiwany przez wszystkie sterowniki bazy danych. Fluent automatycznie wygeneruje nowe identyfikatory UUID podczas tworzenia modeli.

`@ID` ma opcjonalną wartość, ponieważ niezapisane modele mogą jeszcze nie mieć identyfikatora. Aby pobrać identyfikator lub rzucić błąd, użyj `requireID`.

```swift
let id = try planet.requireID()
```

### Exists

`@ID` posiada właściwość `exists`, która reprezentuje, czy model istnieje w bazie danych, czy nie. Podczas inicjalizacji modelu wartość ta wynosi `false`. Po zapisaniu modelu lub po pobraniu modelu z bazy danych wartość ta wynosi `true`. Ta właściwość jest mutowalna.

```swift
if planet.$id.exists {
    // This model exists in database.
}
```

### Niestandardowy identyfikator

Fluent wspiera niestandardowe klucze i typy identyfikatorów za pomocą przeciążenia `@ID(custom:)`.

```swift
final class Planet: Model {
    // Unique identifier for this Planet.
    @ID(custom: "foo")
    var id: Int?
}
```

Powyższy przykład używa `@ID` z niestandardowym kluczem `"foo"` i typem identyfikatora `Int`. Jest to kompatybilne z bazami danych SQL wykorzystującymi automatycznie zwiększane klucze główne, ale nie jest kompatybilne z NoSQL.

Niestandardowe `@ID` pozwalają użytkownikowi określić, w jaki sposób identyfikator powinien być generowany, za pomocą parametru `generatedBy`.

```swift
@ID(custom: "foo", generatedBy: .user)
```

Parametr `generatedBy` wspiera następujące przypadki:

|Generated By|Description|
|-|-|
|`.user`|`@ID` property is expected to be set before saving a new model.|
|`.random`|`@ID` value type must conform to `RandomGeneratable`.|
|`.database`|Database is expected to generate a value upon save.|

Jeśli parametr `generatedBy` zostanie pominięty, Fluent spróbuje wywnioskować odpowiedni przypadek na podstawie typu wartości `@ID`. Na przykład `Int` domyślnie użyje generacji `.database`, chyba że zostanie określone inaczej.

## Inicjalizator

Modele muszą mieć pustą metodę inicjalizującą.

```swift
final class Planet: Model {
    // Creates a new, empty Planet.
    init() { }
}
```

Fluent wymaga tej metody wewnętrznie do inicjalizowania modeli zwracanych przez zapytania. Jest ona również używana do refleksji.

Możesz chcieć dodać do swojego modelu wygodny inicjalizator, który przyjmuje wszystkie właściwości.

```swift
final class Planet: Model {
    // Creates a new Planet with all properties set.
    init(id: UUID? = nil, name: String) {
        self.id = id
        self.name = name
    }
}
```

Używanie wygodnych inicjalizatorów ułatwia dodawanie nowych właściwości do modelu w przyszłości.

## Field

Modele mogą mieć zero lub więcej właściwości `@Field` do przechowywania danych.

```swift
final class Planet: Model {
    // The Planet's name.
    @Field(key: "name")
    var name: String
}
```

Pola wymagają jawnego zdefiniowania klucza bazy danych. Nie musi on być taki sam jak nazwa właściwości.

!!! tip
    Fluent zaleca używanie `snake_case` dla kluczy bazy danych i `camelCase` dla nazw właściwości.

Wartości pól mogą być dowolnym typem zgodnym z `Codable`. Przechowywanie zagnieżdżonych struktur i tablic w `@Field` jest wspierane, ale operacje filtrowania są ograniczone. Zobacz [`@Group`](#group) jako alternatywę.

Dla pól zawierających wartość opcjonalną użyj `@OptionalField`.

```swift
@OptionalField(key: "tag")
var tag: String?
```

!!! warning
    Nieopcjonalne pole, które posiada obserwator właściwości `willSet` odwołujący się do jego bieżącej wartości lub obserwator `didSet` odwołujący się do jego `oldValue`, spowoduje błąd krytyczny (fatal error).

## Relacje

Modele mogą mieć zero lub więcej właściwości relacji odwołujących się do innych modeli, takich jak `@Parent`, `@Children` i `@Siblings`. Dowiedz się więcej o relacjach w sekcji [relacje](relations.md).

## Timestamp

`@Timestamp` to specjalny rodzaj `@Field`, który przechowuje `Foundation.Date`. Znaczniki czasu są ustawiane automatycznie przez Fluent zgodnie z wybranym wyzwalaczem (trigger).

```swift
final class Planet: Model {
    // When this Planet was created.
    @Timestamp(key: "created_at", on: .create)
    var createdAt: Date?

    // When this Planet was last updated.
    @Timestamp(key: "updated_at", on: .update)
    var updatedAt: Date?
}
```

`@Timestamp` wspiera następujące wyzwalacze.

|Trigger|Description|
|-|-|
|`.create`|Set when a new model instance is saved to the database.|
|`.update`|Set when an existing model instance is saved to the database.|
|`.delete`|Set when a model is deleted from the database. See [soft delete](#miękkie-usuwanie-soft-delete).|

Wartość daty `@Timestamp` jest opcjonalna i powinna być ustawiona na `nil` podczas inicjalizacji nowego modelu.

### Format znacznika czasu

Domyślnie `@Timestamp` użyje wydajnego kodowania `datetime` opartego na twoim sterowniku bazy danych. Możesz dostosować sposób przechowywania znacznika czasu w bazie danych za pomocą parametru `format`.

```swift
// Stores an ISO 8601 formatted timestamp representing
// when this model was last updated.
@Timestamp(key: "updated_at", on: .update, format: .iso8601)
var updatedAt: Date?
```

Zwróć uwagę, że powiązana migracja dla tego przykładu `.iso8601` wymagałaby przechowywania w formacie `.string`.

```swift
.field("updated_at", .string)
```

Poniżej wymieniono dostępne formaty znaczników czasu.

|Format|Description|Type|
|-|-|-|
|`.default`|Uses efficient `datetime` encoding for specific database.|Date|
|`.iso8601`|[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) string. Supports `withMilliseconds` parameter.|String|
|`.unix`|Seconds since Unix epoch including fraction.|Double|

Możesz uzyskać bezpośredni dostęp do surowej wartości znacznika czasu za pomocą właściwości `timestamp`.

```swift
// Manually set the timestamp value on this ISO 8601
// formatted @Timestamp.
model.$updatedAt.timestamp = "2020-06-03T16:20:14+00:00"
```

### Miękkie usuwanie (Soft Delete)

Dodanie do modelu `@Timestamp` używającego wyzwalacza `.delete` włączy miękkie usuwanie (soft-deletion).

```swift
final class Planet: Model {
    // When this Planet was deleted.
    @Timestamp(key: "deleted_at", on: .delete)
    var deletedAt: Date?
}
```

Modele usunięte w sposób miękki nadal istnieją w bazie danych po usunięciu, ale nie będą zwracane w zapytaniach.

!!! tip
    Możesz ręcznie ustawić znacznik czasu usunięcia na datę w przyszłości. Może to być używane jako data wygaśnięcia.

Aby wymusić usunięcie modelu obsługującego miękkie usuwanie z bazy danych, użyj parametru `force` w `delete`.

```swift
// Deletes from the database even if the model 
// is soft deletable. 
model.delete(force: true, on: database)
```

Aby przywrócić model usunięty w sposób miękki, użyj metody `restore`.

```swift
// Clears the on delete timestamp allowing this 
// model to be returned in queries. 
model.restore(on: database)
```

Aby uwzględnić modele usunięte w sposób miękki w zapytaniu, użyj `withDeleted`.

```swift
// Fetches all planets including soft deleted.
Planet.query(on: database).withDeleted().all()
```

## Enum

`@Enum` to specjalny rodzaj `@Field` służący do przechowywania typów reprezentowalnych jako ciąg znaków jako natywnych enumeracji bazy danych. Natywne enumeracje bazy danych zapewniają dodatkową warstwę bezpieczeństwa typów w twojej bazie danych i mogą być bardziej wydajne niż surowe enumeracje.

```swift
// String representable, Codable enum for animal types.
enum Animal: String, Codable {
    case dog, cat
}

final class Pet: Model {
    // Stores type of animal as a native database enum.
    @Enum(key: "type")
    var type: Animal
}
```

Tylko typy zgodne z `RawRepresentable`, gdzie `RawValue` jest `String`, są kompatybilne z `@Enum`. Enumeracje oparte na `String` domyślnie spełniają ten wymóg.

Aby przechować opcjonalną enumerację, użyj `@OptionalEnum`.

Baza danych musi być przygotowana do obsługi enumeracji za pomocą migracji. Zobacz [enum](schema.md#enum), aby uzyskać więcej informacji.

### Surowe enumeracje

Każda enumeracja oparta na typie zgodnym z `Codable`, takim jak `String` lub `Int`, może być przechowywana w `@Field`. Będzie ona przechowywana w bazie danych jako surowa wartość.

## Group

`@Group` pozwala przechować zagnieżdżoną grupę pól jako pojedynczą właściwość w twoim modelu. W przeciwieństwie do struktur zgodnych z `Codable` przechowywanych w `@Field`, pola w `@Group` można przeszukiwać (query). Fluent osiąga to, przechowując `@Group` jako płaską strukturę w bazie danych.

Aby użyć `@Group`, najpierw zdefiniuj zagnieżdżoną strukturę, którą chcesz przechować, korzystając z protokołu `Fields`. Jest to bardzo podobne do `Model`, z tym że nie jest wymagany żaden identyfikator ani nazwa schematu. Możesz przechowywać tutaj wiele właściwości, które obsługuje `Model`, takich jak `@Field`, `@Enum`, a nawet inny `@Group`.

```swift
// A pet with name and animal type.
final class Pet: Fields {
    // The pet's name.
    @Field(key: "name")
    var name: String

    // The type of pet. 
    @Field(key: "type")
    var type: String

    // Creates a new, empty Pet.
    init() { }
}
```

Po utworzeniu definicji pól, możesz użyć jej jako wartości właściwości `@Group`.

```swift
final class User: Model {
    // The user's nested pet.
    @Group(key: "pet")
    var pet: Pet
}
```

Pola `@Group` są dostępne za pomocą składni kropkowej.

```swift
let user: User = ...
print(user.pet.name) // String
```

Możesz wykonywać zapytania na zagnieżdżonych polach tak jak zwykle, używając składni kropkowej na opakowaniach właściwości.

```swift
User.query(on: database).filter(\.$pet.$name == "Zizek").all()
```

W bazie danych `@Group` jest przechowywane jako płaska struktura z kluczami połączonymi znakiem `_`. Poniżej znajduje się przykład, jak `User` wyglądałby w bazie danych.

|id|name|pet_name|pet_type|
|-|-|-|-|
|1|Tanner|Zizek|Cat|
|2|Logan|Runa|Dog|

## Codable

Modele domyślnie są zgodne z `Codable`. Oznacza to, że możesz używać swoich modeli z [API Content](../basics/content.md) Vapora, dodając zgodność z protokołem `Content`.

```swift
extension Planet: Content { }

app.get("planets") { req async throws in 
    // Return an array of all planets.
    try await Planet.query(on: req.db).all()
}
```

Podczas serializacji do / z `Codable`, właściwości modelu będą używać swoich nazw zmiennych zamiast kluczy. Relacje będą serializowane jako zagnieżdżone struktury, a wszelkie chętnie (eager) załadowane dane zostaną uwzględnione.

!!! info
    Zalecamy, aby w niemal wszystkich przypadkach używać DTO zamiast modelu dla odpowiedzi API i ciał żądań. Zobacz [Data Transfer Object](#data-transfer-object), aby uzyskać więcej informacji.

### Data Transfer Object

Domyślna zgodność modelu z `Codable` może ułatwić proste zastosowania i prototypowanie. Jednakże, naraża ona informacje z bazy danych na dostęp przez API. Jest to zazwyczaj niepożądane zarówno z punktu widzenia bezpieczeństwa - zwracanie wrażliwych pól, takich jak hash hasła użytkownika, to zły pomysł - jak i z punktu widzenia użyteczności. Utrudnia to zmianę schematu bazy danych bez naruszania API, przyjmowanie lub zwracanie danych w innym formacie, czy dodawanie lub usuwanie pól z API.

W większości przypadków powinieneś używać DTO, czyli obiektu transferu danych (data transfer object), zamiast modelu (znanego również jako domain transfer object). DTO to osobny typ zgodny z `Codable`, reprezentujący strukturę danych, którą chcesz zakodować lub zdekodować. Oddzielają one twoje API od schematu bazy danych i pozwalają na wprowadzanie zmian w modelach bez naruszania publicznego API twojej aplikacji, posiadanie różnych wersji oraz sprawiają, że twoje API jest wygodniejsze w użyciu dla twoich klientów.

Załóżmy poniższy model `User` w kolejnych przykładach.

```swift
// Abridged user model for reference.
final class User: Model {
    @ID(key: .id)
    var id: UUID?

    @Field(key: "first_name")
    var firstName: String

    @Field(key: "last_name")
    var lastName: String
}
```

Jednym z częstych zastosowań DTO jest implementacja żądań `PATCH`. Te żądania zawierają wartości tylko dla pól, które powinny zostać zaktualizowane. Próba zdekodowania `Model` bezpośrednio z takiego żądania zakończyłaby się niepowodzeniem, gdyby brakowało któregoś z wymaganych pól. Poniższy przykład pokazuje użycie DTO do zdekodowania danych żądania i zaktualizowania modelu.

```swift
// Structure of PATCH /users/:id request.
struct PatchUser: Decodable {
    var firstName: String?
    var lastName: String?
}

app.patch("users", ":id") { req async throws -> User in 
    // Decode the request data.
    let patch = try req.content.decode(PatchUser.self)
    // Fetch the desired user from the database.
    guard let user = try await User.find(req.parameters.get("id"), on: req.db) else {
        throw Abort(.notFound)
    }
    // If first name was supplied, update it.
    if let firstName = patch.firstName {
        user.firstName = firstName
    }
    // If new last name was supplied, update it.
    if let lastName = patch.lastName {
        user.lastName = lastName
    }
    // Save the user and return it.
    try await user.save(on: req.db)
    return user
}
```

Innym częstym zastosowaniem DTO jest dostosowywanie formatu odpowiedzi twojego API. Poniższy przykład pokazuje, jak DTO może zostać użyte do dodania obliczanego pola do odpowiedzi.

```swift
// Structure of GET /users response.
struct GetUser: Content {
    var id: UUID
    var name: String
}

app.get("users") { req async throws -> [GetUser] in 
    // Fetch all users from the database.
    let users = try await User.query(on: req.db).all()
    return try users.map { user in
        // Convert each user to GET return type.
        try GetUser(
            id: user.requireID(),
            name: "\(user.firstName) \(user.lastName)"
        )
    }
}
```

Innym częstym zastosowaniem jest obsługa relacji, takich jak relacje typu parent lub children. Zobacz [dokumentację Parent](relations.md#kodowanie-i-dekodowanie-rodziców), aby zobaczyć przykład użycia DTO w celu ułatwienia dekodowania modelu z relacją `@Parent`.

Nawet jeśli struktura DTO jest identyczna ze zgodnością modelu z `Codable`, posiadanie jej jako osobnego typu może pomóc utrzymać porządek w dużych projektach. Jeśli kiedykolwiek będziesz musiał wprowadzić zmianę we właściwościach swoich modeli, nie musisz martwić się o naruszenie publicznego API twojej aplikacji. Możesz również rozważyć umieszczenie swoich DTO w osobnym pakiecie, który może być współdzielony z konsumentami twojego API, dodając zgodność z `Content` w twojej aplikacji Vapor.

## Alias

Protokół `ModelAlias` pozwala na jednoznaczną identyfikację modelu, który jest łączony (joined) wielokrotnie w zapytaniu. Aby uzyskać więcej informacji, zobacz [joins](query.md#złączenie-join).

## Save

Aby zapisać model do bazy danych, użyj metody `save(on:)`.

```swift
planet.save(on: database)
```

Ta metoda wewnętrznie wywoła `create` lub `update`, w zależności od tego, czy model już istnieje w bazie danych.

### Create

Możesz wywołać metodę `create`, aby zapisać nowy model do bazy danych.

```swift
let planet = Planet(name: "Earth")
planet.create(on: database)
```

`create` jest również dostępne dla tablicy modeli. Zapisuje to wszystkie modele do bazy danych w pojedynczej partii / zapytaniu (batch / query).

```swift
// Example of batch create.
[earth, mars].create(on: database)
```

!!! warning
    Modele używające [`@ID(custom:)`](#niestandardowy-identyfikator) z generatorem `.database` (zazwyczaj automatycznie zwiększane `Int`) nie będą miały dostępu do swoich nowo utworzonych identyfikatorów po zbiorczym utworzeniu (batch create). W sytuacjach, gdy potrzebujesz uzyskać dostęp do identyfikatorów, wywołaj `create` na każdym modelu osobno.

Aby utworzyć tablicę modeli osobno, użyj `map` + `flatten`.

```swift
[earth, mars].map { $0.create(on: database) }
    .flatten(on: database.eventLoop)
```

Jeśli używasz `async`/`await`, możesz użyć:

```swift
await withThrowingTaskGroup(of: Void.self) { taskGroup in
    [earth, mars].forEach { model in
        taskGroup.addTask { try await model.create(on: database) }
    }
}
```

### Update

Możesz wywołać metodę `update`, aby zapisać model, który został pobrany z bazy danych.

```swift
guard let planet = try await Planet.find(..., on: database) else {
    throw Abort(.notFound)
}
planet.name = "Earth"
try await planet.update(on: database)
```

Aby zaktualizować tablicę modeli, użyj `map` + `flatten`.

```swift
[earth, mars].map { $0.update(on: database) }
    .flatten(on: database.eventLoop)

// TOOD
```

## Query

Modele udostępniają statyczną metodę `query(on:)`, która zwraca query builder.

```swift
Planet.query(on: database).all()
```

Dowiedz się więcej o wykonywaniu zapytań w sekcji [query](query.md).

## Find

Modele mają statyczną metodę `find(_:on:)` do wyszukiwania instancji modelu na podstawie identyfikatora.

```swift
Planet.find(req.parameters.get("id"), on: database)
```

Ta metoda zwraca `nil`, jeśli nie znaleziono modelu z tym identyfikatorem.

## Lifecycle

Middleware modeli pozwala na podłączenie się do zdarzeń cyklu życia twojego modelu. Wspierane są następujące zdarzenia cyklu życia.

|Method|Description|
|-|-|
|`create`|Runs before a model is created.|
|`update`|Runs before a model is updated.|
|`delete(force:)`|Runs before a model is deleted.|
|`softDelete`|Runs before a model is soft deleted.|
|`restore`|Runs before a model is restored (opposite of soft delete).|

Middleware modeli są deklarowane za pomocą protokołu `ModelMiddleware` lub `AsyncModelMiddleware`. Wszystkie metody cyklu życia mają domyślną implementację, więc musisz zaimplementować tylko te metody, których potrzebujesz. Każda metoda przyjmuje dany model, referencję do bazy danych oraz następną akcję w łańcuchu. Middleware może zdecydować się na wcześniejszy zwrot, zwrócenie nieudanego future, lub wywołanie następnej akcji, aby kontynuować normalnie.

Za pomocą tych metod możesz wykonywać akcje zarówno przed, jak i po zakończeniu określonego zdarzenia. Wykonywanie akcji po zakończeniu zdarzenia można zrealizować, mapując future zwrócony przez następnego respondenta.

```swift
// Example middleware that capitalizes names.
struct PlanetMiddleware: ModelMiddleware {
    func create(model: Planet, on db: Database, next: AnyModelResponder) -> EventLoopFuture<Void> {
        // The model can be altered here before it is created.
        model.name = model.name.capitalized()
        return next.create(model, on: db).map {
            // Once the planet has been created, the code 
            // here will be executed.
            print ("Planet \(model.name) was created")
        }
    }
}
```

lub jeśli używasz `async`/`await`:

```swift
struct PlanetMiddleware: AsyncModelMiddleware {
    func create(model: Planet, on db: Database, next: AnyAsyncModelResponder) async throws {
        // The model can be altered here before it is created.
        model.name = model.name.capitalized()
        try await next.create(model, on: db)
        // Once the planet has been created, the code 
        // here will be executed.
        print ("Planet \(model.name) was created")
    }
}
```

Po utworzeniu middleware, możesz go włączyć za pomocą `app.databases.middleware`.

```swift
// Example of configuring model middleware.
app.databases.middleware.use(PlanetMiddleware(), on: .psql)
```

## Database Space

Fluent wspiera ustawianie przestrzeni (space) dla modelu, co pozwala na podzielenie poszczególnych modeli Fluent między schematami PostgreSQL, bazami danych MySQL oraz wieloma podłączonymi bazami danych SQLite. MongoDB nie wspiera przestrzeni w chwili pisania tego tekstu. Aby umieścić model w przestrzeni innej niż domyślna, dodaj do modelu nową właściwość statyczną:

```swift
public static let schema = "planets"
public static let space: String? = "mirror_universe"

// ...
```

Fluent użyje tego podczas budowania wszystkich zapytań do bazy danych.
