mirror of https://github.com/vapor/docs.git
commit
5a1305f997
18
couscous.yml
18
couscous.yml
|
|
@ -56,6 +56,24 @@ menu:
|
|||
guide-validation:
|
||||
text: Validation
|
||||
relativeUrl: guide/validation.html
|
||||
guide-hash:
|
||||
text: Hash
|
||||
relativeUrl: guide/hash.html
|
||||
fluent:
|
||||
name: Fluent
|
||||
items:
|
||||
fluent-driver:
|
||||
text: Driver
|
||||
relativeUrl: fluent/driver.html
|
||||
fluent-model:
|
||||
text: Model
|
||||
relativeUrl: fluent/model.html
|
||||
fluent-query:
|
||||
text: Query
|
||||
relativeUrl: fluent/query.html
|
||||
fluent-relation:
|
||||
text: Relation
|
||||
relativeUrl: fluent/relation.html
|
||||
routing:
|
||||
name: Routing
|
||||
items:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
currentMenu: fluent-driver
|
||||
---
|
||||
|
||||
# Driver
|
||||
|
||||
Drivers are what power Fluent under the hood. Fluent comes with a memory driver by default and there are many providers for databases like MySQL, SQLite, Mongo, PostgreSQL, and more available as providers.
|
||||
|
||||

