mirror of https://github.com/vapor/docs.git
177 lines
3.8 KiB
Markdown
177 lines
3.8 KiB
Markdown
---
|
|
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 retrieved 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
|
|
|
|
`Siblings` 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`.
|
|
|
|
```swift
|
|
extension Toy {
|
|
func pets() throws -> Siblings<Pet> {
|
|
return try siblings()
|
|
}
|
|
}
|
|
```
|
|
|
|
Now you are free to query pets and toys similarly to children.
|
|
|
|
```swift
|
|
let pet: Pet = ...
|
|
let toys = pet.toys().all()
|
|
```
|
|
|
|
To create a new many-to-many relationship you can do the following.
|
|
|
|
```swift
|
|
|
|
var toy: Toy = ... // Create a new toy
|
|
try toy.save() // Save the toy to the db
|
|
|
|
|
|
var pet: Pet = ... // Create a new pet
|
|
try pet.save() // Save the pet to the db
|
|
|
|
// Link them together in the db
|
|
var pivot = Pivot<Toy, Pet>(toy, pet) // Create the relationship
|
|
try pivot.save() // Save the relationship to the db
|
|
```
|
|
|
|
### Preparation
|
|
|
|
To prepare for a relationship with a `Pivot`, simply add the pivot to the `Droplet`'s preparations.
|
|
|
|
```swift
|
|
let drop = Droplet()
|
|
drop.preparations += [
|
|
Toy.self,
|
|
Pet.self,
|
|
Pivot<Toy, Pet>.self
|
|
]
|
|
```
|