Passed
Push — master ( 499888...edf93f )
by Ronan
03:17 queued 11s
created

Model   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 466
Duplicated Lines 0 %

Test Coverage

Coverage 13.38%

Importance

Changes 21
Bugs 0 Features 0
Metric Value
eloc 140
c 21
b 0
f 0
dl 0
loc 466
ccs 21
cts 157
cp 0.1338
rs 6
wmc 55

25 Methods

Rating   Name   Duplication   Size   Complexity  
A unprefix() 0 7 3
A qualify() 0 8 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 set() 0 3 1
A persistDelete() 0 17 2
A __get() 0 3 1
A __isset() 0 10 3
A __set() 0 3 1
A persistInsert() 0 17 2
A isLoaded() 0 6 2
A __toString() 0 4 1
A serialize() 0 3 1
A get() 0 7 2
B save() 0 34 9
A unserialize() 0 7 2
A delete() 0 17 4
A getQueryBuilderInstance() 0 7 1
A persistUpdate() 0 24 2

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
        if ($this->hasAttribute($attribute)) {
204
            return true;
205
        }
206
        if ($this->hasRelation($attribute)) {
207
            return true;
208
        }
209
210
        return false;
211
    }
212
213
    /**
214
     * General property getter
215
     *
216
     * @param string $attribute
217
     * @return mixed
218
     * @author Ronan Chilvers <[email protected]>
219
     */
220 1
    public function get($attribute)
221
    {
222 1
        $result = $this->getRelation($attribute);
223 1
        if (!is_null($result)) {
224
            return $result;
225
        }
226 1
        return $this->getAttribute($attribute);
227
    }
228
229
    /**
230
     * Magic property getter
231
     *
232
     * @param string $attribute
233
     * @return mixed
234
     * @author Ronan Chilvers <[email protected]>
235
     */
236 1
    public function __get($attribute)
237
    {
238 1
        return $this->get($attribute);
239
    }
240
241
    /**
242
     * General property setter
243
     *
244
     * @param string $attribute
245
     * @param mixed $value
246
     * @author Ronan Chilvers <[email protected]>
247
     */
248
    public function set($attribute, $value)
249
    {
250
        return $this->setAttribute($attribute, $value);
251
    }
252
253
    /**
254
     * Magic property setter
255
     *
256
     * @param string $attribute
257
     * @param mixed $value
258
     * @author Ronan Chilvers <[email protected]>
259
     */
260
    public function __set($attribute, $value)
261
    {
262
        return $this->set($attribute, $value);
263
    }
264
265
    /**
266
     * Serialize the data array
267
     *
268
     * @return string
269
     * @author Ronan Chilvers <[email protected]>
270
     */
271
    public function serialize()
272
    {
273
        return serialize($this->toArray());
274
    }
275
276
    /**
277
     * Unserialize the data array
278
     *
279
     * @param string $serialized
280
     * @author Ronan Chilvers <[email protected]>
281
     */
282
    public function unserialize($serialized)
283
    {
284
        $this->fromArray(unserialize($serialized));
285
        if ($this->useTimestamps()) {
286
            $this->bootHasTimestamps();
287
        }
288
        $this->boot();
289
    }
290
291
    /**
292
     * Standard toString method returns id
293
     *
294
     * @return string
295
     * @author Ronan Chilvers <[email protected]>
296
     */
297
    public function __toString()
298
    {
299
        return (string) $this->getAttribute(
300
            static::primaryKey()
301
        );
302
    }
303
304
    /* Persistance methods **************/
305
    /************************************/
306
307
    /**
308
     * Is this model loaded?
309
     *
310
     * This simply means, do we have a primary key id?
311
     *
312
     * @return boolean
313
     * @author Ronan Chilvers <[email protected]>
314
     */
315
    public function isLoaded()
316
    {
317
        $key = static::primaryKey();
318
        return (
319
            $this->hasAttribute($key) &&
320
            is_numeric($this->getAttribute($key))
321
        );
322
    }
323
324
    /**
325
     * Save this model
326
     *
327
     * This method either inserts or updates the model row based on the presence
328
     * of an ID. It will return false if the save fails.
329
     *
330
     * @return boolean
331
     * @author Ronan Chilvers <[email protected]>
332
     */