|
||||
|
||||
This graphic shows the relation between Drivers and Providers using MySQL as an example. This distinction allows Fluent to be used independently from Vapor.
|
||||
|
||||
If you want to use Fluent without Vapor, you will import Drivers into your package. If you are using Vapor, you will import Providers.
|
||||
|
||||
Search GitHub for:
|
||||
- [Fluent Drivers](https://github.com/vapor?utf8=✓&query=-driver)
|
||||
- [Vapor Providers](https://github.com/vapor?utf8=✓&query=-provider)
|
||||
|
||||
Not all drivers have providers yet, and not all drivers or providers are up to date with the latest Vapor 0.16. This is a great way to contribute!
|
||||
|
||||
## Creating a Driver
|
||||
|
||||
Fluent is a power, database agnostic package for persisting your models. It was designed from the beginnign to work with both SQL and NoSQL databases alike.
|
||||
|
||||
Any database that conforms to `Fluent.Driver` will be able to power the models in Fluent and Vapor.
|
||||
|
||||
The protocol itself is fairly simple:
|
||||
|
||||
```swift
|
||||
public protocol Driver {
|
||||
var idKey: String { get }
|
||||
func query<T: Entity>(_ query: Query<T>) throws -> Node
|
||||
func schema(_ schema: Schema) throws
|
||||
func raw(_ raw: String, _ values: [Node]) throws -> Node
|
||||
}
|
||||
```
|
||||
|
||||
### ID Key
|
||||
|
||||
The ID key will be used to power functionality like `User.find()`. In SQL, this is often `id`. In MongoDB, it is `_id`.
|
||||
|
||||
### Query
|
||||
|
||||
This method will be called for every query made by Fluent. It is the drivers job to properly understand all of the properties on `Query` and return the desired rows, document, or other data as represented by `Node`.
|
||||
|
||||
### Schema
|
||||
|
||||
The schema method will be called before the database is expected to accept queries for a schema. For some NoSQL databases like MongoDB, this can be ignored. For SQL, this is where `CREATE` and other such commands should be called according to `Schema`.
|
||||
|
||||
### Raw
|
||||
|
||||
This is an optional method that can be used by any Fluent driver that accepts string queries. If your database does not accept such queries, an error can be thrown.
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
---
|
||||
currentMenu: fluent-model
|
||||
---
|
||||
|
||||
# Model
|
||||
|
||||
`Model` is the base protocol for any of your application's models, especially those you want to persist.
|
||||
|
||||
> `Model` is only available in Vapor, the Fluent equivalent is `Entity`
|
||||
|
||||
## Example
|
||||
|
||||
Let's create a simple `User` model.
|
||||
|
||||
```swift
|
||||
final class User {
|
||||
var name: String
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The first step to conforming to `Model` is to import Vapor and Fluent.
|
||||
|
||||
```swift
|
||||
import Vapor
|
||||
import Fluent
|
||||
```
|
||||
|
||||
Then add the conformance to your class.
|
||||
|
||||
```swift
|
||||
final class User: Model {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The compiler will inform you that some methods need to be implemented to conform.
|
||||
|
||||
### ID
|
||||
|
||||
The first required property is an identifier. This property will contain the identifier when the model is fetched from the database. If it is nil, it will be set when the model is saved.
|
||||
|
||||
```swift
|
||||
final class User: Model {
|
||||
var id: Node?
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Node Initializable
|
||||
|
||||
The next requirement is a way to create the model from the persisted data. Model uses `NodeInitializable` to achieve this.
|
||||
|
||||
```swift
|
||||
final class User: Model {
|
||||
init(node: Node, in context: Context) throws {
|
||||
id = try node.extract("id")
|
||||
name = try node.extract("name")
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The keys `id` and `name` are what we expect the columns or fields in the database to be named. The `extract` call is marked with a `try` because it will throw an error if the value is not present or is the wrong type.
|
||||
|
||||
### Node Representable
|
||||
|
||||
Now that we have covered initializing the model, we need to show it how to save back into the database. Model uses `NodeRepresentable` to achieve this.
|
||||
|
||||
```swift
|
||||
final class User: Model {
|
||||
func makeNode() throws -> Node {
|
||||
return try Node(node: [
|
||||
"id": id,
|
||||
"name": name
|
||||
])
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
When a `User` is saved, the `makeNode()` method will be called and the resulting `Node` will be saved to the database. The keys `id` and `name` are what we expect the columns or fields in the database to be named.
|
||||
|
||||
## Preparations
|
||||
|
||||
Some databases, like MySQL, need to be prepared for a new schema. In MySQL, this means creating a new table.
|
||||
|
||||
### Prepare
|
||||
|
||||
Let's assume we are using a SQL database. To prepare the database for our `User` class, we need to create a table. If you are using a database like Mongo, you can leave this method unimplemented.
|
||||
|
||||
```swift
|
||||
final class User {
|
||||
static func prepare(_ database: Database) throws {
|
||||
try database.create("users") { users in
|
||||
users.id()
|
||||
users.string("name")
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Here we create a table named `users` that has an identifier field and a string field with the key `name`. This matches both our `init(node: Node)` and `makeNode() -> Node` methods.
|
||||
|
||||
### Revert
|
||||
|
||||
An optional preparation reversion can be created. This will be run if `vapor run prepare --revert` is called.
|
||||
|
||||
```swift
|
||||
final class User {
|
||||
static func revert(_ database: Database) throws {
|
||||
try database.delete("users")
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Here we are deleting the table named `users`.
|
||||
|
||||
### Droplet
|
||||
|
||||
To run these prepations when the applications boots, you must add the Model to the `Droplet`.
|
||||
|
||||
```swift
|
||||
let drop = Droplet(preparations: [User.self])
|
||||
```
|
||||
|
||||
## Full Model
|
||||
|
||||
This is what our final `User` model looks like:
|
||||
|
||||
```swift
|
||||
import Vapor
|
||||
import Fluent
|
||||
|
||||
final class User: Model {
|
||||
var id: Node?
|
||||
var name: String
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
|
||||
init(node: Node, in context: Context) throws {
|
||||
id = try node.extract("id")
|
||||
name = try node.extract("name")
|
||||
}
|
||||
|
||||
func makeNode() throws -> Node {
|
||||
return try Node(node: [
|
||||
"id": id,
|
||||
"name": name
|
||||
])
|
||||
}
|
||||
|
||||
static func prepare(_ database: Database) throws {
|
||||
try database.create("users") { users in
|
||||
users.id()
|
||||
users.string("name")
|
||||
}
|
||||
}
|
||||
|
||||
static func revert(_ database: Database) throws {
|
||||
try database.delete("users")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Interacting
|
||||
|
||||
Now that `User` conforms to `Model`, it has a plethora of new methods like `find()`, `query()`, `makeJSON()` and more.
|
||||
|
||||
### Fetch
|
||||
|
||||
Models can be fetched by their database identifier.
|
||||
|
||||
```swift
|
||||
let user = try User.find(42)
|
||||
```
|
||||
|
||||
### Save
|
||||
|
||||
Newly created models can be saved to the database.
|
||||
|
||||
```swift
|
||||
var user = User(name: "Vapor")
|
||||
try user.save()
|
||||
print(user.id) // prints the new id
|
||||
```
|
||||
|
||||
### Delete
|
||||
|
||||
Persisted models with identifiers can be deleted.
|
||||
|
||||
```swift
|
||||
try user.delete()
|
||||
```
|
||||
|
||||
## Model vs. Entity
|
||||
|
||||
Model has a couple of extra conformances that a pure Fluent entity doesn't have.
|
||||
|
||||
```swift
|
||||
public protocol Model: Entity, JSONRepresentable, StringInitializable, ResponseRepresentable {}
|
||||
```
|
||||
|
||||
As can be seen in the protocol, Vapor models can automatically convert to `JSON`, `Response`, and even be used in type-safe routing.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
currentMenu: fluent-query
|
||||
---
|
||||
|
||||
# Query
|
||||
|
||||
The `Query` class is what powers every interaction with Fluent. Whether you're fetching a model with `.find()` or saving to the database, there is a `Query` involved somewhere.
|
||||
|
||||
## Querying Models
|
||||
|
||||
Every type that conforms to [Model](model.md) gets a static `.query()` method.
|
||||
|
||||
```swift
|
||||
let query = try User.query()
|
||||
```
|
||||
|
||||
This is how you can create a `Query<User>`.
|
||||
|
||||
### No Database
|
||||
|
||||
The `.query()` method is marked with `try` because it can throw an error if the Model has not had its static database property set.
|
||||
|
||||
```swift
|
||||
User.database = drop.database
|
||||
```
|
||||
|
||||
This property is set automatically when you pass the Model as a preparation.
|
||||
|
||||
## Filter
|
||||
|
||||
The most common types of queries involve filtering data.
|
||||
|
||||
```swift
|
||||
let smithsQuery = try User.query().filter("last_name", "Smith")
|
||||
```
|
||||
|
||||
Here is the short hand for adding an `equals` filter to the query. As you can see, queries can be chained together.
|
||||
|
||||
In additional to `equals`, there are many other types of `Filter.Comparison`.
|
||||
|
||||
```swift
|
||||
let over21 = try User.query().filter("age", .greaterThanOrEquals, 21)
|
||||
```
|
||||
|
||||
### Scope
|
||||
|
||||
Filters can also be run on sets.
|
||||
|
||||
```swift
|
||||
let coolPets = try Pet.query().filter("type", .in, ["Dog", "Ferret"])
|
||||
```
|
||||
|
||||
Here only Pets of type Dog _or_ Ferret are returned. The opposite works for `notIn`.
|
||||
|
||||
|
||||
### Contains
|
||||
|
||||
Partially matching filters can also be applied.
|
||||
|
||||
```swift
|
||||
let statesWithNew = try State.query().filter("name", contains: "New")
|
||||
```
|
||||
|
||||
## Retrieving
|
||||
|
||||
There are two methods for running a query.
|
||||
|
||||
### All
|
||||
|
||||
All of the matching entities can be fetched. This returns an array of `[Model]`, in this case users.
|
||||
|
||||
```swift
|
||||
let usersOver21 = try User.query().filter("age", .greaterThanOrEquals, 21).all()
|
||||
```
|
||||
|
||||
### First
|
||||
|
||||
The first matching entity can be fetch. This returns an optional `Model?`, in this case a user.
|
||||
|
||||
```swift
|
||||
let firstSmith = try User.query().filter("last_name", "Smith").first()
|
||||
```
|
||||
|
||||
## Union
|
||||
|
||||
Other Models can be joined onto your query to assist in filtering. The results must still be either `[Model]` or `Model?` for whichever type created the query.
|
||||
|
||||
```swift
|
||||
let usersWithCoolPets = try User.query()
|
||||
.union(Pet.self)
|
||||
.filter(Pet.self, "type", .in, ["Dog", "Ferret"])
|
||||
```
|
||||
|
||||
Here the `User` collection is unioned to the `Pet` collection. Only `User`s who have a dog or a ferret will be returned.
|
||||
|
||||
### Keys
|
||||
|
||||
The `union` method assumes that the querying table has a foreign key identifier to the joining table.
|
||||
|
||||
The above example with users and pets assumes the following schema.
|
||||
|
||||
```
|
||||
users
|
||||
- id
|
||||
- pet_id
|
||||
pets
|
||||
- id
|
||||
```
|
||||
|
||||
Custom foreign keys can be provided through overloads to `union`.
|
||||
|
||||
## Raw Queries
|
||||
|
||||
Since Fluent is focused on interacting with models, each Query requires a model type. If you want to do raw database queries that aren't based on a model, you should use the underlying Fluent Driver to do so.
|
||||
|
||||
```swift
|
||||
if let mysql = drop.database?.driver as? MySQLDriver {
|
||||
let version = try mysql.raw("SELECT @@version")
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
currentMenu: fluent-relation
|
||||
---
|
||||
|
||||
# Relation
|
||||
|
||||
Relations allow foreign key based connections between database entities. This is common in SQL-based databases, but can also be used with NoSQL.
|
||||
|
||||
Fluent's relations are named as follows:
|
||||
- Parent (BelongsTo)
|
||||
- Children (HasMany, HasOne)
|
||||
- Siblings (ManyToMany, BelongsToMany)
|
||||
|
||||
## Parent
|
||||
|
||||
The parent relation should be called on an entity that has a foreign key to another entity. For example, assume the following schema:
|
||||
|
||||
```
|
||||
pets
|
||||
- id
|
||||
- owner_id
|
||||
- name
|
||||
- type
|
||||
|
||||
owner
|
||||
- id
|
||||
- name
|
||||
```
|
||||
|
||||
Here each pet can have one owner. To access that owner from the pet, call `.parent()`.
|
||||
|
||||
```swift
|
||||
let pet: Pet = ...
|
||||
let owner = try pet.parent(pet.ownerId, Owner.self).get()
|
||||
```
|
||||
|
||||
The parent method requires the foreign key for the parent as well as the type.
|
||||
|
||||
### Convenience
|
||||
|
||||
To make requesting a parent easier, a method can be added to the model.
|
||||
|
||||
```swift
|
||||
extension Pet {
|
||||
func owner() throws -> Parent<Owner> {
|
||||
return try parent(ownerId)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Since we are extending `Pet`, we no longer need to use `pet.` before the `ownerId`. Furthermore, because we are providing the type information about `Owner` in the return type of the method, we no longer need to pass that as an argument.
|
||||
|
||||
The `Parent<T>` type is a queryable object, meaning you could `delete()` the parent, `filter()`, etc.
|
||||
|
||||
```swift
|
||||
try pet.owner().delete()
|
||||
```
|
||||
|
||||
To fetch the parent, you must call `get()`.
|
||||
|
||||
```swift
|
||||
let owner = try pet.owner().get()
|
||||
```
|
||||
|
||||
## Children
|
||||
|
||||
`Children` are the opposite side of the `Parent` relationship. Assuming the schema from the previous example, the pets could be retreived from an owner like so:
|
||||
|
||||
```swift
|
||||
let owner: Owner = ...
|
||||
let pets = owner.children(Pet.self).all()
|
||||
```
|
||||
|
||||
Here only the type of child is required.
|
||||
|
||||
### Convenience
|
||||
|
||||
Similarly to `Parent`, convenience methods can be added for children.
|
||||
|
||||
```swift
|
||||
extension Owner {
|
||||
func pets() throws -> Children<Pet> {
|
||||
return try children()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Since the type information is clear from the return value, `Pet.self` does not need to be passed to `children()`.
|
||||
|
||||
The `Children<T>` is also a queryable object like `Parent<T>`. You can call `first()`, `all()`, `filter()`, etc.
|
||||
|
||||
```swift
|
||||
let coolPets = try owner.pets().filter("type", .in, ["Dog", "Ferret"]).all()
|
||||
```
|
||||
|
||||
## Siblings
|
||||
|
||||
`Sibilings` work differently from `Children` or `Parent` since they require a `Pivot`.
|
||||
|
||||
For an example, let's say we want to allow our pets to have multiple toys. But we also want the toys to be shared by multiple pets. We need a pivot entity for this.
|
||||
|
||||
```
|
||||
pets
|
||||
- id
|
||||
- type
|
||||
- owner_id
|
||||
|
||||
toys
|
||||
- id
|
||||
- name
|
||||
|
||||
pets_toys
|
||||
- id
|
||||
- pet_id
|
||||
- toy_id
|
||||
```
|
||||
|
||||
Here you can see the pivot entity, `pets_toys`, or `Pivot<Pet, Toy>`.
|
||||
|
||||
### Convenience
|
||||
|
||||
Let's add the convenience methods to `Pet`.
|
||||
|
||||
```swift
|
||||
extension Pet {
|
||||
func toys() throws -> Siblings<Toy> {
|
||||
return try siblings()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And the opposite for `Toy`.
|
||||
|
||||
```
|
||||
extension Toy {
|
||||
func pets() throws -> Siblings<Pet> {
|
||||
return try siblings()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you are free to query pets and toys simiarly to children.
|
||||
|
||||
```swift
|
||||
let pet: Pet = ...
|
||||
let toys = pet.toys().all()
|
||||
```
|
||||
|
||||
### Preparation
|
||||
|
||||
To prepare for a relationship with a `Pivot`, simply add the pivot to the `Droplet`'s preparations.
|
||||
|
||||
```swift
|
||||
let drop = Droplet(preparations: [
|
||||
Toy.self,
|
||||
Pet.self,
|
||||
Pivot<Toy, Pet>.self
|
||||
])
|
||||
```
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
currentMenu: guide-hash
|
||||
---
|
||||
|
||||
# Hash
|
||||
|
||||
Vapor makes hashing strings easy.
|
||||
|
||||
## Example
|
||||
|
||||
To hash a string, use the `hash` class on `Droplet`.
|
||||
|
||||
```swift
|
||||
let hashed = drop.hash.make("vapor")
|
||||
```
|
||||
|
||||
## SHA2Hasher
|
||||
|
||||
By default, Vapor uses a SHA2Hasher with 256 bits. You can change this by giving the `Droplet` a different hasher.
|
||||
|
||||
```swift
|
||||
let sha512 = SHA2Hasher(variant: .sha512)
|
||||
|
||||
let drop = Droplet(hash: sha512)
|
||||
```
|
||||
|
||||
### Protocol
|
||||
|
||||
You can also create your own hasher. You just need to conform to the `Hash` protocol.
|
||||
|
||||
```swift
|
||||
public protocol Hash: class {
|
||||
var key: String { get set }
|
||||
func make(_ string: String) -> String
|
||||
}
|
||||
```
|
||||
Loading…
Reference in New Issue