vapor-docs/3.0/docs/fluent/querying.md

192 lines
5.6 KiB
Markdown

# Querying Models
Once you have a [model](models.md) (and optionally a [migration](migrations.md)) you can start
querying your database to create, read, update, and delete data.
## Connection
The first thing you need to query your database, is a connection to it. Luckily, they are easy to get.
You can use either the application or an incoming request to create a database connection. You just need
access to the [`DatabaseIdentifier`](provider.md#identifier).
### Request
The preferred method for getting access to a database connection is via an incoming request.
```swift
router.get(...) { req in
return req.withConnection(to: .foo) { db in
// use the db here
}
}
```
The first parameter is the database's [identifier](provider.md#identifier). The second parameter is a closure
that accepts a connection to that database.
!!! tip
Although the closure to `.withConnection(to: ...)` accepts a database _connection_, we often use just `db` for short.
The closure is expected to return a `Future<Void>`. When this future is completed, the connection will be released
back into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.
### Application
You can also create a database connection using the application. This is useful for cases where you must access
the database from outside a request/response event.
```swift
let res = app.withConnection(to: .foo) { db in
// use the db here
}
print(res) // Future<T>
```
This is usually done in the [boot section](../getting-started/structure.md#boot) of your application.
!!! warning
Do not use database connections created by the application in a route closure (when responding to a request).
Always use the incoming request to create a connection to avoid threading issues.
## Create
To create (save) a model to the database, first initialize an instance of your model, then call `.save(on: )`.
```swift
router.post(...) { req in
return req.withConnection(to: .foo) { db -> Future<User> in
let user = User(name: "Vapor", age: 3)
return user.save(on: db).transform(to: user) // Future<User>
}
}
```
### Response
`.save(on: )` returns a `Future<Void>` that completes when the user has finished saving. In this example, we then
map that `Future<Void>` to a `Future<User>` by calling `.map` and passing in the recently-saved user.
You can also use `.map` to return a simple success response.
```swift
router.post(...) { req in
return req.withConnection(to: .foo) { db -> Future<HTTPResponse> in
let user = User(name: "Vapor", age: 3)
return user.save(on: db).map(to: HTTPResponse.self) {
return HTTPResponse(status: .created)
}
}
}
```
### Multiple
If you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures.
```swift
router.post(...) { req in
return req.withConnection(to: .foo) { db -> Future<HTTPResponse> in
let marie = User(name: "Marie Curie", age: 66)
let charles = User(name: "Charles Darwin", age: 73)
return [
marie.save(on: db),
charles.save(on: db)
].map(to: HTTPResponse.self) {
return HTTPResponse(status: .created)
}
}
}
```
## Read
To read models from the database, use `.query()` on the database connection to create a [QueryBuilder](../query-builder).
### All
Fetch all instances of a model from the database using `.all()`.
```swift
router.get(...) { req in
return req.withConnection(to: .foo) { db -> Future<[User]> in
return db.query(User.self).all()
}
}
```
### Filter
Use `.filter(...)` to apply [filters](../query-builder#filters) to your query.
```swift
router.get(...) { req in
return req.withConnection(to: .foo) { db -> Future<[User]> in
return try db.query(User.self).filter(\User.age > 50).all()
}
}
```
### First
You can also use `.first()` to just get the first result.
```swift
router.get(...) { req in
return req.withConnection(to: .foo) { db -> Future<User> in
return try db.query(User.self).filter(\User.name == "Vapor").first().map(to: User.self) { user in
guard let user = user else {
throw Abort(.notFound, reason: "Could not find user.")
}
return user
}
}
}
```
Notice we use `.map(to:)` here to convert the optional user returned by `.first()` to a non-optional
user, or we throw an error.
## Update
```swift
router.put(...) { req in
return req.withConnection(to: .foo) { db -> Future<User> in
return db.query(User.self).first().map(to: User.self) { user in
guard let user = $0 else {
throw Abort(.notFound, reason: "Could not find user.")
}
return user
}.flatMap(to: User.self) { user in
user.age += 1
return user.update(on: db).map(to: User.self) { user }
}
}
}
```
Notice we use `.map(to:)` here to convert the optional user returned by `.first()` to a non-optional
user, or we throw an error.
## Delete
```swift
router.delete(...) { req in
return req.withConnection(to: .foo) { db -> Future<User> in
return db.query(User.self).first().map(to: User.self) { user in
guard let user = $0 else {
throw Abort(.notFound, reason: "Could not find user.")
}
return user
}.flatMap(to: User.self) { user in
return user.delete(on: db).transfom(to: user)
}
}
}
```
Notice we use `.map(to:)` here to convert the optional user returned by `.first()` to a non-optional
user, or we throw an error.