Passed
Push — master ( 48a0b5...08c47d )
by Ronan
04:02
created

Model   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 459
Duplicated Lines 0 %

Test Coverage

Coverage 13.73%

Importance

Changes 20
Bugs 0 Features 0
Metric Value
eloc 136
c 20
b 0
f 0
dl 0
loc 459
ccs 21
cts 153
cp 0.1373
rs 6.96
wmc 53

25 Methods

Rating   Name   Duplication   Size   Complexity  
A set() 0 3 1
A __get() 0 3 1
A __isset() 0 3 1
A unprefix() 0 7 3
A __set() 0 3 1
A qualify() 0 8 2
A serialize() 0 3 1
A get() 0 7 2
A finder() 0 3 1
A boot() 0 2 1
A clone() 0 2 1
A __clone() 0 10 3
A prefix() 0 11 4
A primaryKey() 0 7 2
A __construct() 0 6 2
A table() 0 10 2
A persistDelete() 0 17 2
A persistInsert() 0 17 2
A isLoaded() 0 6 2
A __toString() 0 4 1
B save() 0 34 9
A unserialize() 0 7 2
A persistUpdate() 0 24 2
A delete() 0 17 4
A getQueryBuilderInstance() 0 7 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
3
namespace Ronanchilvers\Orm;
4
5
use Carbon\Carbon;
6
use DateTime;
7
use Exception;
8
use Ronanchilvers\Orm\Features\HasAttributes;
9
use Ronanchilvers\Orm\Features\HasHooks;
10
use Ronanchilvers\Orm\Features\HasRelationships;
11
use Ronanchilvers\Orm\Features\HasTimestamps;
12
use Ronanchilvers\Orm\Orm;
13
use Ronanchilvers\Utility\Str;
14
use RuntimeException;
15
use Serializable;
16
17
/**
18
 * Base model class for all models
19
 *
20
 * @property int id
21
 * @author Ronan Chilvers <[email protected]>
22
 */
