Model::getUrl()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Magister\Services\Database\Elegant;
4
5
use DateTime;
6
use LogicException;
7
use Magister\Services\Contracts\Support\Arrayable;
8
use Magister\Services\Contracts\Support\Jsonable;
9
use Magister\Services\Database\ConnectionResolverInterface as Resolver;
10
use Magister\Services\Database\Elegant\Relations\HasMany;
11
use Magister\Services\Database\Elegant\Relations\HasOne;
12
use Magister\Services\Database\Elegant\Relations\Relation;
13
use Magister\Services\Database\Query\Builder as QueryBuilder;
14
use Magister\Services\Support\Collection;
15
use RuntimeException;
16
17
/**
18
 * Class Model.
19
 */
20
abstract class Model implements Arrayable, \ArrayAccess, Jsonable
21
{
22
    /**
23
     * The connection name for the model.
24
     *
25
     * @var string
26
     */
27
    protected $connection;
28
29
    /**
30
     * The primary key for the model.
31
     *
32
     * @var string
33
     */
34
    protected $primaryKey = 'Id';
35
36
    /**
37
     * The connection resolver instance.
38
     *
39
     * @var \Magister\Services\Database\ConnectionResolverInterface
40
     */
41
    protected static $resolver;
42
43
    /**
44
     * The model's attributes.
45
     *
46
     * @var array
47
     */
48
    protected $attributes = [];
49
50
    /**
51
     * The loaded relationships for the model.
52
     *
53
     * @var array
54
     */
55
    protected $relations = [];
56
57
    /**
58
     * The attributes that should be mutated to dates.
59
     *
60
     * @var array
61
     */
62
    protected $dates = [];
63
64
    /**
65
     * Create a new model instance.
66
     *
67
     * @param array $attributes
68
     */
69
    public function __construct(array $attributes = [])
70
    {
71
        $this->fill($attributes);
72
    }
73
74
    /**
75
     * Fill the model with an array of attributes.
76
     *
77
     * @param array $attributes
78
     *
79
     * @return $this
80
     */
81
    public function fill(array $attributes)
82
    {
83
        foreach ($attributes as $key => $value) {
84
            $this->setAttribute($key, $value);
85
        }
86
87
        return $this;
88
    }
89
90
    /**
91
     * Get all of the items from the model.
92
     *
93
     * @return \Magister\Services\Support\Collection
94
     */
95
    public static function all()
96
    {
97
        $instance = new static();
98
99
        return $instance->newQuery()->get();
0 ignored issues
show
Bug Compatibility introduced by
The expression $instance->newQuery()->get(); of type Magister\Services\Suppor...abase\Elegant\Builder[] adds the type Magister\Services\Database\Elegant\Builder[] to the return on line 99 which is incompatible with the return type documented by Magister\Services\Database\Elegant\Model::all of type Magister\Services\Support\Collection.
Loading history...
100
    }
101
102
    /**
103
     * Find a model by its primary key.
104
     *
105
     * @param mixed $id
106
     *
107
     * @return \Magister\Services\Support\Collection|static|null
108
     */
109
    public static function find($id)
110
    {
111
        return static::query()->find($id);
112
    }
113
114
    /**
115
     * Begin querying the model on a given connection.
116
     *
117
     * @param string|null $connection
118
     *
119
     * @return \Magister\Services\Database\Elegant\Builder
120
     */
121
    public static function on($connection = null)
122
    {
123
        $instance = new static();
124
125
        $instance->setConnection($connection);
126
127
        return $instance->newQuery();
128
    }
129
130
    /**
131
     * Define a one-to-one relationship.
132
     *
133
     * @param string $related
134
     * @param string $foreignKey
135
     * @param string $localKey
136
     *
137
     * @return \Magister\Services\Database\Elegant\Relations\HasOne
138
     */
139 View Code Duplication
    public function hasOne($related, $foreignKey = null, $localKey = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
    {
141
        $foreignKey = $foreignKey ?: $this->getForeignKey();
142
143
        $instance = new $related();
144
145
        $localKey = $localKey ?: $this->getKeyName();
146
147
        return new HasOne($instance->newQuery(), $this, $foreignKey, $localKey);
148
    }
149
150
    /**
151
     * Define a one-to-many relationship.
152
     *
153
     * @param string $related
154
     * @param string $foreignKey
155
     * @param string $localKey
156
     *
157
     * @return \Magister\Services\Database\Elegant\Relations\HasMany
158
     */
159 View Code Duplication
    public function hasMany($related, $foreignKey = null, $localKey = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
    {
161
        $foreignKey = $foreignKey ?: $this->getForeignKey();
162
163
        $instance = new $related();
164
165
        $localKey = $localKey ?: $this->getKeyName();
166
167
        return new HasMany($instance->newQuery(), $this, $foreignKey, $localKey);
168
    }
169
170
    /**
171
     * Create a new instance of the given model.
172
     *
173
     * @param array $attributes
174
     *
175
     * @return static
176
     */
177
    public function newInstance($attributes = [])
178
    {
179
        return new static((array) $attributes);
180
    }
181
182
    /**
183
     * Create a new model instance.
184
     *
185
     * @param array       $attributes
186
     * @param string|null $connection
187
     *
188
     * @return static
189
     */
190
    public function newFromBuilder($attributes = [], $connection = null)
191
    {
192
        $model = $this->newInstance();
193
194
        $model->setRawAttributes((array) $attributes);
195
196
        $model->setConnection($connection ?: $this->connection);
197
198
        return $model;
199
    }
200
201
    /**
202
     * Create a collection of models from plain arrays.
203
     *
204
     * @param array       $items
205
     * @param string|null $connection
206
     *
207
     * @return \Magister\Services\Support\Collection
208
     */
209
    public static function hydrate(array $items, $connection = null)
210
    {
211
        $instance = (new static())->setConnection($connection);
212
213
        $items = array_map(function ($item) use ($instance) {
214
            return $instance->newFromBuilder($item);
215
        }, $items);
216
217
        return $instance->newCollection($items);
218
    }
219
220
    /**
221
     * Begin querying the model.
222
     *
223
     * @return \Magister\Services\Database\Elegant\Builder
224
     */
225
    public static function query()
226
    {
227
        return (new static())->newQuery();
228
    }
229
230
    /**
231
     * Get a new query builder.
232
     *
233
     * @return \Magister\Services\Database\Elegant\Builder
234
     */
235
    public function newQuery()
236
    {
237
        $builder = $this->newElegantBuilder(
238
            $this->newQueryBuilder()
239
        );
240
241
        return $builder->setModel($this);
242
    }
243
244
    /**
245
     * Create a new Elegant builder instance.
246
     *
247
     * @param \Magister\Services\Database\Query\Builder $query
248
     *
249
     * @return \Magister\Services\Database\Elegant\Builder
250
     */
251
    public function newElegantBuilder($query)
252
    {
253
        return new Builder($query);
254
    }
255
256
    /**
257
     * Create a new query builder instance.
258
     *
259
     * @return \Magister\Services\Database\Query\Builder
260
     */
261
    public function newQueryBuilder()
262
    {
263
        $connection = $this->getConnection();
264
265
        return new QueryBuilder($connection, $connection->getProcessor());
266
    }
267
268
    /**
269
     * Create a new collection instance.
270
     *
271
     * @param array $models
272
     *
273
     * @return \Magister\Services\Support\Collection
274
     */
275
    public function newCollection(array $models = [])
276
    {
277
        return new Collection($models);
278
    }
279
280
    /**
281
     * Set a given attribute on the model.
282
     *
283
     * @param string $key
284
     * @param mixed  $value
285
     *
286
     * @return void
287
     */
288
    public function setAttribute($key, $value)
289
    {
290
        $this->attributes[$key] = $value;
291
    }
292
293
    /**
294
     * Set the array of model attributes without checking.
295
     *
296
     * @param array $attributes
297
     *
298
     * @return void
299
     */
300
    public function setRawAttributes(array $attributes)
301
    {
302
        $this->attributes = $attributes;
303
    }
304
305
    /**
306
     * Get an attribute from the model.
307
     *
308
     * @param string $key
309
     *
310
     * @return mixed
311
     */
312
    public function getAttribute($key)
313
    {
314
        if (array_has($this->attributes, $key)) {
315
            return $this->getAttributeValue($key);
316
        }
317
318
        return $this->getRelationValue($key);
319
    }
320
321
    /**
322
     * Get a plain attribute (not a relationship).
323
     *
324
     * @param string $key
325
     *
326
     * @return mixed
327
     */
328
    public function getAttributeValue($key)
329
    {
330
        $value = $this->getAttributeFromArray($key);
331
332
        if (in_array($key, $this->getDates())) {
333
            if (!is_null($value)) {
334
                return $this->asDateTime($value);
335
            }
336
        }
337
338
        return $value;
339
    }
340
341
    /**
342
     * Get a relationship.
343
     *
344
     * @param string $key
345
     *
346
     * @return mixed
347
     */
348
    public function getRelationValue($key)
349
    {
350
        // If the key already exists in the relationships array, it just means the
351
        // relationship has already been loaded, so we'll just return it out of
352
        // here because there is no need to query within the relations twice.
353
        if ($this->relationLoaded($key)) {
354
            return $this->relations[$key];
355
        }
356
357
        // If the "attribute" exists as a method on the model, we will just assume
358
        // it is a relationship and will load and return results from the query
359
        // and hydrate the relationship's value on the "relationships" array.
360
        if (method_exists($this, $key)) {
361
            return $this->getRelationshipFromMethod($key);
362
        }
363
    }
364
365
    /**
366
     * Get an attribute from the $attributes array.
367
     *
368
     * @param string $key
369
     *
370
     * @return mixed
371
     */
372
    protected function getAttributeFromArray($key)
373
    {
374
        return array_get($this->attributes, $key);
375
    }
376
377
    /**
378
     * Get a relationship value from a method.
379
     *
380
     * @param string $method
381
     *
382
     * @throws \LogicException
383
     *
384
     * @return mixed
385
     */
386
    protected function getRelationshipFromMethod($method)
387
    {
388
        $relations = $this->$method();
389
390
        if (!$relations instanceof Relation) {
391
            throw new LogicException('Relationship method must return an object of type '.'Magister\Services\Database\Elegant\Relations\Relation');
392
        }
393
394
        return $this->relations[$method] = $relations->getResults();
0 ignored issues
show
Documentation Bug introduced by
The method getResults does not exist on object<Magister\Services...ant\Relations\Relation>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
395
    }
396
397
    /**
398
     * Get the attributes that should be converted to dates.
399
     *
400
     * @return array
401
     */
402
    public function getDates()
403
    {
404
        return $this->dates;
405
    }
406
407
    /**
408
     * Return a timestamp as a DateTime object.
409
     *
410
     * @param mixed $value
411
     *
412
     * @return \DateTime
413
     */
414
    protected function asDateTime($value)
415
    {
416
        return new DateTime($value);
417
    }
418
419
    /**
420
     * Get all of the current attributes on the model.
421
     *
422
     * @return array
423
     */
424
    public function getAttributes()
425
    {
426
        return $this->attributes;
427
    }
428
429
    /**
430
     * Get all the loaded relations for the instance.
431
     *
432
     * @return array
433
     */
434
    public function getRelations()
435
    {
436
        return $this->relations;
437
    }
438
439
    /**
440
     * Get a specified relationship.
441
     *
442
     * @param string $relation
443
     *
444
     * @return mixed
445
     */
446
    public function getRelation($relation)
447
    {
448
        return $this->relations[$relation];
449
    }
450
451
    /**
452
     * Determine if the given relation is loaded.
453
     *
454
     * @param string $key
455
     *
456
     * @return bool
457
     */
458
    public function relationLoaded($key)
459
    {
460
        return array_key_exists($key, $this->relations);
461
    }
462
463
    /**
464
     * Set the specific relationship in the model.
465
     *
466
     * @param string $relation
467
     * @param mixed  $value
468
     *
469
     * @return $this
470
     */
471
    public function setRelation($relation, $value)
472
    {
473
        $this->relations[$relation] = $value;
474
475
        return $this;
476
    }
477
478
    /**
479
     * Set the entire relations array on the model.
480
     *
481
     * @param array $relations
482
     *
483
     * @return $this
484
     */
485
    public function setRelations(array $relations)
486
    {
487
        $this->relations = $relations;
488
489
        return $this;
490
    }
491
492
    /**
493
     * Get the connection for the model.
494
     *
495
     * @return \Magister\Services\Database\Connection
496
     */
497
    public function getConnection()
498
    {
499
        return static::resolveConnection($this->connection);
500
    }
501
502
    /**
503
     * Get the current connection name for the model.
504
     *
505
     * @return string
506
     */
507
    public function getConnectionName()
508
    {
509
        return $this->connection;
510
    }
511
512
    /**
513
     * Set the connection associated with the model.
514
     *
515
     * @param string $name
516
     *
517
     * @return $this
518
     */
519
    public function setConnection($name)
520
    {
521
        $this->connection = $name;
522
523
        return $this;
524
    }
525
526
    /**
527
     * Resolve a connection instance.
528
     *
529
     * @param string $connection
530
     *
531
     * @return \Magister\Services\Database\Connection
532
     */
533
    public static function resolveConnection($connection = null)
534
    {
535
        return static::$resolver->connection($connection);
536
    }
537
538
    /**
539
     * Get the connection resolver instance.
540
     *
541
     * @return \Magister\Services\Database\ConnectionResolverInterface
542
     */
543
    public static function getConnectionResolver()
544
    {
545
        return static::$resolver;
546
    }
547
548
    /**
549
     * Set the connection resolver instance.
550
     *
551
     * @param \Magister\Services\Database\ConnectionResolverInterface $resolver
552
     *
553
     * @return void
554
     */
555
    public static function setConnectionResolver(Resolver $resolver)
556
    {
557
        static::$resolver = $resolver;
558
    }
559
560
    /**
561
     * Get the url associated with the model.
562
     *
563
     * @throws \RuntimeException
564
     *
565
     * @return void
566
     */
567
    public function getUrl()
568
    {
569
        throw new RuntimeException('The Model class does not implement a getUrl() method.');
570
    }
571
572
    /**
573
     * Get the value of the model's primary key.
574
     *
575
     * @return mixed
576
     */
577
    public function getKey()
578
    {
579
        return $this->getAttribute($this->getKeyName());
580
    }
581
582
    /**
583
     * Get the primary key for the model.
584
     *
585
     * @return string
586
     */
587
    public function getKeyName()
588
    {
589
        return $this->primaryKey;
590
    }
591
592
    /**
593
     * Set the primary key for the model.
594
     *
595
     * @param string $key
596
     *
597
     * @return void
598
     */
599
    public function setKeyName($key)
600
    {
601
        $this->primaryKey = $key;
602
    }
603
604
    /**
605
     * Get the default foreign key name for the model.
606
     *
607
     * @return string
608
     */
609
    public function getForeignKey()
610
    {
611
        return snake_case(class_basename($this)).'_id';
612
    }
613
614
    /**
615
     * Convert the model instance to JSON.
616
     *
617
     * @param int $options
618
     *
619
     * @return string
620
     */
621
    public function toJson($options = 0)
622
    {
623
        return json_encode($this->toArray(), $options);
624
    }
625
626
    /**
627
     * Convert the model instance to an array.
628
     *
629
     * @return array
630
     */
631
    public function toArray()
632
    {
633
        return $this->attributes;
634
    }
635
636
    /**
637
     * Dynamically set attributes on the model.
638
     *
639
     * @param string $key
640
     * @param mixed  $value
641
     *
642
     * @return void
643
     */
644
    public function __set($key, $value)
645
    {
646
        $this->setAttribute($key, $value);
647
    }
648
649
    /**
650
     * Dynamically retrieve attributes on the model.
651
     *
652
     * @param string $key
653
     *
654
     * @return mixed
655
     */
656
    public function __get($key)
657
    {
658
        return $this->getAttribute($key);
659
    }
660
661
    /**
662
     * Determine if an attribute exists on the model.
663
     *
664
     * @param string $key
665
     *
666
     * @return bool
667
     */
668
    public function __isset($key)
669
    {
670
        return isset($this->attributes[$key]);
671
    }
672
673
    /**
674
     * Unset an attribute on the model.
675
     *
676
     * @param string $key
677
     *
678
     * @return void
679
     */
680
    public function __unset($key)
681
    {
682
        unset($this->attributes[$key]);
683
    }
684
685
    /**
686
     * Set the value for a given offset.
687
     *
688
     * @param mixed $offset
689
     * @param mixed $value
690
     *
691
     * @return void
692
     */
693
    public function offsetSet($offset, $value)
694
    {
695
        $this->$offset = $value;
696
    }
697
698
    /**
699
     * Get the value for a given offset.
700
     *
701
     * @param mixed $offset
702
     *
703
     * @return mixed
704
     */
705
    public function offsetGet($offset)
706
    {
707
        return $this->$offset;
708
    }
709
710
    /**
711
     * Determine if the given attribute exists.
712
     *
713
     * @param mixed $offset
714
     *
715
     * @return bool
716
     */
717
    public function offsetExists($offset)
718
    {
719
        return isset($this->$offset);
720
    }
721
722
    /**
723
     * Unset the value for a given offset.
724
     *
725
     * @param mixed $offset
726
     *
727
     * @return void
728
     */
729
    public function offsetUnset($offset)
730
    {
731
        unset($this->$offset);
732
    }
733
734
    /**
735
     * Handle dynamic method calls into the model.
736
     *
737
     * @param string $method
738
     * @param array  $parameters
739
     *
740
     * @return mixed
741
     */
742
    public function __call($method, $parameters)
743
    {
744
        $query = $this->newQuery();
745
746
        return call_user_func_array([$query, $method], $parameters);
747
    }
748
749
    /**
750
     * Handle dynamic static method calls into the method.
751
     *
752
     * @param string $method
753
     * @param array  $parameters
754
     *
755
     * @return mixed
756
     */
757
    public static function __callStatic($method, $parameters)
758
    {
759
        $instance = new static();
760
761
        return call_user_func_array([$instance, $method], $parameters);
762
    }
763
764
    /**
765
     * Convert the model to its string representation.
766
     *
767
     * @return string
768
     */
769
    public function __toString()
770
    {
771
        return $this->toJson();
772
    }
773
}
774