Vapor - Sibling Pivot with additional property
When dealing with siblings (many to many relationship) we may want to use additional properties on the pivot between the siblings to capture more details of the relationship. For example, let's say we have a sibling relationship between Books and Authors where an Author can write several Books, and a Book can be written by several Authors. This relationship between Books and Authors would be maintained by the pivot.
Now if we want to keep track of the number of words contributed by the authors to each books we would need to maintain this information as a property on the pivot.
To do this in Vapor with Fluent we would first add a property to the pivot
final class BookAuthorPivot: Model {
static let schema = "book-author-pivot"
@ID
var id: UUID?
@Parent(key: "book_id")
var book: Book
@Parent(key: "author_id")
var author: Author
@Field(key: "words")
var words: Int
}
So far so good. Now how do we access this words property on the pivot when we need it?
Well, we can access the pivot while creating the sibling relation using attach
as below:
book.$authors
.attach(author, method: .ifNotExists, on: database, { pivot in
pivot.words = authorWordCount
})
.transform(to: .created)
Then we can access the pivot property when required by eager loading it along with its siblings as so:
Book.query(on: db)
.with(\.$authors)
.with(\.$authors.$pivots)
.first() { book in
let bookAuthorWithWords = book.$authors.pivots
...
}
If we want to get the details of a book with all its authors along with the word count of each author we could load all the details and use a Domain Transfer Object (DTO) to return it as shown below:
Book.query(on: req.db)
.filter(\.$id == bookId)
.with(\.$authors)
.with(\.$authors.$pivots)
.first()
.unwrap(or: Abort(.notFound))
.map { book in
var bookAuthorsWithWords: [BookAuthorsWithWords] = []
for (bookAuthor, author) in zip(book.$authors.pivots, book.authors) {
bookAuthorsWithWords.append(BookAuthorsWithWords(book: book, author: author, words: bookAuthor.words))
}
return bookAuthorsWithWords
}
When we remove any sibling relationship from the pivot using detach
all its properties including the additional property we included are removed