What does a `Model` class look like that has a relation? - swift

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.

Related

How to save a model with a specific id in Vapor 3

I'm trying to add some seed data to a table in a migration. For this table I don't want the id value to be generated automatically, but I want to set it manually for each record. How can this be done? My current code looks like the bellow, but the record isn't inserted to the database and no error is thrown when the migration runs.
Model class:
import Foundation
import FluentPostgreSQL
import Vapor
final class PropertyType: PostgreSQLModel {
var id: Int?
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
extension PropertyType: Migration { }
extension PropertyType: Content { }
extension PropertyType: Parameter { }
Migration class:
import FluentPostgreSQL
import Vapor
struct AddPropertyTypes: Migration {
typealias Database = PostgreSQLDatabase
static func prepare(on conn: PostgreSQLConnection) -> Future<Void> {
let propertyType1 = PropertyType(id: 1, name: "Einfamilienhaus")
return propertyType.save(on: conn).transform(to: ())
}
static func revert(on conn: PostgreSQLConnection) -> Future<Void> {
let futures = [1].map { id in
return CodePropertyType.query(on: conn).filter(\CodePropertyType.id == id)
.delete()
}
return futures.flatten(on: conn)
}
}
Just replace
propertyType.save(on: conn)
with
propertyType.create(on: conn)

Vapor 2, One to Many relation

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
}
]
}
}

Using Object Mapping with Kinvey

I have an array of objects I'm trying to get out of one of my collections. I've followed along using their docs and also some Googling and I believe I'm close to the solution, however not close enough. Here's what I have:
class Clothing: Entity {
var categories: [Category]!
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "categories"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- map["clothing"]
gender <- map["gender"]
}
}
class Category: NSObject, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- map["category"]
image <- map["image"]
}
}
I'm able to get the right gender, but the array of categories doesn't seem to get mapped to the Category object. Any thoughts?
your model actually have one issue, as you can see at https://devcenter.kinvey.com/ios/guides/datastore#Model you should use let categories = List<Category>() instead of var categories: [Category]!. Here's the model that and test and worked:
import Kinvey
class Clothing: Entity {
let categories = List<Category>()
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "clothing"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- ("categories", map["categories"])
gender <- ("gender", map["gender"])
}
}
class Category: Object, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- ("category", map["category"])
image <- ("image", map["image"])
}
}
and here's a sample code how to save a new Clothing object
let casualCategory = Category()
casualCategory.title = "Casual"
let shirtCategory = Category()
shirtCategory.title = "Shirt"
let clothing = Clothing()
clothing.gender = "male"
clothing.categories.append(shirtCategory)
clothing.categories.append(casualCategory)
dataStore.save(clothing) { (result: Result<Clothing, Swift.Error>) in
switch result {
case .success(let clothing):
print(clothing)
case .failure(let error):
print(error)
}
}

How to make a Nested Json Model in Vapor Swift Server , Below are the Code for the reference..?

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()

I found a generic way to turn a class into a dictionary, is there a generic way to reverse that process?

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.