Model   F
last analyzed

Complexity

Total Complexity 103

Size/Duplication

Total Lines 955
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 306
dl 0
loc 955
rs 2
c 3
b 0
f 0
wmc 103

52 Methods

Rating   Name   Duplication   Size   Complexity  
A getSingleRecordCond() 0 12 1
A getListRecordCond() 0 4 1
A __construct() 0 17 2
A getSingleRecord() 0 2 1
A getListRecord() 0 15 3
A deleteCond() 0 8 1
A updateAllRecord() 0 6 1
A countCond() 0 7 1
A updateMultiple() 0 10 2
A asObject() 0 3 1
A getRelationshipOptions() 0 12 2
A insert() 0 9 2
A updateCond() 0 17 6
A setWhereValues() 0 29 6
A trigger() 0 12 5
A truncate() 0 4 1
A checkForSoftDelete() 0 8 3
A with() 0 6 2
A insertMultiple() 0 6 2
A relateOneToManyAndManyToOne() 0 19 5
A setWhereValuesArray() 0 16 4
A update() 0 10 2
A isSkipRulesValidation() 0 2 1
A relateOneToMany() 0 11 2
A delete() 0 7 1
A setSkipRulesValidation() 0 3 1
A getTable() 0 2 1
A getPrimaryKey() 0 2 1
A recordWithDeleted() 0 3 1
A onlyRecordDeleted() 0 3 1
A countAllRecord() 0 5 1
A asArray() 0 3 1
A validateData() 0 5 2
A deleteListRecord() 0 7 1
A validateRules() 0 10 4
A deleteRecords() 0 8 2
A relateManyToOne() 0 11 2
A dropdown() 0 19 3
A relate() 0 7 2
A cached() 0 3 1
A getReturnType() 0 6 2
A removeProtectedTableColumns() 0 7 3
A setDb() 0 6 2
A createdAt() 0 3 1
A limit() 0 3 1
A updatedAt() 0 3 1
A orderBy() 0 9 3
A unserialize() 0 9 3
A getDb() 0 2 1
A getQueryBuilder() 0 2 1
A serialize() 0 5 2
A setQueryBuilder() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Model often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Model, and based on these observations, apply Extract Interface, too.

1
<?php
2
    defined('ROOT_PATH') || exit('Access denied');
3
    /**
4
     * TNH Framework
5
     *
6
     * A simple PHP framework using HMVC architecture
7
     *
8
     * This content is released under the MIT License (MIT)
9
     *
10
     * Copyright (c) 2017 TNH Framework
11
     *
12
     * Permission is hereby granted, free of charge, to any person obtaining a copy
13
     * of this software and associated documentation files (the "Software"), to deal
14
     * in the Software without restriction, including without limitation the rights
15
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
     * copies of the Software, and to permit persons to whom the Software is
17
     * furnished to do so, subject to the following conditions:
18
     *
19
     * The above copyright notice and this permission notice shall be included in all
20
     * copies or substantial portions of the Software.
21
     *
22
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
     * SOFTWARE.
29
     */
