Do you have any example of how to create One to Many relation using Vapor 2?
There are some examples of how to do this, but they use the old version of Vapor.
Thank you for all suggestions.
I have found a solution. Here is simple example of an owner having many cars, maybe will be helpful for someone.
Owner:
final class Owner: Model {
static let idKey = "id"
static let nameKey = "name"
static let carsKey = "cars"
var name: String
let storage = Storage()
var cars: Children<Owner, Car> {
return children()
}
init(name: String) {
self.name = name
}
init(row: Row) throws {
name = try row.get(Owner.nameKey)
}
func makeRow() throws -> Row {
var row = Row()
try row.set(Owner.nameKey, name)
return row
}
}
extension Owner: Preparation {
static func prepare(_ database: Database) throws {
try database.create(self) { builder in
builder.id()
builder.string(Owner.nameKey)
}
}
static func revert(_ database: Database) throws {
try database.delete(self)
}
}
extension Owner: JSONConvertible {
convenience init(json: JSON) throws {
try self.init(
name: json.get(Owner.nameKey)
)
}
func makeJSON() throws -> JSON {
var json = JSON()
try json.set(Owner.idKey, id)
try json.set(Owner.nameKey, name)
try json.set(Owner.carsKey, try cars.all())
return json
}
}
extension Owner: ResponseRepresentable { }
extension Owner: Updateable {
public static var updateableKeys: [UpdateableKey<Owner>] {
return [
UpdateableKey(Owner.nameKey, String.self) { owner, text in
owner.name = name
}
]
}
}
Car:
final class Car: Model {
static let idKey = "id"
static let makeKey = "make"
static let modelKey = "model"
static let ownerIdKey = "owner_id"
var make: String
var model: String
var ownerId: Identifier
let storage = Storage()
var owner: Parent<Car, Owner> {
return parent(id: ownerId)
}
init(make: String, model: String, ownerId: Identifier) {
self.make = make
self.model = model
self.ownerId = ownerId
}
init(row: Row) throws {
make = try row.get(Car.makeKey)
model = try row.get(Car.modelKey)
ownerId = try row.get(Car.ownerIdKey)
}
func makeRow() throws -> Row {
var row = Row()
try row.set(Car.makeKey, make)
try row.set(Car.modelKey, model)
try row.set(Car.ownerIdKey, ownerId)
return row
}
}
extension Car: JSONConvertible {
convenience init(json: JSON) throws {
try self.init(
make: json.get(Car.makeKey),
model: json.get(Car.modelKey),
ownerId: json.get(Car.ownerIdKey)
)
}
func makeJSON() throws -> JSON {
var json = JSON()
try json.set(Car.idKey, id)
try json.set(Car.makeKey, make)
try json.set(Car.modelKey, model)
try json.set(Car.ownerIdKey, ownerId)
return json
}
}
extension Car: ResponseRepresentable {}
extension Car: Preparation {
static func prepare(_ database: Database) throws {
try database.create(self) { builder in
builder.id()
builder.string(Car.makeKey)
builder.string(Car.modelKey)
builder.foreignId(for: Owner.self)
}
}
static func revert(_ database: Database) throws {
try database.delete(self)
}
}
extension Car: Updateable {
public static var updateableKeys: [UpdateableKey<Car>] {
return [
UpdateableKey(Car.makeKey, String.self) { car, make in
car.make = make
}
]
}
}
Related
I am using Vapor 3 to try and create just a sample project where I have a dish, the parent, and the reviews for the dish, the child. All the tutorials that I have been seeing haven't been very clear on how to create the relationship or they are using it in conjecture with leaf. I do not want to use leaf for this, I just want to be able to show all the reviews when for the dish when I give it's id, and it seems that it is different than it was for vapor 2.
My 2 models are Dish and Review
Dish.swift: The parent,
import Foundation
import Vapor
import FluentSQLite
final class Dish: Content {
var id: Int?
var name: String
var course: String
var price: Double
var imageURL: String
var description: String
init(name: String, course: String, price: Double, imageURL: String, description: String) {
self.name = name
self.course = course
self.price = price
self.imageURL = imageURL
self.description = description
}
}
extension Dish {
var reviews: Children<Dish, Review> {
return children(\.dishId)
}
}
extension Dish: Parameter { }
extension Dish: SQLiteModel {
static let entity: String = "Dishes"
}
extension Dish: Migration { }
Review.swift, the child,
import Foundation
import Vapor
import FluentSQLite
final class Review: Content {
var id: Int?
var title: String
var body: String
var dishId: Dish.ID
init(title: String, body: String, dishId: Dish.ID) {
self.title = title
self.body = body
self.dishId = dishId
}
}
extension Review {
var dish: Parent<Review, Dish> {
return parent(\.dishId)
}
}
extension Review: Migration { }
extension Review: SQLiteModel {
static let entity: String = "Reviews"
}
extension Review: Parameter { }
the controller for Dish, DishController,
import Foundation
import Vapor
import FluentSQLite
class DishesController: RouteCollection {
func boot(router: Router) throws {
let dishesRoutes = router.grouped("api/dishes")
dishesRoutes.get("/", use: getAll)
dishesRoutes.get(Dish.parameter, use: getById)
dishesRoutes.post(Dish.self, at: "/", use: createDish)
dishesRoutes.delete(Dish.parameter, use: deleteDish)
}
func deleteDish(req: Request) throws -> Future<Dish> {
return try req.parameters.next(Dish.self).delete(on: req)
}
func createDish(req: Request, dish: Dish) -> Future<Dish> {
return dish.save(on: req)
}
func getAll(req: Request) -> Future<[Dish]> {
return Dish.query(on: req).all()
}
func getById(req: Request) throws -> Future<Dish> {
return try req.parameters.next(Dish.self)
}
}
and the controller for reviews. ReviewController,
import Foundation
import Vapor
import FluentSQLite
class ReviewController: RouteCollection {
func boot(router: Router) throws {
let reviewRoutes = router.grouped("api/reviews")
reviewRoutes.get("/", use: getAll)
reviewRoutes.get(Review.parameter, use: getById)
reviewRoutes.post(Review.self, at: "/", use: createReview)
reviewRoutes.delete(Review.parameter, use: deleteReview)
}
func deleteReview(req: Request) throws -> Future<Review> {
return try req.parameters.next(Review.self).delete(on: req)
}
func createReview(req: Request, review: Review) -> Future<Review> {
return review.save(on: req)
}
func getAll(req: Request) -> Future<[Review]> {
return Review.query(on: req).all()
}
func getById(req: Request) throws -> Future<Review> {
return try req.parameters.next(Review.self)
}
}
this is the routes.swift,
import Vapor
/// Register your application's routes here.
public func routes(_ router: Router) throws {
router.get("/reviews", Dish.parameter,"dish") { request -> Future<Dish> in
return try request.parameters.next(Review.self).flatMap(to: Dish.self) { review in
return review.dish.get(on: request)
}
}
let dishesController = DishesController()
try router.register(collection: dishesController)
let reviewController = ReviewController()
try router.register(collection: reviewController)
}
I just want a simple one to many relationship where one dish can have many reviews, but when I use postman to try and access the reviews for the particular dish, all I get is an error. I know that I used the correct syntax in postman because I can use all the other requests from the controllers just fine, just not any for the relationships. Please tell me what i am missing, because I am getting confused as to what I am doing wrong. If there is anything else I can add please ask.
Thank you very much
If you want to access all reviews for the particular dish, try the following code.
router.get("/dish", Dish.parameter,"reviews") { request -> Future<[Review]> in
return try request.parameters.next(Dish.self).flatMap(to: [Review].self) { (dish) in
return try dish.reviews.query(on: request).all()
}
}
Now In postman, pass a dish id as below:
GET: http://localhost:8080/dish/1/reviews
Friend model contain the address reference. Address is saving but not able to retrieve from the end point.
Friend Model
import Foundation
import Vapor
import Fluent
struct Friend: Model {
var exists: Bool = false
var id: Node?
var name: String
var age: Int
var email: String
var residence: FriendAddress
init(name: String, age: Int, email: String, residence: FriendAddress) {
self.name = name
self.age = age
self.email = email
self.residence = residence
}
// NodeInitializable
init(node: Node, in context: Context) throws {
id = try node.extract("_id")
name = try node.extract("name")
age = try node.extract("age")
email = try node.extract("email")
residence = try node.extract("residence")
}
// NodeRepresentable
func makeNode(context: Context) throws -> Node {
return try Node(node: ["_id": id,
"name": name,
"age": age,
"email": email,
"residence": residence
])
}
// Preparation
static func prepare(_ database: Database) throws {
try database.create("friends") { friends in
friends.id()
friends.string("name")
friends.int("age")
friends.string("email")
friends.string("residence")
}
}
static func revert(_ database: Database) throws {
try database.delete("friends")
}
}
Friend Address Model contains owner_id acting as a foreign key (Friend Model)
Friend Addresss Model
import Foundation
import Vapor
import Fluent
struct FriendAddress: Model {
var id: Node?
var owner_id: Node?
var address: String
var address2: String
var pinCode: Int
init(address: String, address2: String, pinCode: Int, owner_id: Node? = nil ) {
self.address = address
self.address2 = address2
self.pinCode = pinCode
self.owner_id = owner_id
}
// NodeInitializable
init(node: Node, in context: Context) throws {
id = try node.extract("_id")
address = try node.extract("address")
address2 = try node.extract("address2")
pinCode = try node.extract("pinCode")
owner_id = try node.extract("owner_id")
}
// NodeRepresentable
func makeNode(context: Context) throws -> Node {
return try Node(node: [
"_id": id,
"address": address,
"address2": address2,
"pinCode": pinCode,
"owner_id": owner_id])
}
// Preparation
static func prepare(_ database: Database) throws {
try database.create("friendAddress") { friendaddress in
friendaddress.id()
friendaddress.string("address")
friendaddress.int("address2")
friendaddress.string("pinCode")
friendaddress.parent(Friend.self, optional: false)
}
}
static func revert(_ database: Database) throws {
try database.delete("friendAddress")
}
}
main.swift
import Vapor
import VaporMongo
import Fluent
import Foundation
let drop = Droplet()
//drop.preparations.append(Friend.self)
drop.preparations = [Friend.self, FriendAddress.self, Pivot<Friend, FriendAddress>.self]
do {
try drop.addProvider(VaporMongo.Provider.self)
} catch {
assertionFailure("Error adding provider: \(error)")
}
let friendController = FriendController()
friendController.addRoutes(drop: drop)
drop.resource("posts", PostController())
drop.run()
Using Vapor I want to store a relationship to children. I haven't been able to find any examples of what the class should look like and I'm just guessing on what to do. Can anyone provide an example of a class that has a relationship to a list of other Model objects?
import Vapor
import Fluent
import Foundation
final class Store: Model {
// MARK: - Model
var id: Node?
var exists: Bool = false
var locationIDs: [Node] = [] // No idea if this is right
var name: String
init(name: String, locationIDs: [Node] = []) {
self.id = nil
self.name = name
self.locationIDs = locationIDs
}
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( "stores" ) { creator in
creator.id()
creator.string("name")
// creator.id("", optional: false) // ???
}
}
static func revert(_ database: Database) throws {
try database.delete("stores")
}
}
Here is my implementation of a User and Group relationship. A user belongs to a group and group has many users, so a simple 1 -> Many relationship.
final public class User: Model {
public var id: Int?
var emailAddress: String
var username: String
var groupId: Int
}
extension User {
var group: Parent<User, Group> {
return parent(\.groupId)
}
}
final public class Group: Model {
public var id: Int?
var name: String
}
extension Group {
var users: Children<Group, Users> {
return children(\.groupId)
}
}
Many <-> Many are similar but use a pivot table in the middle of the relationship.
So I'm trying to build a data-mapper style ORM in Swift. I was trying to find a way to turn any entity into a dictionary so I could map that to a database table. I managed to do that with this code:
public protocol Entity: class {
var id: Int? { get set }
}
func unwrap(any:Any) -> Any? {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}
if mi.children.count == 0 {
return nil
}
let (_, some) = mi.children.first!
return some
}
public func serialize<T: Entity>(entity: T) -> [String:String] {
let aMirror = Mirror(reflecting: entity)
var dict = [String:String]()
for child in aMirror.children {
if let label = child.label where !label.hasPrefix("_") {
switch unwrap(child.value) {
case let entity as Entity:
if let id = entity.id {
dict[label] = String(id)
}
case let .Some(test):
dict[label] = String(test)
default: break
}
}
}
return dict
}
public class User: Entity {
public var id: Int?
var username: String
var password: String
var role: UserRole?
public init(username: String, password: String) {
self.username = username
self.password = password
}
public func assignRole(role: UserRole) {
self.role = role
}
}
public class UserRole: Entity {
public var id: Int?
var name: String
var description: String
public init(name: String, description: String) {
self.name = name
self.description = description
}
}
So now I can do:
let newUser = User(username: "NotMyRealName", password: "SomeKindOfPassword")
let adminRole = UserRole(name: "admin", description: "Administrator of the website")
adminRole.id = 1
newUser.assignRole(adminRole)
print(serialize(newUser))
Which will print: ["password": "SomeKindOfPassword", "role": "1", "username": "NotMyRealName"]
That's all well and good, but now I want to reverse this process.
I'd like to write a function that could use like so:
let user = unserialize(["username":"Me", "password":"secret"]) as? User
Is that possible you think? I understand it might be a bit of a hack if I'd try to bypass the init method, but I'd like to try.
What is the right (cleanest and most concise) way to have PetOwner that can at any later point in program create new instances of Cat?
Let's assume that createAnotherAnimal can be called by PetOwner itself after it gets response to some async request, therefore creating as many instances of Cat as needed at the time of creating PetOwner is not possible.
I solved the problem with injecting factory, but I am not convinced that it is the best way to tackle the problem, what are the alternatives in Swinject?
protocol AnimalType {
var name: String? { get set }
func sound() -> String
}
class Cat: AnimalType {
var name: String?
init(name: String?) {
self.name = name
}
func sound() -> String {
return "Meow!"
}
}
protocol PersonType {
func play() -> String
func createAnotherAnimal() -> Void
}
class PetOwner: PersonType {
var pets: [AnimalType] = []
let petFactory : AnimalFactory
init(petFactory : AnimalFactory) {
self.petFactory = petFactory
}
func createAnotherAnimal() {
let pet = petFactory.factoryMethod()
self.pets.append(pet)
}
func play() -> String {
if(pets.count>0) {
let pet : AnimalType = pets[0];
let name = pet.name ?? "someone"
return "I'm playing with \(name). \(pet.sound())"
} else {
return "No animals"
}
}
}
class AnimalFactory {
let factoryMethod : () -> AnimalType
init(factoryMethod: () -> AnimalType) {
self.factoryMethod = factoryMethod
}
}
// Create a container and register service and component pairs.
let container = Container()
container.register(AnimalType.self) { _ in Cat(name: "Mimi") }
container.register(PersonType.self) { r in PetOwner(petFactory: r.resolve(AnimalFactory.self)!) }
container.register(AnimalFactory.self){r in AnimalFactory(factoryMethod:{ () -> AnimalType in r.resolve(AnimalType.self)!}) }
// The person is resolved to a PetOwner with a Cat.
let person = container.resolve(PersonType.self)!
person.createAnotherAnimal()
print(person.play())