mirror of https://github.com/vapor/docs.git
247 lines
5.9 KiB
Markdown
247 lines
5.9 KiB
Markdown
---
|
||
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 how to save it back into the database. Model uses `NodeRepresentable` to achieve this.
|
||
|
||
```swift
|
||
final class User: Model {
|
||
func makeNode(context: Context) 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.
|
||
|
||
> In most of the cases you do not need to be concerned about `context` argument of the `makeNode(context:)` method. It’s a part of the protocol that allows extensibility in more advanced or specific scenarios.
|
||
|
||
## Preparations
|
||
|
||
Some databases, like MySQL, need to be prepared for a new schema. In MySQL, this means creating a new table. Preparations are also equatable to migrations, as they can be used to alter schemas after they've already been created.
|
||
|
||
### 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`.
|
||
|
||
### Preparations as Migrations
|
||
|
||
If you want to add a field to your table after you've already created the initial schema, you can create a struct or class that conforms to `Preparation` like so:
|
||
|
||
```swift
|
||
|
||
struct AddFooToBar: Preparation {
|
||
static func prepare(_ database: Database) throws {
|
||
try database.modify("bars", closure: { bar in
|
||
bar.string("foo", length: 150, optional: false, unique: false, default: nil)
|
||
})
|
||
}
|
||
|
||
static func revert(_ database: Database) throws {
|
||
|
||
}
|
||
}
|
||
```
|
||
|
||
Then, in your Droplet setup, add this line: `drop.preparations.append(AddFooToBar.self)`
|
||
|
||
### Droplet
|
||
|
||
To run these prepations when the applications boots, you must add the Model to the `Droplet`.
|
||
|
||
```swift
|
||
let drop = Droplet()
|
||
|
||
drop.preparations.append(User.self)
|
||
```
|
||
|
||
> Note: Preparations must be appended before the Droplet is run.
|
||
|
||
## 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(context: Context) 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.
|
||
|
||
## Options
|
||
|
||
Change the table/collection name
|
||
```swift
|
||
static var entity = "new_name"
|
||
```
|