30
31
    class Model {
32
33
       /**
34
         * This model's default database table. 
35
         * @var string the name of table
36
         */
37
        protected $table = '';
38
39
        /**
40
         * The database connection object. Will be set to the default
41
         * connection. This allows individual models to use different DBs
42
         * without overwriting the global database connection.
43
         */
44
        protected $db = null;
45
46
        /**
47
         * This model's default primary key or unique identifier.
48
         * Used by the getSingleRecord(), update() and delete() functions.
49
         */
50
        protected $primaryKey = 'id';
51
52
        /**
53
         * Support for soft deletes and this model's 'deleted' key
54
         * Whether soft delete is enabled or not
55
         * @var boolean
56
         */
57
        protected $softDeleteStatus = false;
58
59
        /**
60
         * Soft delete table column name to use
61
         * @var string
62
         */
63
        protected $softDeleteTableColumn = 'is_deleted';
64
65
        /**
66
         * Whether to return the records with the deleted
67
         * @var boolean
68
         */
69
        protected $returnRecordWithDeleted = false;
70
71
        /**
72
         * Whether to return only the deleted records
73
         * @var boolean
74
         */
75
        protected $returnOnlyRecordDeleted = false;
76
77
        /**
78
         * The various callbacks available to the model. Each are
79
         * simple lists of method names (methods will be call on $this->xxx).
80
         */
81
        protected $beforeCreateCallbacks = array();
82
        protected $afterCreateCallbacks = array();
83
        protected $beforeUpdateCallbacks = array();
84
        protected $afterUpdateCallbacks = array();
85
        protected $beforeGetCallbacks = array();
86
        protected $afterGetCallbacks = array();
87
        protected $beforeDeleteCallbacks = array();
88
        protected $afterDeleteCallbacks = array();
89
90
        /**
91
         * List of methods parameters to use in model callbacks
92
         * @var array
93
         */
94
        protected $callbackParameters = array();
95
96
        /**
97
         * Protected, non-modifiable tables columns
98
         * @var array the list of table attributes that can not be inserted 
99
         * or updated
100
         */
101
        protected $protectedTableColumns = array();
102
103
        /**
104
         * Relationship arrays. Use flat strings for defaults or 
105
         * string => array to customise the class name and primary key
106
         *
107
         * @example 
108
         *  For flat string:
109
         *  $manyToOne = array('relation_name')
110
         *
111
         *  For array:
112
         * $manyToOne = array('relation_name' => array(
113
         *  'primary_key' => 'pk_value',
114
         *  'modek' => 'model_name'
115
         * ))
116
         */
117
        protected $manyToOne = array();
118
        protected $oneToMany = array();
119
120
        /**
121
         * List of relation to return with the fetched record
122
         * @var array
123
         */
124
        protected $withs = array();
125
126
        /**
127
         * An array of validation rules. This needs to be the same format
128
         * as validation rules passed to the FormValidation::setRules library.
129
         */
130
        protected $validationRules = array();
131
132
        /**
133
         * Optionally skip the rules validation. Used in conjunction with
134
         * setSkipRulesValidation() to skip data validation for any future calls.
135
         */
136
        protected $skipRulesValidation = false;
137
138
        /**
139
         * By default we return our results as objects. If we need to override
140
         * this, we can, or, we could use the `asArray()` and `asObject()` scopes.
141
         */
142
        protected $returnRecordType = 'object';
143
144
        /**
145
         * Set current return type array or object
146
         * @var string
147
         */
148
        protected $temporaryReturnRecordType = null;
149
    	
150
    	
151
        /**
152
         * The database cache time to live
153
         * The value is in second 
154
         * @example $dbCacheTimeToLive = 300; //so means 5 minutes
155
         *  for the cache to live
156
         *
157
         * @var integer 
158
         */
159
        protected $dbCacheTimeToLive = 0;
160
161
        /**
162
         * Initialization of the model.
163
         *
164
         * @param object $db the Database instance to use
165
         * NOTE: each model need use different database instance 
166
         * for cache feature to work, so need use "clone" instead of use the global database 
167
         * instance directly.
168
         */
169
        public function __construct(Database $db = null) {
170
            //Note: don't use the property direct access here as 
171
            //some update is done in the method
172
            //$this->setDb()
173
            if ($db !== null) {
174
                $this->setDb($db);
175
            } else {
176
                 /**
177
                 * NOTE: Need use "clone" because some Model need have the personal instance of the database library
178
                 * to prevent duplication
179
                 */
180
                 $obj = & get_instance();
181
                 $this->setDb(clone $obj->database);
182
            }
183
            array_unshift($this->beforeCreateCallbacks, 'removeProtectedTableColumns');
184
            array_unshift($this->beforeUpdateCallbacks, 'removeProtectedTableColumns');
185
            $this->temporaryReturnRecordType = $this->returnRecordType;
186
        }
187
188
        /**
189
         * Fetch a single record based on the primary key. Returns an object.
190
         *
191
         * @param mixed $pk the primary keys to get record for
192
         *
193
         * @return array|object
194
         */
195
        public function getSingleRecord($pk) {
196
            return $this->getSingleRecordCond($this->primaryKey, $pk);
197
        }
198
199
200
        /**
201
         * Fetch a single record based on an arbitrary WHERE call. Can be
202
         * any valid value to DatabaseQueryBuilder->where().
203
         *
204
         * @return array|object
205
         */
206
        public function getSingleRecordCond() {
207
            $where = func_get_args();
208
            $this->checkForSoftDelete();
209
            $this->setWhereValues($where);
210
            $this->trigger('beforeGetCallbacks');
211
            $type = $this->getReturnType();
212
            $this->getQueryBuilder()->from($this->table);
213
            $row = $this->db->get($type);
214
            $row = $this->trigger('afterGetCallbacks', $row);
215
            $this->temporaryReturnRecordType = $this->returnRecordType;
216
            $this->withs = array();
217
            return $row;
218
        }
219
220
        /**
221
         * Fetch all the records in the table. Can be used as a generic call
222
         * to $this->db->get() with scoped methods.
223
         *
224
         * @param array $pks the list of primary keys if not empty to get record
225
         *
226
         * @return array|object
227
         */
228
        public function getListRecord(array $pks = array()) {
229
            $this->trigger('beforeGetCallbacks');
230
            $this->checkForSoftDelete();
231
            if (!empty($pks)) {
232
                $this->getQueryBuilder()->in($this->primaryKey, $pks);
233
            }
234
            $type = $this->getReturnType();
235
            $this->getQueryBuilder()->from($this->table);
236
            $result = $this->db->getAll($type);
237
            foreach ($result as $key => &$row) {
238
                $row = $this->trigger('afterGetCallbacks', $row, ($key == count($result) - 1));
239
            }
240
            $this->temporaryReturnRecordType = $this->returnRecordType;
241
            $this->withs = array();
242
            return $result;
243
        }
244
245
        /**
246
         * Fetch an array of records based on an arbitrary WHERE call.
247
         *
248
         * @return array|object
249
         */
250
        public function getListRecordCond() {
251
            $where = func_get_args();
252
            $this->setWhereValues($where);
253
            return $this->getListRecord();
254
        }
255
256
        /**
257
         * Insert a new row into the table. $data should be an associative array
258
         * of data to be inserted. Returns newly created ID.
259
         *
260
         * @param array $data the data to be inserted
261
         * @param boolean $skipRulesValidation whether to skip rules validation or not
262
         * @param boolean $escape whether to escape all data values
263
         *
264
         * @return mixed the insert id if the table have auto increment or sequence id field
265
         */
266
        public function insert(array $data = array(), $skipRulesValidation = false, $escape = true) {
267
            if ($this->validateData($data, $skipRulesValidation) !== false) {
268
                $data = $this->trigger('beforeCreateCallbacks', $data);
269
                $this->getQueryBuilder()->from($this->table);
270
                $insertId = $this->db->insert($data, $escape);
271
                $this->trigger('afterCreateCallbacks', $insertId);
272
                return $insertId;
273
            } 
274
            return false;
275
        }
276
277
        /**
278
         * Insert multiple rows into the table.
279
         *
280
         * @param array $data the data to be inserted
281
         * @param boolean $skipRulesValidation whether to skip rules validation or not
282
         * @param boolean $escape whether to escape all data values
283
         *
284
         * @return mixed the array of insert id if the table have auto increment or sequence id field
285
         */
286
        public function insertMultiple(array $data = array(), $skipRulesValidation = false, $escape = true) {
287
            $ids = array();
288
            foreach ($data as $key => $row) {
289
                $ids[] = $this->insert($row, $skipRulesValidation, $escape);
290
            }
291
            return $ids;
292
        }
293
294
        /**
295
         * Updated a record based on the primary key value.
296
         *
297
         * @param mixed $pk the value of primary key to use to do update
298
         * @param array $data the data to be inserted
299
         * @param boolean $skipRulesValidation whether to skip rules validation or not
300
         * @param boolean $escape whether to escape all data values
301
         *
302
         * @return boolean the status of the operation
303
         */
304
        public function update($pk, array $data = array(), $skipRulesValidation = false, $escape = true) {
305
            $data = $this->trigger('beforeUpdateCallbacks', $data);
306
            if ($this->validateData($data, $skipRulesValidation) !== false) {
307
                $this->getQueryBuilder()->where($this->primaryKey, $pk)
308
                                        ->from($this->table);
309
                $result = $this->db->update($data, $escape);
310
                $this->trigger('afterUpdateCallbacks', array($data, $result));
311
                return $result;
312
            } 
313
            return false;
314
        }
315
316
        /**
317
         * Update many records, based on an array of primary keys values.
318
         * 
319
         * @param array $pks the array value of primary keys to do update
320
         * @param array $data the data to be inserted
321
         * @param boolean $skipRulesValidation whether to skip rules validation or not
322
         * @param boolean $escape whether to escape all data values
323
         *
324
         * @return boolean the status of the operation
325
         */
326
        public function updateMultiple($pks, array $data = array(), $skipRulesValidation = false, $escape = true) {
327
            $data = $this->trigger('beforeUpdateCallbacks', $data);
328
            if ($this->validateData($data, $skipRulesValidation) !== false) {
329
                $this->getQueryBuilder()->in($this->primaryKey, $pks)
330
                                        ->from($this->table);
331
                $result = $this->db->update($data, $escape);
332
                $this->trigger('afterUpdateCallbacks', array($data, $result));
333
                return $result;
334
            }
335
            return false;
336
        }
337
338
        /**
339
         * Updated a record based on an arbitrary WHERE clause.
340
         *
341
         * @return boolean the status of the operation
342
         */
343
        public function updateCond() {
344
            $args = func_get_args();
345
            $data = array();
346
            if (count($args) == 2 && is_array($args[1])) {
347
                $data = array_pop($args);
348
            } else if (count($args) == 3 && is_array($args[2])) {
349
                $data = array_pop($args);
350
            }
351
            $data = $this->trigger('beforeUpdateCallbacks', $data);
352
            if ($this->validateRules($data) !== false) {
0 ignored issues
show
introduced by
The condition $this->validateRules($data) !== false is always true.
Loading history...
353
                $this->setWhereValues($args);
354
                $this->getQueryBuilder()->from($this->table);
355
                $result = $this->db->update($data);
356
                $this->trigger('afterUpdateCallbacks', array($data, $result));
357
                return $result;
358
            }
359
            return false;
360
        }
361
362
        /**
363
         * Update all records in the database without conditions
364
         * 
365
         * @param array $data the data to be inserted
366
         * @param boolean $escape whether to escape all data values
367
         *
368
         * @return boolean the status of the operation
369
         */
370
        public function updateAllRecord(array $data = array(), $escape = true) {
371
            $data = $this->trigger('beforeUpdateCallbacks', $data);
372
            $this->getQueryBuilder()->from($this->table);
373
            $result = $this->db->update($data, $escape);
374
            $this->trigger('afterUpdateCallbacks', array($data, $result));
375
            return $result;
376
        }
377
378
        /**
379
         * Delete a row from the table by the primary value
380
         * @param array $id the value of primary key to do delete
381
         * 
382
         * @return boolean the status of the operation
383
         */
384
        public function delete($id) {
385
            $this->trigger('beforeDeleteCallbacks', $id);
386
            $this->getQueryBuilder()->where($this->primaryKey, $id)
387
                                    ->from($this->table);  
388
            $result = $this->deleteRecords();
389
            $this->trigger('afterDeleteCallbacks', $result);
390
            return $result;
391
        }
392
393
        /**
394
         * Delete a row from the database table by an arbitrary WHERE clause
395
         * 
396
         * @return boolean the status of the operation
397
         */
398
        public function deleteCond() {
399
            $where = func_get_args();
400
            $where = $this->trigger('beforeDeleteCallbacks', $where);
401
            $this->setWhereValues($where);
402
            $this->getQueryBuilder()->from($this->table);  
403
            $result = $this->deleteRecords();
404
            $this->trigger('afterDeleteCallbacks', $result);
405
            return $result;
406
        }
407
408
        /**
409
         * Delete many rows from the database table by multiple primary values
410
         * 
411
         * @param array $pks the array value of primary keys to do delete
412
         *
413
         * @return boolean the status of the operation
414
         */
415
        public function deleteListRecord(array $pks) {
416
            $pks = $this->trigger('beforeDeleteCallbacks', $pks);
417
            $this->getQueryBuilder()->in($this->primaryKey, $pks)
418
                                    ->from($this->table);  
419
            $result = $this->deleteRecords();
420
            $this->trigger('afterDeleteCallbacks', $result);
421
            return $result;
422
        }
423
424
        /**
425
         * Truncates the table
426
         *
427
         * @return boolean the truncate status
428
         */
429
        public function truncate() {
430
            $this->getQueryBuilder()->from($this->table); 
431
            $result = $this->db->delete();
432
            return $result;
433
        }
434
435
        /**
436
         * Return the record with the relation
437
         * @param  string $relationship the name of relation to fetch record
438
         * @return object               the current instance
439
         */
440
        public function with($relationship) {
441
            $this->withs[] = $relationship;
442
            if (!in_array('relate', $this->afterGetCallbacks)) {
443
                $this->afterGetCallbacks[] = 'relate';
444
            }
445
            return $this;
446
        }
447
		
448
        /**
449
         * Relationship
450
         * @param array|object $row the row to add relation data into
451
         *
452
         * @return array|object the final row after add relation data
453
         */
454
        protected function relate($row) {
455
            if (empty($row)) {
456
                return $row;
457
            }
458
            $row = $this->relateManyToOne($row);
459
            $row = $this->relateOneToMany($row);
460
            return $row;
461
        }
462
463
        /**
464
         * Retrieve and generate a data to use directly in Form::select()
465
         *
466
         * @return array
467
         */
468
        public function dropdown() {
469
            $args = func_get_args();
470
            if (count($args) == 2) {
471
                list($key, $value) = $args;
472
            } else {
473
                $key = $this->primaryKey;
474
                $value = $args[0];
475
            }
476
            $this->trigger('before_dropdown', array($key, $value));
477
            $this->checkForSoftDelete();
478
            $this->getQueryBuilder()->select(array($key, $value))
479
                                    ->from($this->table);
480
            $result = $this->db->getAll();
481
            $options = array();
482
            foreach ($result as $row) {
483
                $options[$row->{$key}] = $row->{$value};
484
            }
485
            $options = $this->trigger('after_dropdown', $options);
486
            return $options;
487
        }
488
489
        /**
490
         * Fetch a total count of rows, disregarding any previous conditions
491
         * 
492
         * @return integer the number of rows
493
         */
494
        public function countAllRecord() {
495
            $this->checkForSoftDelete();
496
            $this->getQueryBuilder()->from($this->table);
497
            $this->db->getAll();
498
            return $this->db->numRows();
499
        }
500
        
501
        /**
502
         * Fetch a count of rows based on an arbitrary WHERE call.
503
         *
504
         * @return integer the number of rows
505
         */
506
        public function countCond() {
507
            $where = func_get_args();
508
            $this->checkForSoftDelete();
509
            $this->setWhereValues($where);
510
            $this->getQueryBuilder()->from($this->table);
511
            $this->db->getAll();
512
            return $this->db->numRows();
513
        }
514
        
515
        /**
516
         * Enabled cache temporary. This method is the shortcut to Database::cached
517
         *
518
         * @param integer $ttl the cache default time to live
519
         *
520
         * @return object the current instance
521
         */
522
        public function cached($ttl = 0) {
523
            $this->db = $this->db->cached($ttl);
524
            return $this;
525
        }
526
527
        /**
528
         * Tell the class to skip the data validation
529
         * @param boolean $status the status of rules validation
530
         *
531
         * @return object the current instance
532
         */
533
        public function setSkipRulesValidation($status = true) {
534
            $this->skipRulesValidation = $status;
535
            return $this;
536
        }
537
538
        /**
539
         * Get the skip validation status
540
         *
541
         * @return boolean
542
         */
543
        public function isSkipRulesValidation() {
544
            return $this->skipRulesValidation;
545
        }
546
547
        /**
548
         * Getter for the table name
549
         *
550
         * @return string the name of table
551
         */
552
        public function getTable() {
553
            return $this->table;
554
        }
555
556
        /**
557
         * Getter for the primary key name
558
         *
559
         * @return string the name of primary key
560
         */
561
        public function getPrimaryKey() {
562
            return $this->primaryKey;
563
        }
564
565
        /**
566
         * Return the next call as an array rather than an object
567
         */
568
        public function asArray() {
569
            $this->temporaryReturnRecordType = 'array';
570
            return $this;
571
        }
572
573
        /**
574
         * Return the next call as an object rather than an array
575
         */
576
        public function asObject() {
577
            $this->temporaryReturnRecordType = 'object';
578
            return $this;
579
        }
580
581
        /**
582
         * Don't care about soft deleted rows on the next call
583
         *
584
         * @return object the current instance
585
         */
586
        public function recordWithDeleted() {
587
            $this->returnRecordWithDeleted = true;
588
            return $this;
589
        }
590
591
        /**
592
         * Only get deleted rows on the next call
593
         * 
594
         * @return object the current instance
595
        */
596
        public function onlyRecordDeleted() {
597
            $this->returnOnlyRecordDeleted = true;
598
            return $this;
599
        }
600
        
601
        /**
602
         * Return the database instance
603
         * @return Database the database instance
604
         */
605
        public function getDb() {
606
            return $this->db;
607
        }
608
609
        /**
610
         * Set the Database instance for future use
611
         * @param Database $db the database object
612
         */
613
        public function setDb(Database $db) {
614
            $this->db = $db;
615
            if ($this->dbCacheTimeToLive > 0) {
616
                $this->db->setCacheTimeToLive($this->dbCacheTimeToLive);
617
            }
618
            return $this;
619
        }
620
621
        /**
622
         * Return the queryBuilder instance this is the shortcut to database queryBuilder
623
         * @return object the DatabaseQueryBuilder instance
624
         */
625
        public function getQueryBuilder() {
626
            return $this->db->getQueryBuilder();
627
        }
628
629
        /**
630
         * Set the DatabaseQueryBuilder instance for future use
631
         * @param object $queryBuilder the DatabaseQueryBuilder object
632
         * @return object
633
         */
634
        public function setQueryBuilder(DatabaseQueryBuilder $queryBuilder) {
635
            $this->db->setQueryBuilder($queryBuilder);
636
            return $this;
637
        }
638
639
        /* --------------------------------------------------------------
640
         * QUERY BUILDER DIRECT ACCESS METHODS
641
         * ------------------------------------------------------------ */
642
643
        /**
644
         * A wrapper to $this->getQueryBuilder()->orderBy()
645
         *
646
         * @see  DatabaseQueryBuilder::orderBy
647
         */
648
        public function orderBy($field, $order = 'ASC') {
649
            if (is_array($field)) {
650
                foreach ($field as $key => $value) {
651
                    $this->getQueryBuilder()->orderBy($key, $value);
652
                }
653
            } else {
654
                $this->getQueryBuilder()->orderBy($field, $order);
655
            }
656
            return $this;
657
        }
658
659
        /**
660
         * A wrapper to $this->getQueryBuilder()->limit()
661
         * 
662
         * @see  DatabaseQueryBuilder::limit
663
         */
664
        public function limit($offset = 0, $limit = 10) {
665
            $this->getQueryBuilder()->limit($offset, $limit);
666
            return $this;
667
        }
668
669
        /**
670
         * Table DATETIME field created_at
671
         *
672
         * @param array $row the data to be inserted
673
         *
674
         * @return array the data after add field for created time
675
         */
676
        protected function createdAt($row) {
677
            $row['created_at'] = date('Y-m-d H:i:s');
678
            return $row;
679
        }
680
681
        /**
682
         * Table DATETIME field  updated_at
683
         *
684
         * @param array $row the data to be updated
685
         *
686
         * @return array the data after add field for updated time
687
         */
688
        protected function updatedAt($row) {
689
           $row['updated_at'] = date('Y-m-d H:i:s');
690
           return $row;
691
        }
692
693
        /**
694
         * Serialises data for you automatically, allowing you to pass
695
         * through objects and let it handle the serialisation in the background
696
         *
697
         * @param array|object $row the data to be serialized
698
         * 
699
         * @return array|object the data after processing
700
         */
701
        protected function serialize($row) {
702
            foreach ($this->callbackParameters as $column) {
703
                $row[$column] = serialize($row[$column]);
704
            }
705
            return $row;
706
        }
707
708
        /**
709
         * Unserialises data for you automatically, allowing you to pass
710
         * through objects and let it handle the serialisation in the background
711
         *
712
         * @param array|object $row the data to be unserialized
713
         * 
714
         * @return array|object the data after processing
715
         */
716
        protected function unserialize($row) {
717
            foreach ($this->callbackParameters as $column) {
718
                if (is_array($row)) {
719
                    $row[$column] = unserialize($row[$column]);
720
                } else {
721
                    $row->$column = unserialize($row->$column);
722
                }
723
            }
724
            return $row;
725
        }
726
727
        /**
728
         * Protect attributes by removing them from data to insert or update
729
         *
730
         * @return mixed the final row after remove the protected
731
         * table columns if they exist
732
         */
733
        protected function removeProtectedTableColumns($row) {
734
            foreach ($this->protectedTableColumns as $attr) {
735
               if (isset($row[$attr])) {
736
                    unset($row[$attr]);
737
                }
738
            }
739
            return $row;
740
        }
741
742
        /**
743
         * Delete record in tha database
744
         * 
745
         * @return boolean the delete status
746
         */
747
        protected function deleteRecords() {
748
            $result = false;
749
            if ($this->softDeleteStatus) {
750
                $result = $this->db->update(array($this->softDeleteTableColumn => 1));
751
            } else {
752
                $result = $this->db->delete();
753
            }
754
            return $result;
755
        }
756
757
        /**
758
         * Validate the data using the validation rules
759
         * 
760
         * @param  array $data the data to validate before insert, update, etc.
761
         * @param boolean $skipValidation whether to skip validation or not
762
         * 
763
         * @return array|boolean
764
         */
765
        protected function validateData($data, $skipValidation) {
766
            if ($skipValidation === false) {
767
                $data = $this->validateRules($data);
768
            }
769
            return $data;
770
        }
771
772
        /**
773
         * Run validation on the passed data
774
         * @param  array $data the data to validate before insert, update, etc.
775
         * 
776
         * @return array|boolean 
777
         */
778
        protected function validateRules(array $data) {
779
            if ($this->isSkipRulesValidation() || empty($this->validationRules)) {
780
                return $data;
781
            }
782
            get_instance()->formvalidation->setData($data);
783
            get_instance()->formvalidation->setRules($this->validationRules);
784
            if (get_instance()->formvalidation->validate() === true) {
785
                return $data;
786
            }
787
            return false;
788
        }
789
790
         /**
791
         * Get the record return type array or object
792
         * 
793
         * @return string|boolean
794
         */
795
        protected function getReturnType(){
796
            $type = false;
797
            if ($this->temporaryReturnRecordType == 'array') {
798
               $type = 'array';
799
            }
800
            return $type;
801
        }
802
803
         /**
804
         * Check if soft delete is enable setting the condition
805
         * @return object the current instance 
806
         */
807
        protected function checkForSoftDelete(){
808
            if ($this->softDeleteStatus && $this->returnRecordWithDeleted !== true) {
809
                $this->getQueryBuilder()->where(
810
                                                $this->softDeleteTableColumn, 
811
                                                (int) $this->returnOnlyRecordDeleted
812
                                            );
813
            }
814
            return $this;
815
        }
816
817
         /**
818
         * Relate for "manyToOne" and "oneToMany"
819
         * 
820
         * @param  string $relationship the name of relation
821
         * @param  string|array $options      the model and primary key values
822
         * @param  object|array $row          the row to update
823
         * @param  string $type the type can be "manyToOne", "oneToMany"
824
         * 
825
         * @return array|object the final row values
826
         */
827
        protected function relateOneToManyAndManyToOne($relationship, $options, $row, $type){
828
            if (in_array($relationship, $this->withs)) {
829
                get_instance()->loader->model($options['model'], $relationship . '_model');
830
                $model = get_instance()->{$relationship . '_model'};
831
                if($type == 'manyToOne'){
832
                    if (is_object($row)) {
833
                        $row->{$relationship} = $model->getSingleRecord($row->{$options['primary_key']});
834
                    } else {
835
                        $row[$relationship] = $model->getSingleRecord($row[$options['primary_key']]);
836
                    }
837
                } else {
838
                    if (is_object($row)) {
839
                        $row->{$relationship} = $model->getListRecordCond($options['primary_key'], $row->{$this->primaryKey});
840
                    } else {
841
                        $row[$relationship] = $model->getListRecordCond($options['primary_key'], $row[$this->primaryKey]);
842
                    }
843
                }
844
            }
845
            return $row;
846
        }
847
848
        /**
849
         * Relate for the relation "manyToOne"
850
         * @see Model::relateOneToManyAndManyToOne
851
         */
852
        protected function relateManyToOne($row) {
853
            foreach ($this->manyToOne as $key => $value) {
854
                $options = $this->getRelationshipOptions($key, $value);
855
                $row = $this->relateOneToManyAndManyToOne(
856
                                                            $options['relationship'], 
857
                                                            $options, 
858
                                                            $row, 
859
                                                            'manyToOne'
860
                                                        );
861
            }
862
            return $row;
863
        }
864
865
        /**
866
         * Relate for the relation "oneToMany"
867
         * @see Model::relateOneToManyAndManyToOne
868
         */
869
        protected function relateOneToMany($row) {
870
            foreach ($this->oneToMany as $key => $value) {
871
                $options = $this->getRelationshipOptions($key, $value);
872
                $row = $this->relateOneToManyAndManyToOne(
873
                                                            $options['relationship'], 
874
                                                            $options, 
875
                                                            $row, 
876
                                                            'oneToMany'
877
                                                        );
878
            }
879
            return $row;
880
        }
881
882
        /**
883
         * Get the relationship options to use 
884
         * @param  mixed $key   the relationship key
885
         * @param  mixed $value the raltionship value for custom option
886
         * @return array the options
887
         */
888
        protected function getRelationshipOptions($key, $value) {
889
            $relationship = null;
890
            $options = null;
891
            if (is_string($value)) {
892
                $relationship = $value;
893
                $options = array('primary_key' => $this->table . '_id', 'model' => $value . '_model');
894
            } else {
895
                $relationship = $key;
896
                $options = $value;
897
            }
898
            $options['relationship'] = $relationship;
899
            return $options;
900
        }
901
		
902
        /**
903
         * Trigger an event and call its observers. Pass through the event name
904
         * (which looks for an instance variable $this->event_name), an array of
905
         * parameters to pass through and an optional 'last in interation' boolean
906
         *
907
         * @param string $event the name of event like afterGetCallbacks
908
         * @param mixed $data the data to pass to the callback
909
         * @param boolean $last if is the last row of data to process
910
         *
911
         * @return mixed the data after each callbacks processed
912
         */
913
        protected function trigger($event, $data = false, $last = true) {
914
            if (isset($this->$event) && is_array($this->$event)) {
915
                foreach ($this->$event as $method) {
916
                    if (strpos($method, '(') !== false) {
917
                        preg_match('/([a-zA-Z0-9\_\-]+)(\(([a-zA-Z0-9\_\-\., ]+)\))?/', $method, $matches);
918
                        $method = $matches[1];
919
                        $this->callbackParameters = explode(',', $matches[3]);
920
                    }
921
                    $data = call_user_func_array(array($this, $method), array($data, $last));
922
                }
923
            }
924
            return $data;
925
        }
926
		
927
        /**
928
         * Set WHERE parameters, when is array
929
         * @param array $params
930
         *
931
         * @return object the current instance
932
         */
933
        protected function setWhereValuesArray(array $params) {
934
            foreach ($params as $field => $value) {
935
                if (is_array($value)) {
936
                    //Condition like xxxx->getListRecordCond(array('id' => array(1,3)));
937
                    $this->getQueryBuilder()->in($field, $value); // WHERE id IN (1, 3)
938
                } else {
939
                    if (is_int($field)) {
940
                        //Condition like xxxx->getListRecordCond(array('id'));
941
                        $this->getQueryBuilder()->where($value);  // WHERE id = ''
942
                    } else {
943
                        //Condition like xxxx->getListRecordCond(array('status' => 0));
944
                        $this->getQueryBuilder()->where($field, $value); // WHERE status = 0
945
                    }
946
                }
947
            }
948
            return $this;
949
        }
950
951
        /**
952
         * Set WHERE parameters, cleverly
953
         * @param mixed $params the parameters of where
954
         * 
955
         * @return object the current instance
956
         */
957
        protected function setWhereValues($params) {
958
            if (count($params) == 1) {
959
                //We have only one parameter
960
                if (is_array($params[0])) {
961
                    //The parameter is an array
962
                    $this->setWhereValuesArray($params[0]);
963
                } else {
964
                    //The parameter is not an array
965
                    //Condition like xxxx->getListRecordCond('status'); 
966
                    $this->getQueryBuilder()->where($params[0]); // WHERE status = ''
967
                }
968
            } else if (count($params) == 2) {
969
                //We have two parameters
970
                if (is_array($params[1])) {
971
                    //2nd param is an array
972
                    //Condition like xxxx->getListRecordCond('id', array(1,3,2));
973
                    $this->getQueryBuilder()->in($params[0], $params[1]); //WHERE id IN (1, 3, 2)
974
                } else {
975
                    //2nd param is not an array 
976
                    //Condition like xxxx->getListRecordCond('status', 1);
977
                    $this->getQueryBuilder()->where($params[0], $params[1]); //WHERE status = 1
978
                }
979
            } else if (count($params) == 3) {
980
                //We have three parameters
981
                //1st param is field
982
                //2nd param is operator
983
                //3rd param is the value
984
                //Condition like xxxx->getListRecordCond('id', '>', 3);
985
                $this->getQueryBuilder()->where($params[0], $params[1], $params[2]); //WHERE id > 3
986
            } 
987
        }
988
    }
989