mirror of https://github.com/vapor/docs.git
relations docs
This commit is contained in:
parent
0cfa0a93f9
commit
bc0e27dbae
|
|
@ -3,12 +3,9 @@
|
|||
Fluent ([vapor/fluent](https://github.com/vapor/fluent)) is a type-safe, fast, and easy-to-use ORM framework built for Swift.
|
||||
It takes advantage of Swift's strong type system to provide an elegant foundation for building database integrations.
|
||||
|
||||
## Driver
|
||||
## Choosing a Driver
|
||||
|
||||
Fluent is a framework for building ORMs, not an ORM itself. To use Fluent, you will first need to choose a database driver to use.
|
||||
|
||||
!!! tip
|
||||
Fluent can support multiple databases and database drivers per application.
|
||||
Fluent is a framework for building ORMs, not an ORM itself. To use Fluent, you will first need to choose a database driver to use. Fluent can support multiple databases and database drivers per application.
|
||||
|
||||
Below is a list of officially supported database drivers for Fluent.
|
||||
|
||||
|
|
@ -19,12 +16,12 @@ Below is a list of officially supported database drivers for Fluent.
|
|||
|SQLite|[fluent-sqlite](https://github.com/vapor/fluent-sqlite)|3.0.0|`sqlite`|Open source, embedded SQL database. Its simplistic nature makes it a great candiate for prototyping and testing.|
|
||||
|MongoDB|fluent-mongo|n/a|`mongo`|Coming soon. Popular NoSQL database.|
|
||||
|
||||
Replace any Xcode placholders (`<#...#>`) in the code snippets below with the information from this table.
|
||||
!!! note
|
||||
Replace any Xcode placholders (`<#...#>`) in the code snippets below with information from the above table.
|
||||
|
||||
!!! seealso
|
||||
You can search GitHub for the tag [`fluent-database`](https://github.com/topics/fluent-database) for a full list of official and third-party Fluent database drivers.
|
||||
You can search GitHub for the tag [`fluent-database`](https://github.com/topics/fluent-database) for a full list of official and third-party Fluent database drivers.
|
||||
|
||||
### Package
|
||||
### Add Package Dependency
|
||||
|
||||
Once you have decided which driver you want, the next step is adding it as a dependency to your project in your SPM package manifest file.
|
||||
|
||||
|
|
@ -52,19 +49,13 @@ Don't forget to add the module as a dependency in the `targets` array. Once you
|
|||
vapor xcode
|
||||
```
|
||||
|
||||
## Model
|
||||
## Creating a Model
|
||||
|
||||
Now let's create your first model. Models represent tables in your database and they are the primary method of interacting with your data.
|
||||
|
||||
Each driver provides convenience model protocols that extend Fluent's base [`Model`](#fixme) protocol. These convenience types make declaring models more concise.
|
||||
Each driver provides convenience model protocols (`PostgreSQLModel`, `SQLiteModel`, etc) that extend Fluent's base [`Model`](#fixme) protocol. These convenience types make declaring models more concise by using standard values for ID key and type.
|
||||
|
||||
|protocol|note|
|
||||
|-|-|
|
||||
|`<#Database#>Model`|Uses standard ID key and type for database engine.|
|
||||
|`<#Database#>UUIDModel`|Uses `UUID` IDs with standard key for database engine.|
|
||||
|`<#Database#>StringModel`|Uses `String` IDs with standard key for database engine.|
|
||||
|
||||
For this example, we will use the standard convenience model protocol.
|
||||
Fill in the Xcode placeholders below with the name of your chosen database, i.e., `PostgreSQL`.
|
||||
|
||||
```swift
|
||||
import Fluent<#Database#>
|
||||
|
|
@ -94,11 +85,11 @@ The example above shows a simple model representing a user. You can make both st
|
|||
|
||||
Take a look at [Fluent → Model](models.md) for more information on creating models with custom ID types and keys.
|
||||
|
||||
## Configure
|
||||
## Configuring the Database
|
||||
|
||||
Now that you have a model, you can configure your database. This is done in your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
Now that you have a model, you can configure your database. This is done in [`configure.swift`](../getting-started/structure.md#configureswift).
|
||||
|
||||
### Provider
|
||||
### Register Provider
|
||||
|
||||
The first step is to register your database driver's provider.
|
||||
|
||||
|
|
@ -114,13 +105,16 @@ try services.register(Fluent<#Database#>Provider())
|
|||
|
||||
Registering the provider will add all of the services required for your Fluent database to work properly. It also includes a default database config struct that uses typical development environment credentials.
|
||||
|
||||
### Custom
|
||||
### Custom Credentials
|
||||
|
||||
If you are using default configuration for your database (such as default credentials or other config) then this may be the only setup you need to perform.
|
||||
|
||||
See the documentation for your specific database type for more information about custom configuration.
|
||||
|
||||
## Migration
|
||||
!!! danger
|
||||
fixme: Add links to config structs.
|
||||
|
||||
## Creating a Migration
|
||||
|
||||
If your database driver uses schemas (is a SQL database), you will need to create a [`Migration`](#fixme) for your new model. Migrations allow Fluent to create a table for your model in a reliable, testable way. You can later create additional migrations to update or delete the model's table or even manipulate data in the table.
|
||||
|
||||
|
|
@ -137,9 +131,11 @@ extension User: <#Database#>Migration { }
|
|||
|
||||
Take a look at [Fluent → Migration](../fluent/migrations.md) if you are interested in learning more about custom migrations.
|
||||
|
||||
### MigrationConfig
|
||||
### Configuring Migrations
|
||||
|
||||
Once you have created a migration, you must register it to Fluent using [`MigrationConfig`](#fixme). This is done in your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
Once you have created a migration, you must register it to Fluent using [`MigrationConfig`](#fixme). This is done in [`configure.swift`](../getting-started/structure.md#configureswift).
|
||||
|
||||
Fill in the database ID (`dbid`) from the table above, i.e., `psql`.
|
||||
|
||||
```swift
|
||||
import Fluent<#Database#>
|
||||
|
|
@ -164,7 +160,7 @@ Migrations complete
|
|||
Server starting on http://localhost:8080
|
||||
```
|
||||
|
||||
## Query
|
||||
## Performing a Query
|
||||
|
||||
Now that you have created a model and a corresponding schema in your database, let's make your first query.
|
||||
|
||||
|
|
@ -176,7 +172,7 @@ router.get("users") { req in
|
|||
|
||||
If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent model working.
|
||||
|
||||
## Connection
|
||||
## Raw Queries
|
||||
|
||||
With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a "raw query".
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ make normal queries to your database.
|
|||
|
||||
In this guide we will cover creating both types of migrations.
|
||||
|
||||
## Create / Delete Schema
|
||||
## Creating and Deleting Schemas
|
||||
|
||||
Let's take a look at how we can use migrations to prepare a schema supporting database to store a theoretical `Galaxy` model.
|
||||
|
||||
Fill in the Xcode placeholders below with your database's name from [Getting Started → Choosing a Driver](getting-started/#choosing-a-driver).
|
||||
|
||||
```swift
|
||||
import Fluent<#Database#>
|
||||
|
||||
|
|
@ -21,9 +23,9 @@ struct Galaxy: <#Database#>Model {
|
|||
}
|
||||
```
|
||||
|
||||
### Automatic
|
||||
### Automatic Model Migrations
|
||||
|
||||
Models provide a shortcut for declaring database migrations. If you conform a type that conforms to [`Model`](#fixme) to migration, Fluent can infer the model's properties and automatically implement the `prepare(...)` and `revert(...)` methods.
|
||||
Models provide a shortcut for declaring database migrations. If you conform a type that conforms to [`Model`](#fixme) to [`Migration`](#fixme), Fluent can infer the model's properties and automatically implement the `prepare(...)` and `revert(...)` methods.
|
||||
|
||||
```swift
|
||||
import Fluent<#Database#>
|
||||
|
|
@ -33,9 +35,7 @@ extension Galaxy: <#Database#>Migration { }
|
|||
|
||||
This method is especially useful for quick prototyping and simple setups. For most other situations you should consider creating a normal, custom migration.
|
||||
|
||||
#### Config
|
||||
|
||||
Add this automatic migration to your [`MigrationConfig`](#fixme) using the `add(model:database:)` method. This is done in your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
Add this automatic migration to your [`MigrationConfig`](#fixme) using the `add(model:database:)` method. This is done in [`configure.swift`](../getting-started/structure.md#configureswift).
|
||||
|
||||
```swift
|
||||
var migrations = MigrationConfig()
|
||||
|
|
@ -45,7 +45,7 @@ services.register(migrations)
|
|||
|
||||
The `add(model:database:)` method will automatically set the model's [`defaultDatabase`](#fixme) property.
|
||||
|
||||
### Custom
|
||||
### Custom Migrations
|
||||
|
||||
We can customize the table created for our model by creating a migration and using the static `create` and `delete` methods on [`Database`](#fixme).
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ struct CreateGalaxy: <#Database#>Migration {
|
|||
}
|
||||
```
|
||||
|
||||
#### Prepare
|
||||
#### Creating a Schema
|
||||
|
||||
The most important method in a migration is `prepare(...)`. This is responsible for effecting the migration's changes. For our `CreateGalaxy` migration, we will use our database's static `create` method to create a schema.
|
||||
|
||||
|
|
@ -88,16 +88,17 @@ try builder.field(for: \.name, type: <#DataType#>)
|
|||
|
||||
Each database has it's own unique data types, so refer to your database's documentation for more information.
|
||||
|
||||
Learn more about creating, updating, and deleting schemas in [Fluent → Schema Builder](../schema-builder).
|
||||
!!! danger
|
||||
fixme: links to db's data types
|
||||
|
||||
#### Revert
|
||||
#### Deleting a Schema
|
||||
|
||||
Each migration should also include a method for _reverting_ the changes it makes. It is used when you boot your
|
||||
app with the `--revert` option.
|
||||
|
||||
For a migration that creates a table in the database, the reversion is quite simple: delete the table.
|
||||
|
||||
To implement `revert` for our model, we can use our database's static `delete(...)` method to indicate that we would like to delete the schema a schema.
|
||||
To implement `revert` for our model, we can use our database's static `delete(...)` method to indicate that we would like to delete the schema.
|
||||
|
||||
```swift
|
||||
import Fluent<#Database#>
|
||||
|
|
@ -114,8 +115,6 @@ To delete a schema, you pass a model type and connection as the two required par
|
|||
|
||||
You can always choose to skip a reversion by simplying returning `conn.future(())`. But note that they are especially useful when testing and debugging your migrations.
|
||||
|
||||
#### Config
|
||||
|
||||
Add this custom migration to your [`MigrationConfig`](#fixme) using the `add(migration:database:)` method. This is done in your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
|
||||
```swift
|
||||
|
|
@ -129,7 +128,7 @@ Make sure to also set the `defaultDatabase` property on your model when using a
|
|||
Galaxy.defaultDatabase = .<#dbid#>
|
||||
```
|
||||
|
||||
## Update Schema
|
||||
## Updating a Schema
|
||||
|
||||
After you deploy your application to production, you may find it necessary to add or remove fields on an existing model. You can achieve this by creating a new migration.
|
||||
|
||||
|
|
@ -155,8 +154,6 @@ struct AddGalaxyMass: <#Database#>Migration {
|
|||
}
|
||||
```
|
||||
|
||||
### Prepare
|
||||
|
||||
Our prepare method will look very similar to the prepare method for a new table, except it will only contain our newly added field.
|
||||
|
||||
```swift
|
||||
|
|
@ -173,8 +170,6 @@ struct AddGalaxyMass: <#Database#>Migration {
|
|||
|
||||
All methods available when creating a schema will be available while updating alongside some new methods for deleting fields. See [`SchemaUpdater`](#fixme) for a list of all available methods.
|
||||
|
||||
### Revert
|
||||
|
||||
To revert this change, we must delete the `mass` field from the table.
|
||||
|
||||
```swift
|
||||
|
|
@ -188,8 +183,6 @@ struct AddGalaxyMass: <#Database#>Migration {
|
|||
}
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Add this migration to your [`MigrationConfig`](#fixme) using the `add(migration:database:)` method. This is done in your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
|
||||
```swift
|
||||
|
|
@ -199,7 +192,7 @@ migrations.add(migration: AddGalaxyMass.self, database: .<#dbid#>)
|
|||
services.register(migrations)
|
||||
```
|
||||
|
||||
## Data
|
||||
## Migrating Data
|
||||
|
||||
While migrations are useful for creating and updating schemas in SQL databases, they can also be used for more general purposes in any database. Migrations are passed a connection upon running which can be used to perform arbitrary database queries.
|
||||
|
||||
|
|
@ -209,12 +202,10 @@ The first step is to create our new migration type.
|
|||
|
||||
```swift
|
||||
struct GalaxyMassCleanup: <#Database#>Migration {
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Prepare
|
||||
|
||||
In the prepare method of this migration, we will perform a query to delete all galaxies which have a mass equal to `0`.
|
||||
|
||||
```swift
|
||||
|
|
@ -222,11 +213,11 @@ struct GalaxyMassCleanup: <#Database#>Migration {
|
|||
static func prepare(on conn: <#Database#>Connection) -> Future<Void> {
|
||||
return Galaxy.query(on: conn).filter(\.mass == 0).delete()
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Revert
|
||||
|
||||
There is no way to undo this migration since it is destructive. You can omit the `revert(...)` method by returning a pre-completed future.
|
||||
|
||||
```swift
|
||||
|
|
@ -239,9 +230,7 @@ struct GalaxyMassCleanup: <#Database#>Migration {
|
|||
}
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
Add this migration to your [`MigrationConfig`](#fixme) using the `add(migration:database:)` method. This is done in your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
Add this migration to your [`MigrationConfig`](#fixme) using the `add(migration:database:)` method. This is done in [`configure.swift`](../getting-started/structure.md#configureswift).
|
||||
|
||||
```swift
|
||||
var migrations = MigrationConfig()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
# Fluent PostgreSQL
|
||||
|
||||
Fluent PostgreSQL ([vapor/fluent-postgresql](https://github.com/vapor/fluent-postgresql)) is a type-safe, fast, and easy-to-use ORM for PostgreSQL built on top of [Fluent](../fluent/getting-started.md).
|
||||
|
||||
!!! seealso
|
||||
The Fluent PostgreSQL package is built on top of [Fluent](../fluent/getting-started.md) and the pure Swift, NIO-based [PostgreSQL Core](../postgresql/getting-started.md). You should refer to their guides for more information about subjects not covered here.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This section will show you how to add Fluent PostgreSQL to your project, create your first `PostgreSQLModel`, and make a database query.
|
||||
|
||||
### Package
|
||||
|
||||
The first step to using Fluent PostgreSQL is adding it as a dependency to your project in your SPM package manifest file.
|
||||
|
||||
```swift
|
||||
// swift-tools-version:4.0
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MyApp",
|
||||
dependencies: [
|
||||
/// Any other dependencies ...
|
||||
|
||||
// 🖋🐘 Swift ORM (queries, models, relations, etc) built on PostgreSQL.
|
||||
.package(url: "https://github.com/vapor/fluent-postgresql.git", from: "1.0.0"),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "App", dependencies: ["FluentPostgreSQL", ...]),
|
||||
.target(name: "Run", dependencies: ["App"]),
|
||||
.testTarget(name: "AppTests", dependencies: ["App"]),
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
Don't forget to add the module as a dependency in the `targets` array. Once you have added the dependency, regenerate your Xcode project with the following command:
|
||||
|
||||
```sh
|
||||
vapor xcode
|
||||
```
|
||||
|
||||
### Model
|
||||
|
||||
Now let's create your first `PostgreSQLModel`. Models represent tables in your PostgreSQL database and they are the primary method of interacting with your data.
|
||||
|
||||
```swift
|
||||
import FluentPostgreSQL
|
||||
import Vapor
|
||||
|
||||
/// A simple user.
|
||||
final class User: PostgreSQLModel {
|
||||
/// The unique identifier for this user.
|
||||
var id: Int?
|
||||
|
||||
/// The user's full name.
|
||||
var name: String
|
||||
|
||||
/// The user's current age in years.
|
||||
var age: Int
|
||||
|
||||
/// Creates a new user.
|
||||
init(id: Int? = nil, name: String, age: Int) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.age = age
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The example above shows a `PostgreSQLModel` for a simple model representing a user. You can make both `struct`s and `class`es a model. You can even conform types that come from external modules. The only requirement is that these types conform to `Codable`, which must be declared on the base type for synthesized (automatic) conformance.
|
||||
|
||||
Standard practice with PostgreSQL databases is using an auto-generated `BIGINT` for creating and storing unique identifiers in the `id` column. It's also possible to use `UUID`s or even `String`s for your identifiers. There are convenience protocol for that.
|
||||
|
||||
|protocol |type |key|
|
||||
|-----------------------|------|---|
|
||||
|`PostgreSQLModel` |Int |id |
|
||||
|`PostgreSQLUUIDModel` |UUID |id |
|
||||
|`PostgreSQLStringModel`|String|id |
|
||||
|
||||
!!! seealso
|
||||
Take a look at [Fluent → Model](../fluent/models.md) for more information on creating models with custom ID types and keys.
|
||||
|
||||
### Migration
|
||||
|
||||
Most of your models will have a corresponding table—or _schema_—in your database. You can use [Fluent → Migration](../fluent/migrations.md) to setup your schemas in a testable, maintainable way.
|
||||
|
||||
If you are creating models to represent an existing table in your database, you don't need a migration. Just set the `defaultDatabase` property on your model so that Fluent knows which database to use if none is specified explicitly.
|
||||
|
||||
```swift
|
||||
User.defaultDatabase = .psql
|
||||
```
|
||||
|
||||
#### Automatic
|
||||
|
||||
Fluent makes it easy to automatically generate a migration for your model
|
||||
|
||||
```swift
|
||||
/// Allows `User` to be used as a migration.
|
||||
extension User: PostgreSQLMigration { }
|
||||
```
|
||||
|
||||
That's all it takes. Fluent uses Codable to analyze your model and will attempt to create an appropriate schema for it.
|
||||
|
||||
## Configure
|
||||
|
||||
The final step is to configure your database. At a minimum, this requires adding two things to your [`configure.swift`](../getting-started/structure.md#configureswift) file.
|
||||
|
||||
- `FluentPostgreSQLProvider`
|
||||
- `MigrationConfig`
|
||||
|
||||
Let's take a look.
|
||||
|
||||
```swift
|
||||
import FluentPostgreSQL
|
||||
|
||||
/// ...
|
||||
|
||||
/// Register providers first
|
||||
try services.register(FluentPostgreSQLProvider())
|
||||
|
||||
/// Configure migrations
|
||||
var migrations = MigrationConfig()
|
||||
migrations.add(model: User.self, database: .psql)
|
||||
services.register(migrations)
|
||||
|
||||
/// Other services....
|
||||
```
|
||||
|
||||
!!! tip
|
||||
If this migration you are adding is also a model, you can use the [`add(model:on:)`](#fixme) convenience to automatically set the model's [`defaultDatabase`](#fixme) property. Otherwise, use the [`add(migration:on)`](#fixme) method.
|
||||
|
||||
Registering the provider will add all of the services required for Fluent PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials.
|
||||
|
||||
You can of course override this config struct if you have non-standard credentials.
|
||||
|
||||
```swift
|
||||
/// Register custom PostgreSQL Config
|
||||
let psqlConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "vapor")
|
||||
services.register(psqlConfig)
|
||||
```
|
||||
|
||||
Once you have the `MigrationConfig` added, you should be able to run your application and see the following:
|
||||
|
||||
```sh
|
||||
Migrating psql DB
|
||||
Migrations complete
|
||||
Server starting on http://localhost:8080
|
||||
```
|
||||
|
||||
## Query
|
||||
|
||||
Now that you have created a model and a corresponding schema in your database, let's make your first query.
|
||||
|
||||
```swift
|
||||
router.get("users") { req in
|
||||
return User.query(on: req).all()
|
||||
}
|
||||
```
|
||||
|
||||
If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent PostgreSQL model and migration working.
|
||||
|
||||
## Connection
|
||||
|
||||
With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a "raw query".
|
||||
|
||||
Let's take a look at a raw PostgreSQL query.
|
||||
|
||||
```swift
|
||||
router.get("psql-version") { req -> Future<String> in
|
||||
struct Version: Decodable {
|
||||
var version: String
|
||||
}
|
||||
|
||||
return req.withPooledConnection(to: .psql) { conn in
|
||||
return try conn.query("SELECT version() as version;", decoding: Version.self).map { rows in
|
||||
return try rows[0].version
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, `withPooledConnection(to:)` is used to create a connection to the database identified by `.psql`. This is the default database identifier. See [Fluent → Database](../fluent/database.md#identifier) to learn more.
|
||||
|
||||
Once you have the `PostgreSQLConnection`, we can perform a query on it. You can learn more about the methods available in [PostgreSQL → Core](core.md).
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# PostgreSQL Schema Builder
|
||||
|
||||
Fluent lets you customize your schemas with PostgreSQL-specific column types. You can even set default values. Let's take a look at creating a simple `Planet` model with custom PostgreSQL field tpyes.
|
||||
|
||||
```swift
|
||||
/// Type of planet.
|
||||
enum PlanetType: String, Codable, CaseIterable, ReflectionDecodable {
|
||||
case smallRocky
|
||||
case gasGiant
|
||||
case dwarf
|
||||
}
|
||||
|
||||
/// Represents a planet.
|
||||
struct Planet: PostgreSQLModel, PostgreSQLMigration {
|
||||
/// Unique identifier.
|
||||
var id: Int?
|
||||
|
||||
/// Name of the planet.
|
||||
let name: String
|
||||
|
||||
/// Planet's specific type.
|
||||
let type: PlanetType
|
||||
}
|
||||
```
|
||||
|
||||
The above model looks great, but there are a couple of problems with the automatically generated migration:
|
||||
|
||||
- `name` uses a `TEXT` column, but we want to use `VARCHAR(64)`.
|
||||
- `type` is defaulting to a `JSONB` column, but we want to store it as `ENUM()`.
|
||||
|
|
@ -20,7 +20,7 @@ You can use convenience methods on a `Container` to create connections manually.
|
|||
|
||||
## Create
|
||||
|
||||
One of the first things you will likely need to do is save some data to your database. You do this in Fluent by initializing an instance of your model then calling [`create(on:)`](#fixme).
|
||||
One of the first things you will need to do is save some data to your database. You do this by initializing an instance of your model then calling [`create(on:)`](#fixme).
|
||||
|
||||
```swift
|
||||
router.post("galaxies") { req in
|
||||
|
|
@ -81,7 +81,7 @@ Below is a list of all supported operators.
|
|||
|`>=`|Greater than or equal|
|
||||
|`<=`|Less than or equal|
|
||||
|
||||
By default, all chained will be used to limit the result set. You can use filter groups to change this behavior.
|
||||
By default, all chained filters will be used to limit the result set. You can use filter groups to change this behavior.
|
||||
|
||||
```swift
|
||||
Galaxy.query(on: conn).group(.or) {
|
||||
|
|
@ -111,6 +111,8 @@ Query results can be sorted by a given field.
|
|||
Galaxy.query(on: conn).sort(\.name, .descending)
|
||||
```
|
||||
|
||||
You can sort by multiple fields to perform tie breaking behavior where there is duplicate information in the one of the sorted fields.
|
||||
|
||||
### Join
|
||||
|
||||
Other models can be joined to an existing query in order to further filter the results.
|
||||
|
|
@ -122,6 +124,8 @@ Galaxy.query(on: conn).join(\Planet.galaxyID, to: \Galaxy.id)
|
|||
|
||||
Once a table has been joined using [`join(_:to:)`](#fixme), you can use fully-qualified key paths to filter results based on data in the joined table.
|
||||
|
||||
The above query fetches all galaxies that have a planet named Earth.
|
||||
|
||||
You can even decode the joined models using [`alsoDecode(...)`](#fixme).
|
||||
|
||||
```swift
|
||||
|
|
@ -156,18 +160,20 @@ For situations where memory conservation is important, use [`chunk(...)`](#fixme
|
|||
|
||||
```swift
|
||||
Galaxy.query(on: conn).chunk(max: 32) { galaxies in
|
||||
print(galaxies)
|
||||
print(galaxies) // Array of 32 or less galaxies
|
||||
}
|
||||
```
|
||||
|
||||
#### First
|
||||
|
||||
The [`first()`](#fixme) method is a convenience for fetching the first result of a query. It will automatically apply a range restriction to avoid transferring unnecessary data. Note that this makes the `first` method different than calling `all` and getting the first item in the array.
|
||||
The [`first()`](#fixme) method is a convenience for fetching the first result of a query. It will automatically apply a range restriction to avoid transferring unnecessary data.
|
||||
|
||||
```swift
|
||||
Galaxy.query(on: conn).filter(\.name == "Milky Way").first()
|
||||
```
|
||||
|
||||
This method is more efficient than calling `all` and getting the first item in the array.
|
||||
|
||||
## Update
|
||||
|
||||
After a model has been fetched from the database and mutated, you can use [`update(on:)`](#fixme) to save the changes.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,196 @@
|
|||
# Fluent Relations
|
||||
|
||||
Coming soon.
|
||||
Fluent supports two methods for relating models: one-to-many (parent-child) and many-to-many (siblings). These relations help make working with a [normalized data structure](https://en.wikipedia.org/wiki/Database_normalization) easy.
|
||||
|
||||
## Parent / Child
|
||||
## Parent-Child
|
||||
|
||||
Coming soon.
|
||||
The most common model relation is the one-to-many or _parent-child_ relation. In this relation, each child model stores at most one identifier of a parent model. In most cases, multiple child models can store the same parent identifier at the same time. This means that any given parent can have zero or more related child models. Hence the name, one (parent) to many (children).
|
||||
|
||||
!!! note
|
||||
If each child must store a _unique_ parent ID, this relation is called a one-to-one relation.
|
||||
|
||||
Take a look at the following diagram in which an example parent-child relation between two models (`Galaxy` and `Planet`) is shown.
|
||||
|
||||

|
||||
|
||||
In the example above, `Galaxy` is the parent and `Planet` is the child. Planets store an identifier referencing exactly one galaxy (the galaxy they are in). In turn, each galaxy has zero or more planets that belong to it.
|
||||
|
||||
Let's take a look at what these models would look like in Fluent.
|
||||
|
||||
```swift
|
||||
struct Galaxy: Model {
|
||||
// ...
|
||||
var id: Int?
|
||||
var name: String
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
struct Planet: Model {
|
||||
// ...
|
||||
var id: Int?
|
||||
var name: String
|
||||
var galaxyID: Int
|
||||
}
|
||||
```
|
||||
|
||||
For more information on defining models see [Fluent → Models](models.md).
|
||||
|
||||
Fluent provides two helpers for working with parent-child relations: [`Parent`](#fixme) and [`Children`](#fixme). These helpers can be created using extensions on the related models for convenient access.
|
||||
|
||||
```swift
|
||||
extension Galaxy {
|
||||
// this galaxy's related planets
|
||||
var planets: Children<Galaxy, Planet> {
|
||||
return children(\.galaxyID)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here the [`children(_:)`](#fixme) method is used on `Galaxy` to create the relation. The resulting type has two generic arguments in the signature that can be thought of as `<From, To`>. Since this relation goes _from_ galaxy _to_ planet, they are ordered as such in the generic arguments.
|
||||
|
||||
Note that this method is not static. That is because it must access the galaxy's identifier to perform the relation lookup.
|
||||
|
||||
```swift
|
||||
extension Planet {
|
||||
// this planet's related galaxy
|
||||
var galaxy: Parent<Planet, Galaxy> {
|
||||
return parent(\.galaxyID)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here the [`parent(_:)`](#fixme) method is used on `Planet` to create the inverse relation. The resulting type also has two generic arguments. In this case, they are reversed since this relation now goes _from_ planet _to_ galaxy.
|
||||
|
||||
Note that this method is also not static. That is because it must access the referenced identifier to perform the relation lookup.
|
||||
|
||||
Now that the models and relation properties are created, they can be used to create, read, update, and delete related data.
|
||||
|
||||
```swift
|
||||
let galaxy: Galaxy = ...
|
||||
let planets = galaxy.planets.query(on: ...).all()
|
||||
```
|
||||
|
||||
The `query(on:)` method on a relation creates an instance of [`QueryBuilder`](#fixme) filtered to the related models. See [Fluent → Querying](querying.md) for more information on working with the query builder.
|
||||
|
||||
```swift
|
||||
let planet: Planet = ...
|
||||
let galaxy = planet.galaxy.get(on: ...)
|
||||
```
|
||||
|
||||
Since the child can have at most _one_ parent, the most useful method is [`get(on:)`] which simply returns the parent model.
|
||||
|
||||
## Siblings
|
||||
|
||||
Coming soon.
|
||||
A more powerful (and complex) relation is the many-to-many or _siblings_ relation. In this relation, two models are related by a third model called a _pivot_. The pivot is a simple model that carries one identifier for each of the two related models. Because a third model (the pivot) stores identifiers, each model can be related to zero or more models on the other side of the relation.
|
||||
|
||||
Take a look at the following diagram in which an example siblings relation between two models (`Planet` and `Tag`) and a pivot (`PlanetTag`) is shown.
|
||||
|
||||

|
||||
|
||||
A siblings relation is required for the above example because:
|
||||
|
||||
- Both **Earth** and **Venus** have the **Earth Sized** tag.
|
||||
- **Earth** has both the **Earth Sized** and **Liquid Water** tag.
|
||||
|
||||
In other words, two planets can share one tag _and_ two tags can share one planet. This is a many-to-many relation.
|
||||
|
||||
Let's take a look at what these models would look like in Fluent.
|
||||
|
||||
|
||||
```swift
|
||||
struct Planet: Model {
|
||||
// ...
|
||||
var id: Int?
|
||||
var name: String
|
||||
var galaxyID: Int
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
struct Tag: Model {
|
||||
// ...
|
||||
var id: Int?
|
||||
var name: String
|
||||
}
|
||||
```
|
||||
|
||||
For more information on defining models see [Fluent → Models](models.md).
|
||||
|
||||
Now let's take a look at the pivot. It may seem a bit intimidating at first, but it's really quite simple.
|
||||
|
||||
```swift
|
||||
struct PlanetTag: Pivot {
|
||||
// ...
|
||||
|
||||
typealias Left = Planet
|
||||
typealias Right = Tag
|
||||
|
||||
static var leftIDKey: LeftIDKey = \.planetID
|
||||
static var rightIDKey: RightIDKey = \.tagID
|
||||
|
||||
var id: Int?
|
||||
var planetID: Int
|
||||
var tagID: Int
|
||||
}
|
||||
```
|
||||
|
||||
A pivot must have `Left` and `Right` model types. In this case, those model types are `Planet` and `Tag`. Although it is arbitrary which model is left vs. right, a good rule of thumb is to order things alphabetically for consistency.
|
||||
|
||||
Once the left and right models are defined, we must supply Fluent with key paths to the stored properties for each ID. We can use the `LeftIDKey` and `RightIDKey` type-aliases to do this.
|
||||
|
||||
A `Pivot` is also a `Model` itself. You are free to store any additional properties here if you like. Don't forget to create a migration for it if you are using a database that supports schemas.
|
||||
|
||||
Once the pivot and your models are created, you can add convenience extensions for interacting with the relation just like the parent-child relation.
|
||||
|
||||
```swift
|
||||
extension Planet {
|
||||
// this planet's related tags
|
||||
var tags: Siblings<Planet, Tag, PlanetTag> {
|
||||
return siblings()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because the siblings relation requires three models, it has three generic arguments. You can think of the arguments as `<From, To, Through>`. This relation goes _from_ a planet _to_ tags _through_ the planet tag pivot.
|
||||
|
||||
The other side of the relation (on tag) is similar. Only the first two generic arguments are flipped.
|
||||
|
||||
```swift
|
||||
extension Tag {
|
||||
// all planets that have this tag
|
||||
var planets: Siblings<Tag, Planet, PlanetTag> {
|
||||
return siblings()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now that the relations are setup, we can query a planet's tags. This works just like the `Children` type in the parent-child relationship.
|
||||
|
||||
```swift
|
||||
let planet: Planet = ...
|
||||
planet.tags.query(on: ...).all()
|
||||
```
|
||||
|
||||
### Modifiable Pivot
|
||||
|
||||
If the pivot conforms to [`ModifiablePivot`](#fixme), then Fluent can help to create and delete pivots (called attaching and detaching).
|
||||
|
||||
Conforming a pivot is fairly simple. Fluent just needs to be able to initialize the pivot from two related models.
|
||||
|
||||
```swift
|
||||
extension PlanetTag: ModifiablePivot {
|
||||
init(_ planet: Planet, _ tag: Tag) throws {
|
||||
planetID = try planet.requireID()
|
||||
tagID = try tag.requireID()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once the pivot type conforms, there will be extra methods available on the siblings relation.
|
||||
|
||||
```swift
|
||||
let planet: Planet = ...
|
||||
let tag: Tag = ...
|
||||
planet.tags.attach(tag, on: ...)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
# Fluent Schema Builder
|
||||
|
||||
# Schema Builder
|
||||
|
||||
#### Custom
|
||||
|
||||
You can also implement custom migrations for more fine-grain control over the schemas generated. For example, you may want to store a User's name using a `VARCHAR(64)` instead of `TEXT`. Just like `PostgreSQLModel`, any `struct` or `class` can conform to `PostgreSQLMigration`.
|
||||
|
||||
```swift
|
||||
/// Creates a table for `User`s.
|
||||
struct CreateUser: PostgreSQLMigration {
|
||||
static func prepare(on conn: PostgreSQLConnection) -> Future<Void> {
|
||||
return PostgreSQLDatabase.create(User.self, on: conn) { builder in
|
||||
builder.field(for: \.id)
|
||||
builder.field(for: \.name, type: .varchar(64))
|
||||
}
|
||||
}
|
||||
|
||||
static func revert(on conn: PostgreSQLConnection) -> Future<Void> {
|
||||
return PostgreSQLDatabase.delete(User.self, on: conn)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Migrations consist of two methods: `prepare(...)` and `revert(...)`. The prepare method runs once and should prepare the database for storing and fetching your model. The revert method runs only if you need to undo changes to your database and should undo anything you do in the prepare method.
|
||||
|
||||
Custom migrations are also useful for situations where you may need to _alter_ an existing table, like to add a new column.
|
||||
|
||||
```swift
|
||||
/// Adds a new field to `User`'s table.
|
||||
struct AddUsernameToUser: PostgreSQLMigration {
|
||||
static func prepare(on conn: PostgreSQLConnection) -> Future<Void> {
|
||||
return PostgreSQLDatabase.update(User.self, on: conn) { builder in
|
||||
builder.field(for: \.username)
|
||||
}
|
||||
}
|
||||
|
||||
static func revert(on conn: PostgreSQLConnection) -> Future<Void> {
|
||||
return PostgreSQLDatabase.update(User.self) { builder in
|
||||
builder.deleteField(for: \.username, on: conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Constraints
|
||||
|
||||
You can also add foreign key and unique constraints to your models during a migration.
|
||||
|
||||
```swift
|
||||
// creates a foreign key constraint ensuring Post.userID is a valid User.id
|
||||
builder.foreignKey(from: \Post.userID, to: \User.id)
|
||||
// creates a unique constraint ensuring no other posts have the same slug
|
||||
builder.unique(on: \Post.slug)
|
||||
```
|
||||
|
||||
!!! seealso
|
||||
Take a look at [Fluent → Migration](../fluent/migrations.md) if you are interested in learning more about migrations.
|
||||
|
||||
|
||||
## Create
|
||||
|
||||
Coming soon.
|
||||
|
||||
## Update
|
||||
|
||||
Coming soon.
|
||||
|
||||
## Delete
|
||||
|
||||
Coming soon.
|
||||
|
||||
## References
|
||||
|
||||
Coming soon.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Fluent Transactions
|
||||
|
||||
Coming soon.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# PostgreSQL Data
|
||||
|
||||
More information coming soon.
|
||||
Loading…
Reference in New Issue