mirror of https://github.com/vapor/docs.git
192 lines
5.6 KiB
Markdown
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.
|