23
abstract class Model implements Serializable
24
{
25
    use HasHooks,
26
        HasAttributes,
27
        HasTimestamps,
28
        HasRelationships;
29
30
    /**
31
     * @var string
32
     */
33
    static protected $finder = '';
34
35
    /**
36
     * @var string
37
     */
38
    static protected $table = '';
39
40
    /**
41
     * @var string
42
     */
43
    static protected $columnPrefix = '';
44
45
    /**
46
     * @var string
47
     */
48
    static protected $primaryKey = '';
49
50
    /**
51
     * Get the finder class for this model
52
     *
53
     * @return string
54
     * @author Ronan Chilvers <[email protected]>
55
     */
56
    static public function finder()
57
    {
58
        return static::$finder;
59
    }
60
61
    /**
62
     * Get the table name for this model
63
     *
64
     * @return string
65
     * @author Ronan Chilvers <[email protected]>
66
     */
67
    static public function table()
68
    {
69
        if ('' === static::$table) {
70
            $reflection = new \ReflectionClass(get_called_class());
71
            $table = Str::plural(Str::snake($reflection->getShortName()), 2);
72
73
            return $table;
74
        }
75
76
        return static::$table;
77
    }
78
79
    /**
80
     * Get the primary key fieldname for this model
81
     *
82
     * @return string
83
     * @author Ronan Chilvers <[email protected]>
84
     */
85
    static public function primaryKey()
86
    {
87
        if ('' === static::$primaryKey) {
88
            return static::prefix('id');
89
        }
90
91
        return static::$primaryKey;
92
    }
93
94
    /**
95
     * Prefix a string with the configured field prefix
96
     *
97
     * @param  string $string
98
     * @return string
99
     * @author Ronan Chilvers <[email protected]>
100
     */
101 1
    static public function prefix($string)
102
    {
103 1
        $prefix = static::$columnPrefix;
104 1
        if (!empty($prefix)) {
105
            $prefix = "{$prefix}_";
106
        }
107 1
        if (!empty($prefix) && 0 === strpos($string, $prefix)) {
108
            return $string;
109
        }
110
111 1
        return "{$prefix}{$string}";
112
    }
113
114
    /**
115
     * Un-prefix a string with the configured field prefix
116
     *
117
     * @param string $string
118
     * @return string
119
     * @author Ronan Chilvers <[email protected]>
120
     */
121 1
    static public function unprefix($string)
122
    {
123 1
        if (!empty(static::$columnPrefix) && 0 === strpos($string, static::$columnPrefix)) {
124
            return substr($string, strlen(static::$columnPrefix) + 1);
125
        }
126
127 1
        return $string;
128
    }
129
130
    /**
131
     * Transform a string into a fully qualified column with table and prefix
132
     *
133
     * @param string $string
134
     * @return string
135
     * @author Ronan Chilvers <[email protected]>
136
     */
137
    public static function qualify($string)
138
    {
139
        if ('*' != $string) {
140
            $string = static::prefix($string);
141
        }
142
        $table = static::table();
143
144
        return "{$table}.{$string}";
145
    }
146
147
    /**
148
     * Class constructor
149
     *
150
     * @author Ronan Chilvers <[email protected]>
151
     */
152 2
    public function __construct()
153
    {
154 2
        if ($this->useTimestamps()) {
155 2
            $this->bootHasTimestamps();
156
        }
157 2
        $this->boot();
158 2
    }
159
160
    /**
161
     * Magic clone method to ensure that cloned models are new
162
     *
163
     * @author Ronan Chilvers <[email protected]>
164
     */
165
    public function __clone()
166
    {
167
        $primaryKey = static::primaryKey();
168
        if (isset($this->data[$primaryKey])) {
169
            unset($this->data[$primaryKey]);
170
        }
171
        if ($this->useTimestamps()) {
172
            $this->clearTimestamps();
173
        }
174
        $this->clone();
175
    }
176
177
    /**
178
     * Clone function designed to be overriden by subclasses
179
     *
180
     *
181
     * @author Ronan Chilvers <[email protected]>
182
     */
183
    protected function clone()
184
    {}
185
186
    /**
187
     * Boot the model
188
     *
189
     * @author Ronan Chilvers <[email protected]>
190
     */
191 2
    protected function boot()
192 2
    {}
193
194
    /**
195
     * Magic property isset
196
     *
197
     * @param string $attribute
198
     * @return bool
199
     * @author Ronan Chilvers <[email protected]>
200
     */
201
    public function __isset($attribute)
202
    {
203
        return $this->hasAttribute($attribute);
204
    }
205
206
    /**
207
     * General property getter
208
     *
209
     * @param string $attribute
210
     * @return mixed
211
     * @author Ronan Chilvers <[email protected]>
212
     */
213 1
    public function get($attribute)
214
    {
215 1
        $result = $this->getRelation($attribute);
216 1
        if (!is_null($result)) {
217
            return $result;
218
        }
219 1
        return $this->getAttribute($attribute);
220
    }
221
222
    /**
223
     * Magic property getter
224
     *
225
     * @param string $attribute
226
     * @return mixed
227
     * @author Ronan Chilvers <[email protected]>
228
     */
229 1
    public function __get($attribute)
230
    {
231 1
        return $this->get($attribute);
232
    }
233
234
    /**
235
     * General property setter
236
     *
237
     * @param string $attribute
238
     * @param mixed $value
239
     * @author Ronan Chilvers <[email protected]>
240
     */
241
    public function set($attribute, $value)
242
    {
243
        return $this->setAttribute($attribute, $value);
244
    }
245
246
    /**
247
     * Magic property setter
248
     *
249
     * @param string $attribute
250
     * @param mixed $value
251
     * @author Ronan Chilvers <[email protected]>
252
     */
253
    public function __set($attribute, $value)
254
    {
255
        return $this->set($attribute, $value);
256
    }
257
258
    /**
259
     * Serialize the data array
260
     *
261
     * @return string
262
     * @author Ronan Chilvers <[email protected]>
263
     */
264
    public function serialize()
265
    {
266
        return serialize($this->toArray());
267
    }
268
269
    /**
270
     * Unserialize the data array
271
     *
272
     * @param string $serialized
273
     * @author Ronan Chilvers <[email protected]>
274
     */
275
    public function unserialize($serialized)
276
    {
277
        $this->fromArray(unserialize($serialized));
278
        if ($this->useTimestamps()) {
279
            $this->bootHasTimestamps();
280
        }
281
        $this->boot();
282
    }
283
284
    /**
285
     * Standard toString method returns id
286
     *
287
     * @return string
288
     * @author Ronan Chilvers <[email protected]>
289
     */
290
    public function __toString()
291
    {
292
        return (string) $this->getAttribute(
293
            static::primaryKey()
294
        );
295
    }
296
297
    /* Persistance methods **************/
298
    /************************************/
299
300
    /**
301
     * Is this model loaded?
302
     *
303
     * This simply means, do we have a primary key id?
304
     *
305
     * @return boolean
306
     * @author Ronan Chilvers <[email protected]>
307
     */
308
    public function isLoaded()
309
    {
310
        $key = static::primaryKey();
311
        return (
312
            $this->hasAttribute($key) &&
313
            is_numeric($this->getAttribute($key))
314
        );
315
    }
316
317
    /**
318
     * Save this model
319
     *
320
     * This method either inserts or updates the model row based on the presence
321
     * of an ID. It will return false if the save fails.
322
     *
323
     * @return boolean
324
     * @author Ronan Chilvers <[email protected]>
325
     */
326
    public function save()
327
    {
328
        if (false === $this->beforeSave()) {
329
            return false;
330
        }
331
        if (true === $this->isLoaded()) {
332
            if (false === $this->beforeUpdate()) {
333
                return false;
334
            }
335
            if ($this->useTimestamps()) {
336
                $this->updateTimestamps();
337
            }
338
            if (true !== $this->persistUpdate()) {
339
                return false;
340
            }
341
            $this->afterUpdate();
342
            $this->afterSave();
343
344
            return true;
345
        }
346
347
        if (false === $this->beforeCreate()) {
348
            return false;
349
        }
350
        if ($this->useTimestamps()) {
351
            $this->updateTimestamps();
352
        }
353
        if (true !== $this->persistInsert()) {
354
            return false;
355
        }
356
        $this->afterCreate();
357
        $this->afterSave();
358
359
        return true;
360
    }
361
362
    /**
363
     * Delete this model record
364
     *
365
     * @return boolean
366
     * @author Ronan Chilvers <[email protected]>
367
     */
368
    public function delete()
369
    {
370
        if (!$this->isLoaded()) {
371
            throw new RuntimeException(
372
                sprintf('Unable to delete model without primary key %s', static::primaryKey())
373
            );
374
        }
375
        if (false === $this->beforeDelete()) {
376
            return false;
377
        }
378
        if (false === $this->persistDelete()) {
379
            return false;
380
        }
381
        unset($this->data[static::primaryKey()]);
382
        $this->afterDelete();
383
384
        return true;
385
    }
386
387
    /**
388
     * Insert this model into the database
389
     *
390
     * @return boolean
391
     * @author Ronan Chilvers <[email protected]>
392
     */
393
    protected function persistInsert()
394
    {
395
        $this->beforePersist();
396
        $queryBuilder = $this->getQueryBuilderInstance();
397
        $query        = $queryBuilder->insert();
398
        $data         = $this->getAttributes();
399
        unset($data[static::primaryKey()]);
400
        $query->values(
401
            $data
402
        );
403
        if (true !== $query->execute()) {
404
            return false;
405
        }
406
        $this->data[static::primaryKey()] = $queryBuilder->getConnection()->lastInsertId();
407
        $this->oldData = [];
408
409
        return true;
410
    }
411
412
    /**
413
     * Update this model in the database
414
     *
415
     * @return boolean
416
     * @author Ronan Chilvers <[email protected]>
417
     */
418
    protected function persistUpdate()
419
    {
420
        $this->beforePersist();
421
        $queryBuilder = $this->getQueryBuilderInstance();
422
        $query        = $queryBuilder->update();
423
        $data         = $this->getAttributes();
424
        $id           = $data[static::primaryKey()];
425
        unset($data[static::primaryKey()]);
426
        $query
427
            ->set(
428
                $data
429
            )
430
            ->where(
431
                static::primaryKey(),
432
                '=',
433
                $id
434
            );
435
        $result = $query->execute();
436
        if (false == $result) {
437
            return false;
438
        }
439
        $this->oldData = [];
440
441
        return true;
442
    }
443
444
    /**
445
     * Delete this model from the database
446
     *
447
     * @return boolean
448
     * @author Ronan Chilvers <[email protected]>
449
     */
450
    protected function persistDelete()
451
    {
452
        $queryBuilder = $this->getQueryBuilderInstance();
453
        $query = $queryBuilder
454
            ->delete()
455
            ->where(
456
                static::primaryKey(),
457
                '=',
458
                $this->data[static::primaryKey()]
459
            )
460
            ;
461
        if (false === $query->execute()) {
462
            return false;
463
        }
464
        unset($this->data[static::primaryKey()]);
465
466
        return true;
467
    }
468
469
    /**
470
     * Get a query builder for this model
471
     *
472
     * @return \Ronanchilvers\Orm\QueryBuilder
473
     * @author Ronan Chilvers <[email protected]>
474
     */
475
    protected function getQueryBuilderInstance()
476
    {
477
        $connection = Orm::getConnection();
478
479
        return new QueryBuilder(
480
            $connection,
481
            get_called_class()
482
        );
483
    }
484
485
    /* Persistance methods **************/
486
    /************************************/
487
}
488