333
    public function save()
334
    {
335
        if (false === $this->beforeSave()) {
336
            return false;
337
        }
338
        if (true === $this->isLoaded()) {
339
            if (false === $this->beforeUpdate()) {
340
                return false;
341
            }
342
            if ($this->useTimestamps()) {
343
                $this->updateTimestamps();
344
            }
345
            if (true !== $this->persistUpdate()) {
346
                return false;
347
            }
348
            $this->afterUpdate();
349
            $this->afterSave();
350
351
            return true;
352
        }
353
354
        if (false === $this->beforeCreate()) {
355
            return false;
356
        }
357
        if ($this->useTimestamps()) {
358
            $this->updateTimestamps();
359
        }
360
        if (true !== $this->persistInsert()) {
361
            return false;
362
        }
363
        $this->afterCreate();
364
        $this->afterSave();
365
366
        return true;
367
    }
368
369
    /**
370
     * Delete this model record
371
     *
372
     * @return boolean
373
     * @author Ronan Chilvers <[email protected]>
374
     */
375
    public function delete()
376
    {
377
        if (!$this->isLoaded()) {
378
            throw new RuntimeException(
379
                sprintf('Unable to delete model without primary key %s', static::primaryKey())
380
            );
381
        }
382
        if (false === $this->beforeDelete()) {
383
            return false;
384
        }
385
        if (false === $this->persistDelete()) {
386
            return false;
387
        }
388
        unset($this->data[static::primaryKey()]);
389
        $this->afterDelete();
390
391
        return true;
392
    }
393
394
    /**
395
     * Insert this model into the database
396
     *
397
     * @return boolean
398
     * @author Ronan Chilvers <[email protected]>
399
     */
400
    protected function persistInsert()
401
    {
402
        $this->beforePersist();
403
        $queryBuilder = $this->getQueryBuilderInstance();
404
        $query        = $queryBuilder->insert();
405
        $data         = $this->getAttributes();
406
        unset($data[static::primaryKey()]);
407
        $query->values(
408
            $data
409
        );
410
        if (true !== $query->execute()) {
411
            return false;
412
        }
413
        $this->data[static::primaryKey()] = $queryBuilder->getConnection()->lastInsertId();
414
        $this->oldData = [];
415
416
        return true;
417
    }
418
419
    /**
420
     * Update this model in the database
421
     *
422
     * @return boolean
423
     * @author Ronan Chilvers <[email protected]>
424
     */
425
    protected function persistUpdate()
426
    {
427
        $this->beforePersist();
428
        $queryBuilder = $this->getQueryBuilderInstance();
429
        $query        = $queryBuilder->update();
430
        $data         = $this->getAttributes();
431
        $id           = $data[static::primaryKey()];
432
        unset($data[static::primaryKey()]);
433
        $query
434
            ->set(
435
                $data
436
            )
437
            ->where(
438
                static::primaryKey(),
439
                '=',
440
                $id
441
            );
442
        $result = $query->execute();
443
        if (false == $result) {
444
            return false;
445
        }
446
        $this->oldData = [];
447
448
        return true;
449
    }
450
451
    /**
452
     * Delete this model from the database
453
     *
454
     * @return boolean
455
     * @author Ronan Chilvers <[email protected]>
456
     */
457
    protected function persistDelete()
458
    {
459
        $queryBuilder = $this->getQueryBuilderInstance();
460
        $query = $queryBuilder
461
            ->delete()
462
            ->where(
463
                static::primaryKey(),
464
                '=',
465
                $this->data[static::primaryKey()]
466
            )
467
            ;
468
        if (false === $query->execute()) {
469
            return false;
470
        }
471
        unset($this->data[static::primaryKey()]);
472
473
        return true;
474
    }
475
476
    /**
477
     * Get a query builder for this model
478
     *
479
     * @return \Ronanchilvers\Orm\QueryBuilder
480
     * @author Ronan Chilvers <[email protected]>
481
     */
482
    protected function getQueryBuilderInstance()
483
    {
484
        $connection = Orm::getConnection();
485
486
        return new QueryBuilder(
487
            $connection,
488
            get_called_class()
489
        );
490
    }
491
492
    /* Persistance methods **************/
493
    /************************************/
494
}
495