priore

MySQL helper and base object definition

May 11th, 2018
63
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //
  2. //  ENMySQL.swift
  3. //
  4. import OHMySQL        // https://github.com/oleghnidets/OHMySQL
  5. import EVReflection   // https://github.com/evermeer/EVReflection
  6.  
  7. typealias sql = OHMySQLQueryRequestFactory
  8.  
  9. enum ENMySQLError: Int {
  10.     case insertWithoutValues = -12001
  11. }
  12.  
  13. class ENMySQL {
  14.    
  15.     private var coordinator: OHMySQLStoreCoordinator?
  16.     private var context: OHMySQLQueryContext?
  17.    
  18.     private var isTransaction: Bool = false
  19.    
  20.     init() {
  21.        
  22.         //          // mysql with certificates
  23.         //        let key = Bundle.main.path(forResource: "client-key", ofType: "pem")
  24.         //        let cert = Bundle.main.path(forResource: "client-cert", ofType: "pem")
  25.         //        let ca = Bundle.main.path(forResource: "ca-cert", ofType: "pem")
  26.         //
  27.         //        let config = OHSSLConfig(key: key!,
  28.         //                                 certPath: cert!,
  29.         //                                 certAuthPath: ca!,
  30.         //                                 certAuthPEMPath: nil,
  31.         //                                 cipher: nil)
  32.         //
  33.         //        let root = OHMySQLUser(userName: "username",
  34.         //                               password: "password",
  35.         //                               sslConfig: config!,
  36.         //                               serverName: "localhost",
  37.         //                               dbName: "db_name",
  38.         //                               port: 3306,
  39.         //                               socket: nil)
  40.        
  41.         let root = OHMySQLUser(userName: "username",
  42.                                password: "passwords",
  43.                                serverName: "localhost",
  44.                                dbName: "db_name",
  45.                                port: 3306,
  46.                                socket: nil)
  47.        
  48.         coordinator = OHMySQLStoreCoordinator(user: root!)
  49.         coordinator?.encoding = .UTF8MB4
  50.        
  51.         context = OHMySQLQueryContext()
  52.         context?.storeCoordinator = coordinator!
  53.        
  54.         coordinator?.connect()
  55.     }
  56.    
  57.     deinit {
  58.         coordinator?.disconnect()
  59.     }
  60.    
  61.     func disconnect() {
  62.         coordinator?.disconnect()
  63.     }
  64.    
  65.     func select(_ query: OHMySQLQueryRequest, _ completion: (_ elements: [[String: Any]]?, _ error: Error?) -> Void) {
  66.        
  67.         do {
  68.             let elements = try context?.executeQueryRequestAndFetchResult(query)
  69.             completion(elements, nil)
  70.         } catch {
  71.             completion(nil, error)
  72.         }
  73.        
  74.     }
  75.    
  76.     func select<T:ENBaseObject>(id: AnyObject, completion: (_ object: T?, _ error: Error?) -> Void) {
  77.        
  78.         do {
  79.             var object: T?
  80.            
  81.             let condition = "\(T().primaryKey()))=\(id)"
  82.             let query = sql.selectFirst(T.table, condition: condition)
  83.             if let elements = try context?.executeQueryRequestAndFetchResult(query), let dict = elements.first {
  84.                 object = T.init(dictionary: dict as NSDictionary)
  85.             }
  86.            
  87.             completion(object, nil)
  88.         } catch  {
  89.             completion(nil, error)
  90.         }
  91.        
  92.     }
  93.    
  94.     func select<T:ENBaseObject>(condition: String?, order: [String] = [], ascending: Bool = true, completion: (_ objects: [T]?, _ error: Error?) -> Void) {
  95.        
  96.         select(table: T.table, condition: condition, order: order, ascending: ascending) { (elements, error) in
  97.             var objects:[T] = []
  98.            
  99.             elements?.forEach { (dict) in
  100.                 let obj = T.init(dictionary: dict as NSDictionary)
  101.                 objects.append(obj)
  102.             }
  103.            
  104.             completion(objects, error)
  105.         }
  106.     }
  107.    
  108.     func select(table: String, condition: String?, order: [String] = [], ascending: Bool = true, completion: (_ result: [[String: Any]]?, _ error: Error?) -> Void) {
  109.         do {
  110.             var query = sql.select(table, condition: condition)
  111.             if order.count > 0 {
  112.                 query = sql.select(table, condition: condition, orderBy: order, ascending: ascending)
  113.             }
  114.             let result = try context?.executeQueryRequestAndFetchResult(query)
  115.             completion(result, nil)
  116.         } catch {
  117.             completion(nil, error)
  118.         }
  119.     }
  120.    
  121.     func select<T:ENBaseObject>(object: T, completion: (_ object: T?, _ error: Error?) -> Void) {
  122.        
  123.         do {
  124.             let id = object.value(forKey: object.primaryKey())
  125.             let condition = "\(object.primaryKey()))=\(id!)"
  126.             let query = sql.selectFirst(object.mySQLTable(), condition: condition)
  127.             if let elements = try context?.executeQueryRequestAndFetchResult(query), let dict = elements.first {
  128.                 let result = T.init(dictionary: dict as NSDictionary)
  129.                 completion(result, nil)
  130.             }
  131.            
  132.         } catch  {
  133.             completion(nil, error)
  134.         }
  135.     }
  136.    
  137.     func join(type: String,
  138.               table: String,
  139.               columns: [String],
  140.               on: [String: String],
  141.               completion: (_ result: [[String: Any]]?, _ error: Error?) -> Void) {
  142.         do {
  143.             let query = sql.joinType(type, fromTable: table, columnNames: columns, joinOn: on)
  144.             let result = try context?.executeQueryRequestAndFetchResult(query)
  145.             completion(result, nil)
  146.         } catch {
  147.             completion(nil, error)
  148.         }
  149.     }
  150.    
  151.     func insert<T:ENBaseObject>(_ object: T, _ completion: ((_ error: Error?) -> Void)? = nil) {
  152.        
  153.         do {
  154.             context?.insertObject(object)
  155.             if !isTransaction {
  156.                 try context?.save()
  157.             }
  158.             completion?(nil)
  159.         } catch {
  160.             completion?(error)
  161.         }
  162.     }
  163.    
  164.     func insert<T:ENBaseObject>(_ object: T, _ completion: ((_ id: NSNumber?, _ error: Error?) -> Void)) {
  165.        
  166.         if !isTransaction {
  167.             do {
  168.                 context?.insertObject(object)
  169.                 try context?.save()
  170.                 let result = context?.lastInsertID()
  171.                 completion(result, nil)
  172.             } catch {
  173.                 completion(nil, error)
  174.             }
  175.         }
  176.     }
  177.    
  178.     func delete<T:ENBaseObject>(_ object: T, _ completion: ((_ error: Error?) -> Void)? = nil)
  179.     {
  180.         do {
  181.             context?.deleteObject(object)
  182.             if !isTransaction {
  183.                 try context?.save()
  184.             }
  185.             completion?(nil)
  186.         } catch {
  187.             completion?(error)
  188.         }
  189.     }
  190.    
  191.     func update<T:ENBaseObject>(_ object: T, _ completion: ((_ error: Error?) -> Void)? = nil)
  192.     {
  193.         do {
  194.             context?.updateObject(object)
  195.             if !isTransaction {
  196.                 try context?.save()
  197.             }
  198.             completion?(nil)
  199.         } catch {
  200.             completion?(error)
  201.         }
  202.     }
  203.    
  204.     func transaction(_ block: (_ context: OHMySQLQueryContext?) -> Void, _ completion: ((_ error: Error?) -> Void)? = nil) {
  205.        
  206.         do {
  207.             isTransaction = true
  208.             block(context)
  209.             isTransaction = false
  210.            
  211.             try context?.save()
  212.            
  213.             completion?(nil)
  214.         } catch {
  215.             completion?(error)
  216.         }
  217.     }
  218.    
  219. }
  220.  
  221.  
  222. //
  223. //  ENBaseObject.swift
  224. //
  225. import OHMySQL        // https://github.com/oleghnidets/OHMySQL
  226. import EVReflection   // https://github.com/evermeer/EVReflection
  227.  
  228. class ENBaseObject: EVObject, OHMappingProtocol {
  229.    
  230.     static var table: String {
  231.         return "\(String(describing: self).dropFirst(2))"
  232.     }
  233.    
  234.     static var primaryKey: String {
  235.         return "id\(self.table)"
  236.     }
  237.    
  238.     convenience init(id: Any) {
  239.         self.init()
  240.        
  241.         self.setValue(id, forKey: primaryKey())
  242.     }
  243.    
  244.     // MARK: - EVObject
  245.    
  246.     override func initValidation(_ _dict: NSDictionary) {
  247.         // convert fields named "NNN.NNN" to "class-property-name.property-name"
  248.         // ex. "BookChapter.VerseNumber" to "verseNumber" property of "bookChapter" class property
  249.         // for a query type: SELECT VerseNumber AS BookChapter.VerseNumber FROM Chapters
  250.         if let dict = _dict as? Dictionary<String, AnyObject> {
  251.             // look for the keys that have a dot in the name
  252.             let keys = dict.keys.filter({ $0.contains(".") })
  253.             if keys.count > 0 {
  254.                 var counter: Int = 0
  255.                 keys.forEach { (key) in
  256.                    
  257.                     // takes the first part of the name that identifies a property
  258.                     let keypaths = key.split(separator: ".")
  259.                     if let first = keypaths.first {
  260.                         let propertyName = "\(first)".prefix(1).lowercased() + first.dropFirst()
  261.                        
  262.                         // if the property has never been initialized, get the property class type
  263.                         if self.value(forKey: propertyName) == nil,
  264.                             let type = self.typeForKey(propertyName),
  265.                             let className = "\(type)"
  266.                                 .split(separator: ".")
  267.                                 .last?
  268.                                 .replacingOccurrences(of: ">)", with: ""),
  269.                             let classType = NSClassFromString(className) as? EVObject.Type {
  270.                            
  271.                             // takes only the values for the property and normalizes the name of the keys
  272.                             let filtered = dict.filter({ $0.key.hasPrefix("\(propertyName).") })
  273.                             let partial = filtered.updateKeys({ (key) -> String in
  274.                                 return key.replacingOccurrences(of: "\(propertyName).", with: "")
  275.                             })
  276.                            
  277.                             // instance object
  278.                             let object = classType.init(dictionary: partial as NSDictionary)
  279.                             self.setValue(object, forKey: propertyName)
  280.                            
  281.                             // if has processed all keys, ends
  282.                             counter += partial.count
  283.                             if counter >= keys.count {
  284.                                 return
  285.                             }
  286.                         }
  287.                     }
  288.                 }
  289.             }
  290.         }
  291.     }
  292.    
  293.     // MARK: - OHMappingProtocol
  294.    
  295.     func mappingDictionary() -> [AnyHashable : Any]! {
  296.         return self.toDictionary() as? [AnyHashable: Any]
  297.     }
  298.    
  299.     func mySQLTable() -> String! {
  300.         return type(of: self).table
  301.     }
  302.    
  303.     func primaryKey() -> String! {
  304.         return type(of: self).primaryKey
  305.     }
  306.    
  307.     // MARK: - Database
  308.    
  309.     func select(_ completion: @escaping (_ object: ENBaseObject? , _ error: Error?) -> Void) {
  310.        
  311.         let db = ENMySQL()
  312.         db.select(object: self) { (object, error) in
  313.             completion(object, error)
  314.         }
  315.     }
  316.    
  317.     func insert(_ completion: ((_ error: Error?) -> Void)? = nil) {
  318.        
  319.         let db = ENMySQL()
  320.         db.insert(self) { (_, error) in
  321.             completion?(error)
  322.         }
  323.     }
  324.    
  325.     func delete(_ completion: ((_ error: Error?) -> Void)? = nil) {
  326.        
  327.         let db = ENMySQL()
  328.         db.delete(self, completion)
  329.     }
  330.    
  331.     func update(_ completion: ((_ error: Error?) -> Void)? = nil) {
  332.        
  333.         let db = ENMySQL()
  334.         db.update(self, completion)
  335.     }
  336. }
  337.  
  338. fileprivate extension Dictionary {
  339.     func updateKeys(_ transform: (Key) -> Key) -> Dictionary {
  340.         return Dictionary(uniqueKeysWithValues:
  341.             self.map { (transform($0), $1) })
  342.     }
  343. }
RAW Paste Data