· 9 years ago · Jan 13, 2017, 11:36 AM
1<?php
2
3namespace Illuminate\Database\Eloquent;
4
5use Closure;
6use Exception;
7use ArrayAccess;
8use Carbon\Carbon;
9use LogicException;
10use JsonSerializable;
11use DateTimeInterface;
12use Illuminate\Support\Arr;
13use Illuminate\Support\Str;
14use InvalidArgumentException;
15use Illuminate\Contracts\Support\Jsonable;
16use Illuminate\Contracts\Events\Dispatcher;
17use Illuminate\Contracts\Support\Arrayable;
18use Illuminate\Contracts\Routing\UrlRoutable;
19use Illuminate\Contracts\Queue\QueueableEntity;
20use Illuminate\Database\Eloquent\Relations\Pivot;
21use Illuminate\Database\Eloquent\Relations\HasOne;
22use Illuminate\Database\Eloquent\Relations\HasMany;
23use Illuminate\Database\Eloquent\Relations\MorphTo;
24use Illuminate\Database\Eloquent\Relations\MorphOne;
25use Illuminate\Database\Eloquent\Relations\Relation;
26use Illuminate\Support\Collection as BaseCollection;
27use Illuminate\Database\Eloquent\Relations\BelongsTo;
28use Illuminate\Database\Eloquent\Relations\MorphMany;
29use Illuminate\Database\Query\Builder as QueryBuilder;
30use Illuminate\Database\Eloquent\Relations\MorphToMany;
31use Illuminate\Database\Eloquent\Relations\BelongsToMany;
32use Illuminate\Database\Eloquent\Relations\HasManyThrough;
33use Illuminate\Database\ConnectionResolverInterface as Resolver;
34
35abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
36{
37 /**
38 * The connection name for the model.
39 *
40 * @var string
41 */
42 protected $connection;
43
44 /**
45 * The table associated with the model.
46 *
47 * @var string
48 */
49 protected $table;
50
51 /**
52 * The primary key for the model.
53 *
54 * @var string
55 */
56 protected $primaryKey = 'id';
57
58 /**
59 * The "type" of the auto-incrementing ID.
60 *
61 * @var string
62 */
63 protected $keyType = 'int';
64
65 /**
66 * The number of models to return for pagination.
67 *
68 * @var int
69 */
70 protected $perPage = 15;
71
72 /**
73 * Indicates if the IDs are auto-incrementing.
74 *
75 * @var bool
76 */
77 public $incrementing = true;
78
79 /**
80 * Indicates if the model should be timestamped.
81 *
82 * @var bool
83 */
84 public $timestamps = true;
85
86 /**
87 * The model's attributes.
88 *
89 * @var array
90 */
91 protected $attributes = [];
92
93 /**
94 * The model attribute's original state.
95 *
96 * @var array
97 */
98 protected $original = [];
99
100 /**
101 * The loaded relationships for the model.
102 *
103 * @var array
104 */
105 protected $relations = [];
106
107 /**
108 * The attributes that should be hidden for arrays.
109 *
110 * @var array
111 */
112 protected $hidden = [];
113
114 /**
115 * The attributes that should be visible in arrays.
116 *
117 * @var array
118 */
119 protected $visible = [];
120
121 /**
122 * The accessors to append to the model's array form.
123 *
124 * @var array
125 */
126 protected $appends = [];
127
128 /**
129 * The attributes that are mass assignable.
130 *
131 * @var array
132 */
133 protected $fillable = [];
134
135 /**
136 * The attributes that aren't mass assignable.
137 *
138 * @var array
139 */
140 protected $guarded = ['*'];
141
142 /**
143 * The attributes that should be mutated to dates.
144 *
145 * @var array
146 */
147 protected $dates = [];
148
149 /**
150 * The storage format of the model's date columns.
151 *
152 * @var string
153 */
154 protected $dateFormat;
155
156 /**
157 * The attributes that should be cast to native types.
158 *
159 * @var array
160 */
161 protected $casts = [];
162
163 /**
164 * The relationships that should be touched on save.
165 *
166 * @var array
167 */
168 protected $touches = [];
169
170 /**
171 * User exposed observable events.
172 *
173 * @var array
174 */
175 protected $observables = [];
176
177 /**
178 * The relations to eager load on every query.
179 *
180 * @var array
181 */
182 protected $with = [];
183
184 /**
185 * Indicates if the model exists.
186 *
187 * @var bool
188 */
189 public $exists = false;
190
191 /**
192 * Indicates if the model was inserted during the current request lifecycle.
193 *
194 * @var bool
195 */
196 public $wasRecentlyCreated = false;
197
198 /**
199 * Indicates whether attributes are snake cased on arrays.
200 *
201 * @var bool
202 */
203 public static $snakeAttributes = true;
204
205 /**
206 * The connection resolver instance.
207 *
208 * @var \Illuminate\Database\ConnectionResolverInterface
209 */
210 protected static $resolver;
211
212 /**
213 * The event dispatcher instance.
214 *
215 * @var \Illuminate\Contracts\Events\Dispatcher
216 */
217 protected static $dispatcher;
218
219 /**
220 * The array of booted models.
221 *
222 * @var array
223 */
224 protected static $booted = [];
225
226 /**
227 * The array of global scopes on the model.
228 *
229 * @var array
230 */
231 protected static $globalScopes = [];
232
233 /**
234 * Indicates if all mass assignment is enabled.
235 *
236 * @var bool
237 */
238 protected static $unguarded = false;
239
240 /**
241 * The cache of the mutated attributes for each class.
242 *
243 * @var array
244 */
245 protected static $mutatorCache = [];
246
247 /**
248 * The many to many relationship methods.
249 *
250 * @var array
251 */
252 public static $manyMethods = ['belongsToMany', 'morphToMany', 'morphedByMany'];
253
254 /**
255 * The name of the "created at" column.
256 *
257 * @var string
258 */
259 const CREATED_AT = 'created_at';
260
261 /**
262 * The name of the "updated at" column.
263 *
264 * @var string
265 */
266 const UPDATED_AT = 'updated_at';
267
268 /**
269 * Create a new Eloquent model instance.
270 *
271 * @param array $attributes
272 * @return void
273 */
274 public function __construct(array $attributes = [])
275 {
276 $this->bootIfNotBooted();
277
278 $this->syncOriginal();
279
280 $this->fill($attributes);
281 }
282
283 /**
284 * Check if the model needs to be booted and if so, do it.
285 *
286 * @return void
287 */
288 protected function bootIfNotBooted()
289 {
290 if (! isset(static::$booted[static::class])) {
291 static::$booted[static::class] = true;
292
293 $this->fireModelEvent('booting', false);
294
295 static::boot();
296
297 $this->fireModelEvent('booted', false);
298 }
299 }
300
301 /**
302 * The "booting" method of the model.
303 *
304 * @return void
305 */
306 protected static function boot()
307 {
308 static::bootTraits();
309 }
310
311 /**
312 * Boot all of the bootable traits on the model.
313 *
314 * @return void
315 */
316 protected static function bootTraits()
317 {
318 $class = static::class;
319
320 foreach (class_uses_recursive($class) as $trait) {
321 if (method_exists($class, $method = 'boot'.class_basename($trait))) {
322 forward_static_call([$class, $method]);
323 }
324 }
325 }
326
327 /**
328 * Clear the list of booted models so they will be re-booted.
329 *
330 * @return void
331 */
332 public static function clearBootedModels()
333 {
334 static::$booted = [];
335 static::$globalScopes = [];
336 }
337
338 /**
339 * Register a new global scope on the model.
340 *
341 * @param \Illuminate\Database\Eloquent\Scope|\Closure|string $scope
342 * @param \Closure|null $implementation
343 * @return mixed
344 *
345 * @throws \InvalidArgumentException
346 */
347 public static function addGlobalScope($scope, Closure $implementation = null)
348 {
349 if (is_string($scope) && ! is_null($implementation)) {
350 return static::$globalScopes[static::class][$scope] = $implementation;
351 }
352
353 if ($scope instanceof Closure) {
354 return static::$globalScopes[static::class][spl_object_hash($scope)] = $scope;
355 }
356
357 if ($scope instanceof Scope) {
358 return static::$globalScopes[static::class][get_class($scope)] = $scope;
359 }
360
361 throw new InvalidArgumentException('Global scope must be an instance of Closure or Scope.');
362 }
363
364 /**
365 * Determine if a model has a global scope.
366 *
367 * @param \Illuminate\Database\Eloquent\Scope|string $scope
368 * @return bool
369 */
370 public static function hasGlobalScope($scope)
371 {
372 return ! is_null(static::getGlobalScope($scope));
373 }
374
375 /**
376 * Get a global scope registered with the model.
377 *
378 * @param \Illuminate\Database\Eloquent\Scope|string $scope
379 * @return \Illuminate\Database\Eloquent\Scope|\Closure|null
380 */
381 public static function getGlobalScope($scope)
382 {
383 if (! is_string($scope)) {
384 $scope = get_class($scope);
385 }
386
387 return Arr::get(static::$globalScopes, static::class.'.'.$scope);
388 }
389
390 /**
391 * Get the global scopes for this class instance.
392 *
393 * @return array
394 */
395 public function getGlobalScopes()
396 {
397 return Arr::get(static::$globalScopes, static::class, []);
398 }
399
400 /**
401 * Register an observer with the Model.
402 *
403 * @param object|string $class
404 * @param int $priority
405 * @return void
406 */
407 public static function observe($class, $priority = 0)
408 {
409 $instance = new static;
410
411 $className = is_string($class) ? $class : get_class($class);
412
413 // When registering a model observer, we will spin through the possible events
414 // and determine if this observer has that method. If it does, we will hook
415 // it into the model's event system, making it convenient to watch these.
416 foreach ($instance->getObservableEvents() as $event) {
417 if (method_exists($class, $event)) {
418 static::registerModelEvent($event, $className.'@'.$event, $priority);
419 }
420 }
421 }
422
423 /**
424 * Fill the model with an array of attributes.
425 *
426 * @param array $attributes
427 * @return $this
428 *
429 * @throws \Illuminate\Database\Eloquent\MassAssignmentException
430 */
431 public function fill(array $attributes)
432 {
433 $totallyGuarded = $this->totallyGuarded();
434
435 foreach ($this->fillableFromArray($attributes) as $key => $value) {
436 $key = $this->removeTableFromKey($key);
437
438 // The developers may choose to place some attributes in the "fillable"
439 // array, which means only those attributes may be set through mass
440 // assignment to the model, and all others will just be ignored.
441 if ($this->isFillable($key)) {
442 $this->setAttribute($key, $value);
443 } elseif ($totallyGuarded) {
444 throw new MassAssignmentException($key);
445 }
446 }
447
448 return $this;
449 }
450
451 /**
452 * Fill the model with an array of attributes. Force mass assignment.
453 *
454 * @param array $attributes
455 * @return $this
456 */
457 public function forceFill(array $attributes)
458 {
459 return static::unguarded(function () use ($attributes) {
460 return $this->fill($attributes);
461 });
462 }
463
464 /**
465 * Get the fillable attributes of a given array.
466 *
467 * @param array $attributes
468 * @return array
469 */
470 protected function fillableFromArray(array $attributes)
471 {
472 if (count($this->getFillable()) > 0 && ! static::$unguarded) {
473 return array_intersect_key($attributes, array_flip($this->getFillable()));
474 }
475
476 return $attributes;
477 }
478
479 /**
480 * Create a new instance of the given model.
481 *
482 * @param array $attributes
483 * @param bool $exists
484 * @return static
485 */
486 public function newInstance($attributes = [], $exists = false)
487 {
488 // This method just provides a convenient way for us to generate fresh model
489 // instances of this current model. It is particularly useful during the
490 // hydration of new objects via the Eloquent query builder instances.
491 $model = new static((array) $attributes);
492
493 $model->exists = $exists;
494
495 return $model;
496 }
497
498 /**
499 * Create a new model instance that is existing.
500 *
501 * @param array $attributes
502 * @param string|null $connection
503 * @return static
504 */
505 public function newFromBuilder($attributes = [], $connection = null)
506 {
507 $model = $this->newInstance([], true);
508
509 $model->setRawAttributes((array) $attributes, true);
510
511 $model->setConnection($connection ?: $this->getConnectionName());
512
513 return $model;
514 }
515
516 /**
517 * Create a collection of models from plain arrays.
518 *
519 * @param array $items
520 * @param string|null $connection
521 * @return \Illuminate\Database\Eloquent\Collection
522 */
523 public static function hydrate(array $items, $connection = null)
524 {
525 $instance = (new static)->setConnection($connection);
526
527 $items = array_map(function ($item) use ($instance) {
528 return $instance->newFromBuilder($item);
529 }, $items);
530
531 return $instance->newCollection($items);
532 }
533
534 /**
535 * Create a collection of models from a raw query.
536 *
537 * @param string $query
538 * @param array $bindings
539 * @param string|null $connection
540 * @return \Illuminate\Database\Eloquent\Collection
541 */
542 public static function hydrateRaw($query, $bindings = [], $connection = null)
543 {
544 $instance = (new static)->setConnection($connection);
545
546 $items = $instance->getConnection()->select($query, $bindings);
547
548 return static::hydrate($items, $connection);
549 }
550
551 /**
552 * Save a new model and return the instance.
553 *
554 * @param array $attributes
555 * @return static
556 */
557 public static function create(array $attributes = [])
558 {
559 $model = new static($attributes);
560
561 $model->save();
562
563 return $model;
564 }
565
566 /**
567 * Save a new model and return the instance. Allow mass-assignment.
568 *
569 * @param array $attributes
570 * @return static
571 */
572 public static function forceCreate(array $attributes)
573 {
574 return static::unguarded(function () use ($attributes) {
575 return (new static)->create($attributes);
576 });
577 }
578
579 /**
580 * Begin querying the model.
581 *
582 * @return \Illuminate\Database\Eloquent\Builder
583 */
584 public static function query()
585 {
586 return (new static)->newQuery();
587 }
588
589 /**
590 * Begin querying the model on a given connection.
591 *
592 * @param string|null $connection
593 * @return \Illuminate\Database\Eloquent\Builder
594 */
595 public static function on($connection = null)
596 {
597 // First we will just create a fresh instance of this model, and then we can
598 // set the connection on the model so that it is be used for the queries
599 // we execute, as well as being set on each relationship we retrieve.
600 $instance = new static;
601
602 $instance->setConnection($connection);
603
604 return $instance->newQuery();
605 }
606
607 /**
608 * Begin querying the model on the write connection.
609 *
610 * @return \Illuminate\Database\Query\Builder
611 */
612 public static function onWriteConnection()
613 {
614 $instance = new static;
615
616 return $instance->newQuery()->useWritePdo();
617 }
618
619 /**
620 * Get all of the models from the database.
621 *
622 * @param array|mixed $columns
623 * @return \Illuminate\Database\Eloquent\Collection|static[]
624 */
625 public static function all($columns = ['*'])
626 {
627 $columns = is_array($columns) ? $columns : func_get_args();
628
629 $instance = new static;
630
631 return $instance->newQuery()->get($columns);
632 }
633
634 /**
635 * Reload a fresh model instance from the database.
636 *
637 * @param array|string $with
638 * @return static|null
639 */
640 public function fresh($with = [])
641 {
642 if (! $this->exists) {
643 return;
644 }
645
646 if (is_string($with)) {
647 $with = func_get_args();
648 }
649
650 $key = $this->getKeyName();
651
652 return static::newQueryWithoutScopes()->with($with)->where($key, $this->getKey())->first();
653 }
654
655 /**
656 * Eager load relations on the model.
657 *
658 * @param array|string $relations
659 * @return $this
660 */
661 public function load($relations)
662 {
663 if (is_string($relations)) {
664 $relations = func_get_args();
665 }
666
667 $query = $this->newQuery()->with($relations);
668
669 $query->eagerLoadRelations([$this]);
670
671 return $this;
672 }
673
674 /**
675 * Begin querying a model with eager loading.
676 *
677 * @param array|string $relations
678 * @return \Illuminate\Database\Eloquent\Builder|static
679 */
680 public static function with($relations)
681 {
682 if (is_string($relations)) {
683 $relations = func_get_args();
684 }
685
686 $instance = new static;
687
688 return $instance->newQuery()->with($relations);
689 }
690
691 /**
692 * Append attributes to query when building a query.
693 *
694 * @param array|string $attributes
695 * @return $this
696 */
697 public function append($attributes)
698 {
699 if (is_string($attributes)) {
700 $attributes = func_get_args();
701 }
702
703 $this->appends = array_unique(
704 array_merge($this->appends, $attributes)
705 );
706
707 return $this;
708 }
709
710 /**
711 * Define a one-to-one relationship.
712 *
713 * @param string $related
714 * @param string $foreignKey
715 * @param string $localKey
716 * @return \Illuminate\Database\Eloquent\Relations\HasOne
717 */
718 public function hasOne($related, $foreignKey = null, $localKey = null)
719 {
720 $foreignKey = $foreignKey ?: $this->getForeignKey();
721
722 $instance = new $related;
723 $instance->setConnection($this->getConnectionName());
724
725 $localKey = $localKey ?: $this->getKeyName();
726
727 return new HasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
728 }
729
730 /**
731 * Define a polymorphic one-to-one relationship.
732 *
733 * @param string $related
734 * @param string $name
735 * @param string $type
736 * @param string $id
737 * @param string $localKey
738 * @return \Illuminate\Database\Eloquent\Relations\MorphOne
739 */
740 public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
741 {
742 $instance = new $related;
743 $instance->setConnection($this->getConnectionName());
744
745 list($type, $id) = $this->getMorphs($name, $type, $id);
746
747 $table = $instance->getTable();
748
749 $localKey = $localKey ?: $this->getKeyName();
750
751 return new MorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
752 }
753
754 /**
755 * Define an inverse one-to-one or many relationship.
756 *
757 * @param string $related
758 * @param string $foreignKey
759 * @param string $otherKey
760 * @param string $relation
761 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
762 */
763 public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
764 {
765 // If no relation name was given, we will use this debug backtrace to extract
766 // the calling method's name and use that as the relationship name as most
767 // of the time this will be what we desire to use for the relationships.
768 if (is_null($relation)) {
769 list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
770
771 $relation = $caller['function'];
772 }
773
774 // If no foreign key was supplied, we can use a backtrace to guess the proper
775 // foreign key name by using the name of the relationship function, which
776 // when combined with an "_id" should conventionally match the columns.
777 if (is_null($foreignKey)) {
778 $foreignKey = Str::snake($relation).'_id';
779 }
780
781 $instance = new $related;
782 $instance->setConnection($this->getConnectionName());
783
784 // Once we have the foreign key names, we'll just create a new Eloquent query
785 // for the related models and returns the relationship instance which will
786 // actually be responsible for retrieving and hydrating every relations.
787 $query = $instance->newQuery();
788
789 $otherKey = $otherKey ?: $instance->getKeyName();
790
791 return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
792 }
793
794 /**
795 * Define a polymorphic, inverse one-to-one or many relationship.
796 *
797 * @param string $name
798 * @param string $type
799 * @param string $id
800 * @return \Illuminate\Database\Eloquent\Relations\MorphTo
801 */
802 public function morphTo($name = null, $type = null, $id = null)
803 {
804 // If no name is provided, we will use the backtrace to get the function name
805 // since that is most likely the name of the polymorphic interface. We can
806 // use that to get both the class and foreign key that will be utilized.
807 if (is_null($name)) {
808 list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
809
810 $name = $caller['function'];
811 }
812
813 list($type, $id) = $this->getMorphs(Str::snake($name), $type, $id);
814
815 // If the type value is null it is probably safe to assume we're eager loading
816 // the relationship. In this case we'll just pass in a dummy query where we
817 // need to remove any eager loads that may already be defined on a model.
818 if (empty($class = $this->$type)) {
819 return new MorphTo(
820 $this->newQuery()->setEagerLoads([]), $this, $id, null, $type, $name
821 );
822 }
823
824 // If we are not eager loading the relationship we will essentially treat this
825 // as a belongs-to style relationship since morph-to extends that class and
826 // we will pass in the appropriate values so that it behaves as expected.
827 else {
828 $class = $this->getActualClassNameForMorph($class);
829
830 $instance = new $class;
831
832 if (! $instance->getConnectionName()) {
833 $instance->setConnection($this->connection);
834 }
835
836 return new MorphTo(
837 $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
838 );
839 }
840 }
841
842 /**
843 * Retrieve the fully qualified class name from a slug.
844 *
845 * @param string $class
846 * @return string
847 */
848 public function getActualClassNameForMorph($class)
849 {
850 return Arr::get(Relation::morphMap(), $class, $class);
851 }
852
853 /**
854 * Define a one-to-many relationship.
855 *
856 * @param string $related
857 * @param string $foreignKey
858 * @param string $localKey
859 * @return \Illuminate\Database\Eloquent\Relations\HasMany
860 */
861 public function hasMany($related, $foreignKey = null, $localKey = null)
862 {
863 $foreignKey = $foreignKey ?: $this->getForeignKey();
864
865 $instance = new $related;
866 $instance->setConnection($this->getConnectionName());
867
868 $localKey = $localKey ?: $this->getKeyName();
869
870 return new HasMany($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
871 }
872
873 /**
874 * Define a has-many-through relationship.
875 *
876 * @param string $related
877 * @param string $through
878 * @param string|null $firstKey
879 * @param string|null $secondKey
880 * @param string|null $localKey
881 * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
882 */
883 public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null)
884 {
885 $through = new $through;
886
887 $firstKey = $firstKey ?: $this->getForeignKey();
888
889 $secondKey = $secondKey ?: $through->getForeignKey();
890
891 $localKey = $localKey ?: $this->getKeyName();
892
893 $instance = new $related;
894 $instance->setConnection($this->getConnectionName());
895
896 return new HasManyThrough($instance->newQuery(), $this, $through, $firstKey, $secondKey, $localKey);
897 }
898
899 /**
900 * Define a polymorphic one-to-many relationship.
901 *
902 * @param string $related
903 * @param string $name
904 * @param string $type
905 * @param string $id
906 * @param string $localKey
907 * @return \Illuminate\Database\Eloquent\Relations\MorphMany
908 */
909 public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
910 {
911 $instance = new $related;
912 $instance->setConnection($this->getConnectionName());
913
914 // Here we will gather up the morph type and ID for the relationship so that we
915 // can properly query the intermediate table of a relation. Finally, we will
916 // get the table and create the relationship instances for the developers.
917 list($type, $id) = $this->getMorphs($name, $type, $id);
918
919 $table = $instance->getTable();
920
921 $localKey = $localKey ?: $this->getKeyName();
922
923 return new MorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
924 }
925
926 /**
927 * Define a many-to-many relationship.
928 *
929 * @param string $related
930 * @param string $table
931 * @param string $foreignKey
932 * @param string $otherKey
933 * @param string $relation
934 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
935 */
936 public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
937 {
938 // If no relationship name was passed, we will pull backtraces to get the
939 // name of the calling function. We will use that function name as the
940 // title of this relation since that is a great convention to apply.
941 if (is_null($relation)) {
942 $relation = $this->getBelongsToManyCaller();
943 }
944
945 // First, we'll need to determine the foreign key and "other key" for the
946 // relationship. Once we have determined the keys we'll make the query
947 // instances as well as the relationship instances we need for this.
948 $foreignKey = $foreignKey ?: $this->getForeignKey();
949
950 $instance = new $related;
951 $instance->setConnection($this->getConnectionName());
952
953 $otherKey = $otherKey ?: $instance->getForeignKey();
954
955 // If no table name was provided, we can guess it by concatenating the two
956 // models using underscores in alphabetical order. The two model names
957 // are transformed to snake case from their default CamelCase also.
958 if (is_null($table)) {
959 $table = $this->joiningTable($related);
960 }
961
962 // Now we're ready to create a new query builder for the related model and
963 // the relationship instances for the relation. The relations will set
964 // appropriate query constraint and entirely manages the hydrations.
965 $query = $instance->newQuery();
966
967 return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation);
968 }
969
970 /**
971 * Define a polymorphic many-to-many relationship.
972 *
973 * @param string $related
974 * @param string $name
975 * @param string $table
976 * @param string $foreignKey
977 * @param string $otherKey
978 * @param bool $inverse
979 * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
980 */
981 public function morphToMany($related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
982 {
983 $caller = $this->getBelongsToManyCaller();
984
985 // First, we will need to determine the foreign key and "other key" for the
986 // relationship. Once we have determined the keys we will make the query
987 // instances, as well as the relationship instances we need for these.
988 $foreignKey = $foreignKey ?: $name.'_id';
989
990 $instance = new $related;
991 $instance->setConnection($this->getConnectionName());
992
993 $otherKey = $otherKey ?: $instance->getForeignKey();
994
995 // Now we're ready to create a new query builder for this related model and
996 // the relationship instances for this relation. This relations will set
997 // appropriate query constraints then entirely manages the hydrations.
998 $query = $instance->newQuery();
999
1000 $table = $table ?: Str::plural($name);
1001
1002 return new MorphToMany(
1003 $query, $this, $name, $table, $foreignKey,
1004 $otherKey, $caller, $inverse
1005 );
1006 }
1007
1008 /**
1009 * Define a polymorphic, inverse many-to-many relationship.
1010 *
1011 * @param string $related
1012 * @param string $name
1013 * @param string $table
1014 * @param string $foreignKey
1015 * @param string $otherKey
1016 * @return \Illuminate\Database\Eloquent\Relations\MorphToMany
1017 */
1018 public function morphedByMany($related, $name, $table = null, $foreignKey = null, $otherKey = null)
1019 {
1020 $foreignKey = $foreignKey ?: $this->getForeignKey();
1021
1022 // For the inverse of the polymorphic many-to-many relations, we will change
1023 // the way we determine the foreign and other keys, as it is the opposite
1024 // of the morph-to-many method since we're figuring out these inverses.
1025 $otherKey = $otherKey ?: $name.'_id';
1026
1027 return $this->morphToMany($related, $name, $table, $foreignKey, $otherKey, true);
1028 }
1029
1030 /**
1031 * Get the relationship name of the belongs to many.
1032 *
1033 * @return string
1034 */
1035 protected function getBelongsToManyCaller()
1036 {
1037 $self = __FUNCTION__;
1038
1039 $caller = Arr::first(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), function ($trace) use ($self) {
1040 $caller = $trace['function'];
1041
1042 return ! in_array($caller, Model::$manyMethods) && $caller != $self;
1043 });
1044
1045 return ! is_null($caller) ? $caller['function'] : null;
1046 }
1047
1048 /**
1049 * Get the joining table name for a many-to-many relation.
1050 *
1051 * @param string $related
1052 * @return string
1053 */
1054 public function joiningTable($related)
1055 {
1056 // The joining table name, by convention, is simply the snake cased models
1057 // sorted alphabetically and concatenated with an underscore, so we can
1058 // just sort the models and join them together to get the table name.
1059 $base = Str::snake(class_basename($this));
1060
1061 $related = Str::snake(class_basename($related));
1062
1063 $models = [$related, $base];
1064
1065 // Now that we have the model names in an array we can just sort them and
1066 // use the implode function to join them together with an underscores,
1067 // which is typically used by convention within the database system.
1068 sort($models);
1069
1070 return strtolower(implode('_', $models));
1071 }
1072
1073 /**
1074 * Destroy the models for the given IDs.
1075 *
1076 * @param array|int $ids
1077 * @return int
1078 */
1079 public static function destroy($ids)
1080 {
1081 // We'll initialize a count here so we will return the total number of deletes
1082 // for the operation. The developers can then check this number as a boolean
1083 // type value or get this total count of records deleted for logging, etc.
1084 $count = 0;
1085
1086 $ids = is_array($ids) ? $ids : func_get_args();
1087
1088 $instance = new static;
1089
1090 // We will actually pull the models from the database table and call delete on
1091 // each of them individually so that their events get fired properly with a
1092 // correct set of attributes in case the developers wants to check these.
1093 $key = $instance->getKeyName();
1094
1095 foreach ($instance->whereIn($key, $ids)->get() as $model) {
1096 if ($model->delete()) {
1097 $count++;
1098 }
1099 }
1100
1101 return $count;
1102 }
1103
1104 /**
1105 * Delete the model from the database.
1106 *
1107 * @return bool|null
1108 *
1109 * @throws \Exception
1110 */
1111 public function delete()
1112 {
1113 if (is_null($this->getKeyName())) {
1114 throw new Exception('No primary key defined on model.');
1115 }
1116
1117 if ($this->exists) {
1118 if ($this->fireModelEvent('deleting') === false) {
1119 return false;
1120 }
1121
1122 // Here, we'll touch the owning models, verifying these timestamps get updated
1123 // for the models. This will allow any caching to get broken on the parents
1124 // by the timestamp. Then we will go ahead and delete the model instance.
1125 $this->touchOwners();
1126
1127 $this->performDeleteOnModel();
1128
1129 $this->exists = false;
1130
1131 // Once the model has been deleted, we will fire off the deleted event so that
1132 // the developers may hook into post-delete operations. We will then return
1133 // a boolean true as the delete is presumably successful on the database.
1134 $this->fireModelEvent('deleted', false);
1135
1136 return true;
1137 }
1138 }
1139
1140 /**
1141 * Force a hard delete on a soft deleted model.
1142 *
1143 * This method protects developers from running forceDelete when trait is missing.
1144 *
1145 * @return bool|null
1146 */
1147 public function forceDelete()
1148 {
1149 return $this->delete();
1150 }
1151
1152 /**
1153 * Perform the actual delete query on this model instance.
1154 *
1155 * @return void
1156 */
1157 protected function performDeleteOnModel()
1158 {
1159 $this->setKeysForSaveQuery($this->newQueryWithoutScopes())->delete();
1160 }
1161
1162 /**
1163 * Register a saving model event with the dispatcher.
1164 *
1165 * @param \Closure|string $callback
1166 * @param int $priority
1167 * @return void
1168 */
1169 public static function saving($callback, $priority = 0)
1170 {
1171 static::registerModelEvent('saving', $callback, $priority);
1172 }
1173
1174 /**
1175 * Register a saved model event with the dispatcher.
1176 *
1177 * @param \Closure|string $callback
1178 * @param int $priority
1179 * @return void
1180 */
1181 public static function saved($callback, $priority = 0)
1182 {
1183 static::registerModelEvent('saved', $callback, $priority);
1184 }
1185
1186 /**
1187 * Register an updating model event with the dispatcher.
1188 *
1189 * @param \Closure|string $callback
1190 * @param int $priority
1191 * @return void
1192 */
1193 public static function updating($callback, $priority = 0)
1194 {
1195 static::registerModelEvent('updating', $callback, $priority);
1196 }
1197
1198 /**
1199 * Register an updated model event with the dispatcher.
1200 *
1201 * @param \Closure|string $callback
1202 * @param int $priority
1203 * @return void
1204 */
1205 public static function updated($callback, $priority = 0)
1206 {
1207 static::registerModelEvent('updated', $callback, $priority);
1208 }
1209
1210 /**
1211 * Register a creating model event with the dispatcher.
1212 *
1213 * @param \Closure|string $callback
1214 * @param int $priority
1215 * @return void
1216 */
1217 public static function creating($callback, $priority = 0)
1218 {
1219 static::registerModelEvent('creating', $callback, $priority);
1220 }
1221
1222 /**
1223 * Register a created model event with the dispatcher.
1224 *
1225 * @param \Closure|string $callback
1226 * @param int $priority
1227 * @return void
1228 */
1229 public static function created($callback, $priority = 0)
1230 {
1231 static::registerModelEvent('created', $callback, $priority);
1232 }
1233
1234 /**
1235 * Register a deleting model event with the dispatcher.
1236 *
1237 * @param \Closure|string $callback
1238 * @param int $priority
1239 * @return void
1240 */
1241 public static function deleting($callback, $priority = 0)
1242 {
1243 static::registerModelEvent('deleting', $callback, $priority);
1244 }
1245
1246 /**
1247 * Register a deleted model event with the dispatcher.
1248 *
1249 * @param \Closure|string $callback
1250 * @param int $priority
1251 * @return void
1252 */
1253 public static function deleted($callback, $priority = 0)
1254 {
1255 static::registerModelEvent('deleted', $callback, $priority);
1256 }
1257
1258 /**
1259 * Remove all of the event listeners for the model.
1260 *
1261 * @return void
1262 */
1263 public static function flushEventListeners()
1264 {
1265 if (! isset(static::$dispatcher)) {
1266 return;
1267 }
1268
1269 $instance = new static;
1270
1271 foreach ($instance->getObservableEvents() as $event) {
1272 static::$dispatcher->forget("eloquent.{$event}: ".static::class);
1273 }
1274 }
1275
1276 /**
1277 * Register a model event with the dispatcher.
1278 *
1279 * @param string $event
1280 * @param \Closure|string $callback
1281 * @param int $priority
1282 * @return void
1283 */
1284 protected static function registerModelEvent($event, $callback, $priority = 0)
1285 {
1286 if (isset(static::$dispatcher)) {
1287 $name = static::class;
1288
1289 static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback, $priority);
1290 }
1291 }
1292
1293 /**
1294 * Get the observable event names.
1295 *
1296 * @return array
1297 */
1298 public function getObservableEvents()
1299 {
1300 return array_merge(
1301 [
1302 'creating', 'created', 'updating', 'updated',
1303 'deleting', 'deleted', 'saving', 'saved',
1304 'restoring', 'restored',
1305 ],
1306 $this->observables
1307 );
1308 }
1309
1310 /**
1311 * Set the observable event names.
1312 *
1313 * @param array $observables
1314 * @return $this
1315 */
1316 public function setObservableEvents(array $observables)
1317 {
1318 $this->observables = $observables;
1319
1320 return $this;
1321 }
1322
1323 /**
1324 * Add an observable event name.
1325 *
1326 * @param array|mixed $observables
1327 * @return void
1328 */
1329 public function addObservableEvents($observables)
1330 {
1331 $observables = is_array($observables) ? $observables : func_get_args();
1332
1333 $this->observables = array_unique(array_merge($this->observables, $observables));
1334 }
1335
1336 /**
1337 * Remove an observable event name.
1338 *
1339 * @param array|mixed $observables
1340 * @return void
1341 */
1342 public function removeObservableEvents($observables)
1343 {
1344 $observables = is_array($observables) ? $observables : func_get_args();
1345
1346 $this->observables = array_diff($this->observables, $observables);
1347 }
1348
1349 /**
1350 * Increment a column's value by a given amount.
1351 *
1352 * @param string $column
1353 * @param int $amount
1354 * @param array $extra
1355 * @return int
1356 */
1357 protected function increment($column, $amount = 1, array $extra = [])
1358 {
1359 return $this->incrementOrDecrement($column, $amount, $extra, 'increment');
1360 }
1361
1362 /**
1363 * Decrement a column's value by a given amount.
1364 *
1365 * @param string $column
1366 * @param int $amount
1367 * @param array $extra
1368 * @return int
1369 */
1370 protected function decrement($column, $amount = 1, array $extra = [])
1371 {
1372 return $this->incrementOrDecrement($column, $amount, $extra, 'decrement');
1373 }
1374
1375 /**
1376 * Run the increment or decrement method on the model.
1377 *
1378 * @param string $column
1379 * @param int $amount
1380 * @param array $extra
1381 * @param string $method
1382 * @return int
1383 */
1384 protected function incrementOrDecrement($column, $amount, $extra, $method)
1385 {
1386 $query = $this->newQuery();
1387
1388 if (! $this->exists) {
1389 return $query->{$method}($column, $amount, $extra);
1390 }
1391
1392 $this->incrementOrDecrementAttributeValue($column, $amount, $method);
1393
1394 return $query->where($this->getKeyName(), $this->getKey())->{$method}($column, $amount, $extra);
1395 }
1396
1397 /**
1398 * Increment the underlying attribute value and sync with original.
1399 *
1400 * @param string $column
1401 * @param int $amount
1402 * @param string $method
1403 * @return void
1404 */
1405 protected function incrementOrDecrementAttributeValue($column, $amount, $method)
1406 {
1407 $this->{$column} = $this->{$column} + ($method == 'increment' ? $amount : $amount * -1);
1408
1409 $this->syncOriginalAttribute($column);
1410 }
1411
1412 /**
1413 * Update the model in the database.
1414 *
1415 * @param array $attributes
1416 * @param array $options
1417 * @return bool
1418 */
1419 public function update(array $attributes = [], array $options = [])
1420 {
1421 if (! $this->exists) {
1422 return false;
1423 }
1424
1425 return $this->fill($attributes)->save($options);
1426 }
1427
1428 /**
1429 * Save the model and all of its relationships.
1430 *
1431 * @return bool
1432 */
1433 public function push()
1434 {
1435 if (! $this->save()) {
1436 return false;
1437 }
1438
1439 // To sync all of the relationships to the database, we will simply spin through
1440 // the relationships and save each model via this "push" method, which allows
1441 // us to recurse into all of these nested relations for the model instance.
1442 foreach ($this->relations as $models) {
1443 $models = $models instanceof Collection
1444 ? $models->all() : [$models];
1445
1446 foreach (array_filter($models) as $model) {
1447 if (! $model->push()) {
1448 return false;
1449 }
1450 }
1451 }
1452
1453 return true;
1454 }
1455
1456 /**
1457 * Save the model to the database.
1458 *
1459 * @param array $options
1460 * @return bool
1461 */
1462 public function save(array $options = [])
1463 {
1464 $query = $this->newQueryWithoutScopes();
1465
1466 // If the "saving" event returns false we'll bail out of the save and return
1467 // false, indicating that the save failed. This provides a chance for any
1468 // listeners to cancel save operations if validations fail or whatever.
1469 if ($this->fireModelEvent('saving') === false) {
1470 return false;
1471 }
1472
1473 // If the model already exists in the database we can just update our record
1474 // that is already in this database using the current IDs in this "where"
1475 // clause to only update this model. Otherwise, we'll just insert them.
1476 if ($this->exists) {
1477 $saved = $this->isDirty() ?
1478 $this->performUpdate($query) : true;
1479 }
1480
1481 // If the model is brand new, we'll insert it into our database and set the
1482 // ID attribute on the model to the value of the newly inserted row's ID
1483 // which is typically an auto-increment value managed by the database.
1484 else {
1485 $saved = $this->performInsert($query);
1486 }
1487
1488 if ($saved) {
1489 $this->finishSave($options);
1490 }
1491
1492 return $saved;
1493 }
1494
1495 /**
1496 * Save the model to the database using transaction.
1497 *
1498 * @param array $options
1499 * @return bool
1500 *
1501 * @throws \Throwable
1502 */
1503 public function saveOrFail(array $options = [])
1504 {
1505 return $this->getConnection()->transaction(function () use ($options) {
1506 return $this->save($options);
1507 });
1508 }
1509
1510 /**
1511 * Finish processing on a successful save operation.
1512 *
1513 * @param array $options
1514 * @return void
1515 */
1516 protected function finishSave(array $options)
1517 {
1518 $this->fireModelEvent('saved', false);
1519
1520 $this->syncOriginal();
1521
1522 if (Arr::get($options, 'touch', true)) {
1523 $this->touchOwners();
1524 }
1525 }
1526
1527 /**
1528 * Perform a model update operation.
1529 *
1530 * @param \Illuminate\Database\Eloquent\Builder $query
1531 * @return bool
1532 */
1533 protected function performUpdate(Builder $query)
1534 {
1535 // If the updating event returns false, we will cancel the update operation so
1536 // developers can hook Validation systems into their models and cancel this
1537 // operation if the model does not pass validation. Otherwise, we update.
1538 if ($this->fireModelEvent('updating') === false) {
1539 return false;
1540 }
1541
1542 // First we need to create a fresh query instance and touch the creation and
1543 // update timestamp on the model which are maintained by us for developer
1544 // convenience. Then we will just continue saving the model instances.
1545 if ($this->timestamps) {
1546 $this->updateTimestamps();
1547 }
1548
1549 // Once we have run the update operation, we will fire the "updated" event for
1550 // this model instance. This will allow developers to hook into these after
1551 // models are updated, giving them a chance to do any special processing.
1552 $dirty = $this->getDirty();
1553
1554 if (count($dirty) > 0) {
1555 $this->setKeysForSaveQuery($query)->update($dirty);
1556
1557 $this->fireModelEvent('updated', false);
1558 }
1559
1560 return true;
1561 }
1562
1563 /**
1564 * Perform a model insert operation.
1565 *
1566 * @param \Illuminate\Database\Eloquent\Builder $query
1567 * @return bool
1568 */
1569 protected function performInsert(Builder $query)
1570 {
1571 if ($this->fireModelEvent('creating') === false) {
1572 return false;
1573 }
1574
1575 // First we'll need to create a fresh query instance and touch the creation and
1576 // update timestamps on this model, which are maintained by us for developer
1577 // convenience. After, we will just continue saving these model instances.
1578 if ($this->timestamps) {
1579 $this->updateTimestamps();
1580 }
1581
1582 // If the model has an incrementing key, we can use the "insertGetId" method on
1583 // the query builder, which will give us back the final inserted ID for this
1584 // table from the database. Not all tables have to be incrementing though.
1585 $attributes = $this->attributes;
1586
1587 if ($this->getIncrementing()) {
1588 $this->insertAndSetId($query, $attributes);
1589 }
1590
1591 // If the table isn't incrementing we'll simply insert these attributes as they
1592 // are. These attribute arrays must contain an "id" column previously placed
1593 // there by the developer as the manually determined key for these models.
1594 else {
1595 $query->insert($attributes);
1596 }
1597
1598 // We will go ahead and set the exists property to true, so that it is set when
1599 // the created event is fired, just in case the developer tries to update it
1600 // during the event. This will allow them to do so and run an update here.
1601 $this->exists = true;
1602
1603 $this->wasRecentlyCreated = true;
1604
1605 $this->fireModelEvent('created', false);
1606
1607 return true;
1608 }
1609
1610 /**
1611 * Insert the given attributes and set the ID on the model.
1612 *
1613 * @param \Illuminate\Database\Eloquent\Builder $query
1614 * @param array $attributes
1615 * @return void
1616 */
1617 protected function insertAndSetId(Builder $query, $attributes)
1618 {
1619 $id = $query->insertGetId($attributes, $keyName = $this->getKeyName());
1620
1621 $this->setAttribute($keyName, $id);
1622 }
1623
1624 /**
1625 * Touch the owning relations of the model.
1626 *
1627 * @return void
1628 */
1629 public function touchOwners()
1630 {
1631 foreach ($this->touches as $relation) {
1632 $this->$relation()->touch();
1633
1634 if ($this->$relation instanceof self) {
1635 $this->$relation->fireModelEvent('saved', false);
1636
1637 $this->$relation->touchOwners();
1638 } elseif ($this->$relation instanceof Collection) {
1639 $this->$relation->each(function (Model $relation) {
1640 $relation->touchOwners();
1641 });
1642 }
1643 }
1644 }
1645
1646 /**
1647 * Determine if the model touches a given relation.
1648 *
1649 * @param string $relation
1650 * @return bool
1651 */
1652 public function touches($relation)
1653 {
1654 return in_array($relation, $this->touches);
1655 }
1656
1657 /**
1658 * Fire the given event for the model.
1659 *
1660 * @param string $event
1661 * @param bool $halt
1662 * @return mixed
1663 */
1664 protected function fireModelEvent($event, $halt = true)
1665 {
1666 if (! isset(static::$dispatcher)) {
1667 return true;
1668 }
1669
1670 // We will append the names of the class to the event to distinguish it from
1671 // other model events that are fired, allowing us to listen on each model
1672 // event set individually instead of catching event for all the models.
1673 $event = "eloquent.{$event}: ".static::class;
1674
1675 $method = $halt ? 'until' : 'fire';
1676
1677 return static::$dispatcher->$method($event, $this);
1678 }
1679
1680 /**
1681 * Set the keys for a save update query.
1682 *
1683 * @param \Illuminate\Database\Eloquent\Builder $query
1684 * @return \Illuminate\Database\Eloquent\Builder
1685 */
1686 protected function setKeysForSaveQuery(Builder $query)
1687 {
1688 $query->where($this->getKeyName(), '=', $this->getKeyForSaveQuery());
1689
1690 return $query;
1691 }
1692
1693 /**
1694 * Get the primary key value for a save query.
1695 *
1696 * @return mixed
1697 */
1698 protected function getKeyForSaveQuery()
1699 {
1700 if (isset($this->original[$this->getKeyName()])) {
1701 return $this->original[$this->getKeyName()];
1702 }
1703
1704 return $this->getAttribute($this->getKeyName());
1705 }
1706
1707 /**
1708 * Update the model's update timestamp.
1709 *
1710 * @return bool
1711 */
1712 public function touch()
1713 {
1714 if (! $this->timestamps) {
1715 return false;
1716 }
1717
1718 $this->updateTimestamps();
1719
1720 return $this->save();
1721 }
1722
1723 /**
1724 * Update the creation and update timestamps.
1725 *
1726 * @return void
1727 */
1728 protected function updateTimestamps()
1729 {
1730 $time = $this->freshTimestamp();
1731
1732 if (! $this->isDirty(static::UPDATED_AT)) {
1733 $this->setUpdatedAt($time);
1734 }
1735
1736 if (! $this->exists && ! $this->isDirty(static::CREATED_AT)) {
1737 $this->setCreatedAt($time);
1738 }
1739 }
1740
1741 /**
1742 * Set the value of the "created at" attribute.
1743 *
1744 * @param mixed $value
1745 * @return $this
1746 */
1747 public function setCreatedAt($value)
1748 {
1749 $this->{static::CREATED_AT} = $value;
1750
1751 return $this;
1752 }
1753
1754 /**
1755 * Set the value of the "updated at" attribute.
1756 *
1757 * @param mixed $value
1758 * @return $this
1759 */
1760 public function setUpdatedAt($value)
1761 {
1762 $this->{static::UPDATED_AT} = $value;
1763
1764 return $this;
1765 }
1766
1767 /**
1768 * Get the name of the "created at" column.
1769 *
1770 * @return string
1771 */
1772 public function getCreatedAtColumn()
1773 {
1774 return static::CREATED_AT;
1775 }
1776
1777 /**
1778 * Get the name of the "updated at" column.
1779 *
1780 * @return string
1781 */
1782 public function getUpdatedAtColumn()
1783 {
1784 return static::UPDATED_AT;
1785 }
1786
1787 /**
1788 * Get a fresh timestamp for the model.
1789 *
1790 * @return \Carbon\Carbon
1791 */
1792 public function freshTimestamp()
1793 {
1794 return new Carbon;
1795 }
1796
1797 /**
1798 * Get a fresh timestamp for the model.
1799 *
1800 * @return string
1801 */
1802 public function freshTimestampString()
1803 {
1804 return $this->fromDateTime($this->freshTimestamp());
1805 }
1806
1807 /**
1808 * Get a new query builder for the model's table.
1809 *
1810 * @return \Illuminate\Database\Eloquent\Builder
1811 */
1812 public function newQuery()
1813 {
1814 $builder = $this->newQueryWithoutScopes();
1815
1816 foreach ($this->getGlobalScopes() as $identifier => $scope) {
1817 $builder->withGlobalScope($identifier, $scope);
1818 }
1819
1820 return $builder;
1821 }
1822
1823 /**
1824 * Get a new query instance without a given scope.
1825 *
1826 * @param \Illuminate\Database\Eloquent\Scope|string $scope
1827 * @return \Illuminate\Database\Eloquent\Builder
1828 */
1829 public function newQueryWithoutScope($scope)
1830 {
1831 $builder = $this->newQuery();
1832
1833 return $builder->withoutGlobalScope($scope);
1834 }
1835
1836 /**
1837 * Get a new query builder that doesn't have any global scopes.
1838 *
1839 * @return \Illuminate\Database\Eloquent\Builder|static
1840 */
1841 public function newQueryWithoutScopes()
1842 {
1843 $builder = $this->newEloquentBuilder(
1844 $this->newBaseQueryBuilder()
1845 );
1846
1847 // Once we have the query builders, we will set the model instances so the
1848 // builder can easily access any information it may need from the model
1849 // while it is constructing and executing various queries against it.
1850 return $builder->setModel($this)->with($this->with);
1851 }
1852
1853 /**
1854 * Create a new Eloquent query builder for the model.
1855 *
1856 * @param \Illuminate\Database\Query\Builder $query
1857 * @return \Illuminate\Database\Eloquent\Builder|static
1858 */
1859 public function newEloquentBuilder($query)
1860 {
1861 return new Builder($query);
1862 }
1863
1864 /**
1865 * Get a new query builder instance for the connection.
1866 *
1867 * @return \Illuminate\Database\Query\Builder
1868 */
1869 protected function newBaseQueryBuilder()
1870 {
1871 $conn = $this->getConnection();
1872
1873 $grammar = $conn->getQueryGrammar();
1874
1875 return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
1876 }
1877
1878 /**
1879 * Create a new Eloquent Collection instance.
1880 *
1881 * @param array $models
1882 * @return \Illuminate\Database\Eloquent\Collection
1883 */
1884 public function newCollection(array $models = [])
1885 {
1886 return new Collection($models);
1887 }
1888
1889 /**
1890 * Create a new pivot model instance.
1891 *
1892 * @param \Illuminate\Database\Eloquent\Model $parent
1893 * @param array $attributes
1894 * @param string $table
1895 * @param bool $exists
1896 * @return \Illuminate\Database\Eloquent\Relations\Pivot
1897 */
1898 public function newPivot(Model $parent, array $attributes, $table, $exists)
1899 {
1900 return new Pivot($parent, $attributes, $table, $exists);
1901 }
1902
1903 /**
1904 * Get the table associated with the model.
1905 *
1906 * @return string
1907 */
1908 public function getTable()
1909 {
1910 if (isset($this->table)) {
1911 return $this->table;
1912 }
1913
1914 return str_replace('\\', '', Str::snake(Str::plural(class_basename($this))));
1915 }
1916
1917 /**
1918 * Set the table associated with the model.
1919 *
1920 * @param string $table
1921 * @return $this
1922 */
1923 public function setTable($table)
1924 {
1925 $this->table = $table;
1926
1927 return $this;
1928 }
1929
1930 /**
1931 * Get the value of the model's primary key.
1932 *
1933 * @return mixed
1934 */
1935 public function getKey()
1936 {
1937 return $this->getAttribute($this->getKeyName());
1938 }
1939
1940 /**
1941 * Get the queueable identity for the entity.
1942 *
1943 * @return mixed
1944 */
1945 public function getQueueableId()
1946 {
1947 return $this->getKey();
1948 }
1949
1950 /**
1951 * Get the primary key for the model.
1952 *
1953 * @return string
1954 */
1955 public function getKeyName()
1956 {
1957 return $this->primaryKey;
1958 }
1959
1960 /**
1961 * Set the primary key for the model.
1962 *
1963 * @param string $key
1964 * @return $this
1965 */
1966 public function setKeyName($key)
1967 {
1968 $this->primaryKey = $key;
1969
1970 return $this;
1971 }
1972
1973 /**
1974 * Get the table qualified key name.
1975 *
1976 * @return string
1977 */
1978 public function getQualifiedKeyName()
1979 {
1980 return $this->getTable().'.'.$this->getKeyName();
1981 }
1982
1983 /**
1984 * Get the auto incrementing key type.
1985 *
1986 * @return string
1987 */
1988 public function getKeyType()
1989 {
1990 return $this->keyType;
1991 }
1992
1993 /**
1994 * Get the value of the model's route key.
1995 *
1996 * @return mixed
1997 */
1998 public function getRouteKey()
1999 {
2000 return $this->getAttribute($this->getRouteKeyName());
2001 }
2002
2003 /**
2004 * Get the route key for the model.
2005 *
2006 * @return string
2007 */
2008 public function getRouteKeyName()
2009 {
2010 return $this->getKeyName();
2011 }
2012
2013 /**
2014 * Determine if the model uses timestamps.
2015 *
2016 * @return bool
2017 */
2018 public function usesTimestamps()
2019 {
2020 return $this->timestamps;
2021 }
2022
2023 /**
2024 * Get the polymorphic relationship columns.
2025 *
2026 * @param string $name
2027 * @param string $type
2028 * @param string $id
2029 * @return array
2030 */
2031 protected function getMorphs($name, $type, $id)
2032 {
2033 $type = $type ?: $name.'_type';
2034
2035 $id = $id ?: $name.'_id';
2036
2037 return [$type, $id];
2038 }
2039
2040 /**
2041 * Get the class name for polymorphic relations.
2042 *
2043 * @return string
2044 */
2045 public function getMorphClass()
2046 {
2047 $morphMap = Relation::morphMap();
2048
2049 $class = static::class;
2050
2051 if (! empty($morphMap) && in_array($class, $morphMap)) {
2052 return array_search($class, $morphMap, true);
2053 }
2054
2055 return $class;
2056 }
2057
2058 /**
2059 * Get the number of models to return per page.
2060 *
2061 * @return int
2062 */
2063 public function getPerPage()
2064 {
2065 return $this->perPage;
2066 }
2067
2068 /**
2069 * Set the number of models to return per page.
2070 *
2071 * @param int $perPage
2072 * @return $this
2073 */
2074 public function setPerPage($perPage)
2075 {
2076 $this->perPage = $perPage;
2077
2078 return $this;
2079 }
2080
2081 /**
2082 * Get the default foreign key name for the model.
2083 *
2084 * @return string
2085 */
2086 public function getForeignKey()
2087 {
2088 return Str::snake(class_basename($this)).'_id';
2089 }
2090
2091 /**
2092 * Get the hidden attributes for the model.
2093 *
2094 * @return array
2095 */
2096 public function getHidden()
2097 {
2098 return $this->hidden;
2099 }
2100
2101 /**
2102 * Set the hidden attributes for the model.
2103 *
2104 * @param array $hidden
2105 * @return $this
2106 */
2107 public function setHidden(array $hidden)
2108 {
2109 $this->hidden = $hidden;
2110
2111 return $this;
2112 }
2113
2114 /**
2115 * Add hidden attributes for the model.
2116 *
2117 * @param array|string|null $attributes
2118 * @return void
2119 */
2120 public function addHidden($attributes = null)
2121 {
2122 $attributes = is_array($attributes) ? $attributes : func_get_args();
2123
2124 $this->hidden = array_merge($this->hidden, $attributes);
2125 }
2126
2127 /**
2128 * Make the given, typically hidden, attributes visible.
2129 *
2130 * @param array|string $attributes
2131 * @return $this
2132 */
2133 public function makeVisible($attributes)
2134 {
2135 $this->hidden = array_diff($this->hidden, (array) $attributes);
2136
2137 if (! empty($this->visible)) {
2138 $this->addVisible($attributes);
2139 }
2140
2141 return $this;
2142 }
2143
2144 /**
2145 * Make the given, typically visible, attributes hidden.
2146 *
2147 * @param array|string $attributes
2148 * @return $this
2149 */
2150 public function makeHidden($attributes)
2151 {
2152 $attributes = (array) $attributes;
2153
2154 $this->visible = array_diff($this->visible, $attributes);
2155
2156 $this->hidden = array_unique(array_merge($this->hidden, $attributes));
2157
2158 return $this;
2159 }
2160
2161 /**
2162 * Get the visible attributes for the model.
2163 *
2164 * @return array
2165 */
2166 public function getVisible()
2167 {
2168 return $this->visible;
2169 }
2170
2171 /**
2172 * Set the visible attributes for the model.
2173 *
2174 * @param array $visible
2175 * @return $this
2176 */
2177 public function setVisible(array $visible)
2178 {
2179 $this->visible = $visible;
2180
2181 return $this;
2182 }
2183
2184 /**
2185 * Add visible attributes for the model.
2186 *
2187 * @param array|string|null $attributes
2188 * @return void
2189 */
2190 public function addVisible($attributes = null)
2191 {
2192 $attributes = is_array($attributes) ? $attributes : func_get_args();
2193
2194 $this->visible = array_merge($this->visible, $attributes);
2195 }
2196
2197 /**
2198 * Set the accessors to append to model arrays.
2199 *
2200 * @param array $appends
2201 * @return $this
2202 */
2203 public function setAppends(array $appends)
2204 {
2205 $this->appends = $appends;
2206
2207 return $this;
2208 }
2209
2210 /**
2211 * Get the fillable attributes for the model.
2212 *
2213 * @return array
2214 */
2215 public function getFillable()
2216 {
2217 return $this->fillable;
2218 }
2219
2220 /**
2221 * Set the fillable attributes for the model.
2222 *
2223 * @param array $fillable
2224 * @return $this
2225 */
2226 public function fillable(array $fillable)
2227 {
2228 $this->fillable = $fillable;
2229
2230 return $this;
2231 }
2232
2233 /**
2234 * Get the guarded attributes for the model.
2235 *
2236 * @return array
2237 */
2238 public function getGuarded()
2239 {
2240 return $this->guarded;
2241 }
2242
2243 /**
2244 * Set the guarded attributes for the model.
2245 *
2246 * @param array $guarded
2247 * @return $this
2248 */
2249 public function guard(array $guarded)
2250 {
2251 $this->guarded = $guarded;
2252
2253 return $this;
2254 }
2255
2256 /**
2257 * Disable all mass assignable restrictions.
2258 *
2259 * @param bool $state
2260 * @return void
2261 */
2262 public static function unguard($state = true)
2263 {
2264 static::$unguarded = $state;
2265 }
2266
2267 /**
2268 * Enable the mass assignment restrictions.
2269 *
2270 * @return void
2271 */
2272 public static function reguard()
2273 {
2274 static::$unguarded = false;
2275 }
2276
2277 /**
2278 * Determine if current state is "unguarded".
2279 *
2280 * @return bool
2281 */
2282 public static function isUnguarded()
2283 {
2284 return static::$unguarded;
2285 }
2286
2287 /**
2288 * Run the given callable while being unguarded.
2289 *
2290 * @param callable $callback
2291 * @return mixed
2292 */
2293 public static function unguarded(callable $callback)
2294 {
2295 if (static::$unguarded) {
2296 return $callback();
2297 }
2298
2299 static::unguard();
2300
2301 try {
2302 return $callback();
2303 } finally {
2304 static::reguard();
2305 }
2306 }
2307
2308 /**
2309 * Determine if the given attribute may be mass assigned.
2310 *
2311 * @param string $key
2312 * @return bool
2313 */
2314 public function isFillable($key)
2315 {
2316 if (static::$unguarded) {
2317 return true;
2318 }
2319
2320 // If the key is in the "fillable" array, we can of course assume that it's
2321 // a fillable attribute. Otherwise, we will check the guarded array when
2322 // we need to determine if the attribute is black-listed on the model.
2323 if (in_array($key, $this->getFillable())) {
2324 return true;
2325 }
2326
2327 if ($this->isGuarded($key)) {
2328 return false;
2329 }
2330
2331 return empty($this->getFillable()) && ! Str::startsWith($key, '_');
2332 }
2333
2334 /**
2335 * Determine if the given key is guarded.
2336 *
2337 * @param string $key
2338 * @return bool
2339 */
2340 public function isGuarded($key)
2341 {
2342 return in_array($key, $this->getGuarded()) || $this->getGuarded() == ['*'];
2343 }
2344
2345 /**
2346 * Determine if the model is totally guarded.
2347 *
2348 * @return bool
2349 */
2350 public function totallyGuarded()
2351 {
2352 return count($this->getFillable()) == 0 && $this->getGuarded() == ['*'];
2353 }
2354
2355 /**
2356 * Remove the table name from a given key.
2357 *
2358 * @param string $key
2359 * @return string
2360 */
2361 protected function removeTableFromKey($key)
2362 {
2363 if (! Str::contains($key, '.')) {
2364 return $key;
2365 }
2366
2367 return last(explode('.', $key));
2368 }
2369
2370 /**
2371 * Get the relationships that are touched on save.
2372 *
2373 * @return array
2374 */
2375 public function getTouchedRelations()
2376 {
2377 return $this->touches;
2378 }
2379
2380 /**
2381 * Set the relationships that are touched on save.
2382 *
2383 * @param array $touches
2384 * @return $this
2385 */
2386 public function setTouchedRelations(array $touches)
2387 {
2388 $this->touches = $touches;
2389
2390 return $this;
2391 }
2392
2393 /**
2394 * Get the value indicating whether the IDs are incrementing.
2395 *
2396 * @return bool
2397 */
2398 public function getIncrementing()
2399 {
2400 return $this->incrementing;
2401 }
2402
2403 /**
2404 * Set whether IDs are incrementing.
2405 *
2406 * @param bool $value
2407 * @return $this
2408 */
2409 public function setIncrementing($value)
2410 {
2411 $this->incrementing = $value;
2412
2413 return $this;
2414 }
2415
2416 /**
2417 * Convert the model instance to JSON.
2418 *
2419 * @param int $options
2420 * @return string
2421 */
2422 public function toJson($options = 0)
2423 {
2424 return json_encode($this->jsonSerialize(), $options);
2425 }
2426
2427 /**
2428 * Convert the object into something JSON serializable.
2429 *
2430 * @return array
2431 */
2432 public function jsonSerialize()
2433 {
2434 return $this->toArray();
2435 }
2436
2437 /**
2438 * Convert the model instance to an array.
2439 *
2440 * @return array
2441 */
2442 public function toArray()
2443 {
2444 $attributes = $this->attributesToArray();
2445
2446 return array_merge($attributes, $this->relationsToArray());
2447 }
2448
2449 /**
2450 * Convert the model's attributes to an array.
2451 *
2452 * @return array
2453 */
2454 public function attributesToArray()
2455 {
2456 $attributes = $this->getArrayableAttributes();
2457
2458 // If an attribute is a date, we will cast it to a string after converting it
2459 // to a DateTime / Carbon instance. This is so we will get some consistent
2460 // formatting while accessing attributes vs. arraying / JSONing a model.
2461 foreach ($this->getDates() as $key) {
2462 if (! isset($attributes[$key])) {
2463 continue;
2464 }
2465
2466 $attributes[$key] = $this->serializeDate(
2467 $this->asDateTime($attributes[$key])
2468 );
2469 }
2470
2471 $mutatedAttributes = $this->getMutatedAttributes();
2472
2473 // We want to spin through all the mutated attributes for this model and call
2474 // the mutator for the attribute. We cache off every mutated attributes so
2475 // we don't have to constantly check on attributes that actually change.
2476 foreach ($mutatedAttributes as $key) {
2477 if (! array_key_exists($key, $attributes)) {
2478 continue;
2479 }
2480
2481 $attributes[$key] = $this->mutateAttributeForArray(
2482 $key, $attributes[$key]
2483 );
2484 }
2485
2486 // Next we will handle any casts that have been setup for this model and cast
2487 // the values to their appropriate type. If the attribute has a mutator we
2488 // will not perform the cast on those attributes to avoid any confusion.
2489 foreach ($this->getCasts() as $key => $value) {
2490 if (! array_key_exists($key, $attributes) ||
2491 in_array($key, $mutatedAttributes)) {
2492 continue;
2493 }
2494
2495 $attributes[$key] = $this->castAttribute(
2496 $key, $attributes[$key]
2497 );
2498
2499 if ($attributes[$key] && ($value === 'date' || $value === 'datetime')) {
2500 $attributes[$key] = $this->serializeDate($attributes[$key]);
2501 }
2502 }
2503
2504 // Here we will grab all of the appended, calculated attributes to this model
2505 // as these attributes are not really in the attributes array, but are run
2506 // when we need to array or JSON the model for convenience to the coder.
2507 foreach ($this->getArrayableAppends() as $key) {
2508 $attributes[$key] = $this->mutateAttributeForArray($key, null);
2509 }
2510
2511 return $attributes;
2512 }
2513
2514 /**
2515 * Get an attribute array of all arrayable attributes.
2516 *
2517 * @return array
2518 */
2519 protected function getArrayableAttributes()
2520 {
2521 return $this->getArrayableItems($this->attributes);
2522 }
2523
2524 /**
2525 * Get all of the appendable values that are arrayable.
2526 *
2527 * @return array
2528 */
2529 protected function getArrayableAppends()
2530 {
2531 if (! count($this->appends)) {
2532 return [];
2533 }
2534
2535 return $this->getArrayableItems(
2536 array_combine($this->appends, $this->appends)
2537 );
2538 }
2539
2540 /**
2541 * Get the model's relationships in array form.
2542 *
2543 * @return array
2544 */
2545 public function relationsToArray()
2546 {
2547 $attributes = [];
2548
2549 foreach ($this->getArrayableRelations() as $key => $value) {
2550 // If the values implements the Arrayable interface we can just call this
2551 // toArray method on the instances which will convert both models and
2552 // collections to their proper array form and we'll set the values.
2553 if ($value instanceof Arrayable) {
2554 $relation = $value->toArray();
2555 }
2556
2557 // If the value is null, we'll still go ahead and set it in this list of
2558 // attributes since null is used to represent empty relationships if
2559 // if it a has one or belongs to type relationships on the models.
2560 elseif (is_null($value)) {
2561 $relation = $value;
2562 }
2563
2564 // If the relationships snake-casing is enabled, we will snake case this
2565 // key so that the relation attribute is snake cased in this returned
2566 // array to the developers, making this consistent with attributes.
2567 if (static::$snakeAttributes) {
2568 $key = Str::snake($key);
2569 }
2570
2571 // If the relation value has been set, we will set it on this attributes
2572 // list for returning. If it was not arrayable or null, we'll not set
2573 // the value on the array because it is some type of invalid value.
2574 if (isset($relation) || is_null($value)) {
2575 $attributes[$key] = $relation;
2576 }
2577
2578 unset($relation);
2579 }
2580
2581 return $attributes;
2582 }
2583
2584 /**
2585 * Get an attribute array of all arrayable relations.
2586 *
2587 * @return array
2588 */
2589 protected function getArrayableRelations()
2590 {
2591 return $this->getArrayableItems($this->relations);
2592 }
2593
2594 /**
2595 * Get an attribute array of all arrayable values.
2596 *
2597 * @param array $values
2598 * @return array
2599 */
2600 protected function getArrayableItems(array $values)
2601 {
2602 if (count($this->getVisible()) > 0) {
2603 $values = array_intersect_key($values, array_flip($this->getVisible()));
2604 }
2605
2606 if (count($this->getHidden()) > 0) {
2607 $values = array_diff_key($values, array_flip($this->getHidden()));
2608 }
2609
2610 return $values;
2611 }
2612
2613 /**
2614 * Get an attribute from the model.
2615 *
2616 * @param string $key
2617 * @return mixed
2618 */
2619 public function getAttribute($key)
2620 {
2621 if (! $key) {
2622 return;
2623 }
2624
2625 if (array_key_exists($key, $this->attributes) || $this->hasGetMutator($key)) {
2626 return $this->getAttributeValue($key);
2627 }
2628
2629 if (method_exists(self::class, $key)) {
2630 return;
2631 }
2632
2633 return $this->getRelationValue($key);
2634 }
2635
2636 /**
2637 * Get a plain attribute (not a relationship).
2638 *
2639 * @param string $key
2640 * @return mixed
2641 */
2642 public function getAttributeValue($key)
2643 {
2644 $value = $this->getAttributeFromArray($key);
2645
2646 // If the attribute has a get mutator, we will call that then return what
2647 // it returns as the value, which is useful for transforming values on
2648 // retrieval from the model to a form that is more useful for usage.
2649 if ($this->hasGetMutator($key)) {
2650 return $this->mutateAttribute($key, $value);
2651 }
2652
2653 // If the attribute exists within the cast array, we will convert it to
2654 // an appropriate native PHP type dependant upon the associated value
2655 // given with the key in the pair. Dayle made this comment line up.
2656 if ($this->hasCast($key)) {
2657 return $this->castAttribute($key, $value);
2658 }
2659
2660 // If the attribute is listed as a date, we will convert it to a DateTime
2661 // instance on retrieval, which makes it quite convenient to work with
2662 // date fields without having to create a mutator for each property.
2663 if (in_array($key, $this->getDates()) && ! is_null($value)) {
2664 return $this->asDateTime($value);
2665 }
2666
2667 return $value;
2668 }
2669
2670 /**
2671 * Get a relationship.
2672 *
2673 * @param string $key
2674 * @return mixed
2675 */
2676 public function getRelationValue($key)
2677 {
2678 // If the key already exists in the relationships array, it just means the
2679 // relationship has already been loaded, so we'll just return it out of
2680 // here because there is no need to query within the relations twice.
2681 if ($this->relationLoaded($key)) {
2682 return $this->relations[$key];
2683 }
2684
2685 // If the "attribute" exists as a method on the model, we will just assume
2686 // it is a relationship and will load and return results from the query
2687 // and hydrate the relationship's value on the "relationships" array.
2688 if (method_exists($this, $key)) {
2689 return $this->getRelationshipFromMethod($key);
2690 }
2691 }
2692
2693 /**
2694 * Get an attribute from the $attributes array.
2695 *
2696 * @param string $key
2697 * @return mixed
2698 */
2699 protected function getAttributeFromArray($key)
2700 {
2701 if (array_key_exists($key, $this->attributes)) {
2702 return $this->attributes[$key];
2703 }
2704 }
2705
2706 /**
2707 * Get a relationship value from a method.
2708 *
2709 * @param string $method
2710 * @return mixed
2711 *
2712 * @throws \LogicException
2713 */
2714 protected function getRelationshipFromMethod($method)
2715 {
2716 $relations = $this->$method();
2717
2718 if (! $relations instanceof Relation) {
2719 throw new LogicException('Relationship method must return an object of type '
2720 .'Illuminate\Database\Eloquent\Relations\Relation');
2721 }
2722
2723 $this->setRelation($method, $results = $relations->getResults());
2724
2725 return $results;
2726 }
2727
2728 /**
2729 * Determine if a get mutator exists for an attribute.
2730 *
2731 * @param string $key
2732 * @return bool
2733 */
2734 public function hasGetMutator($key)
2735 {
2736 return method_exists($this, 'get'.Str::studly($key).'Attribute');
2737 }
2738
2739 /**
2740 * Get the value of an attribute using its mutator.
2741 *
2742 * @param string $key
2743 * @param mixed $value
2744 * @return mixed
2745 */
2746 protected function mutateAttribute($key, $value)
2747 {
2748 return $this->{'get'.Str::studly($key).'Attribute'}($value);
2749 }
2750
2751 /**
2752 * Get the value of an attribute using its mutator for array conversion.
2753 *
2754 * @param string $key
2755 * @param mixed $value
2756 * @return mixed
2757 */
2758 protected function mutateAttributeForArray($key, $value)
2759 {
2760 $value = $this->mutateAttribute($key, $value);
2761
2762 return $value instanceof Arrayable ? $value->toArray() : $value;
2763 }
2764
2765 /**
2766 * Determine whether an attribute should be cast to a native type.
2767 *
2768 * @param string $key
2769 * @param array|string|null $types
2770 * @return bool
2771 */
2772 public function hasCast($key, $types = null)
2773 {
2774 if (array_key_exists($key, $this->getCasts())) {
2775 return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
2776 }
2777
2778 return false;
2779 }
2780
2781 /**
2782 * Get the casts array.
2783 *
2784 * @return array
2785 */
2786 public function getCasts()
2787 {
2788 if ($this->getIncrementing()) {
2789 return array_merge([
2790 $this->getKeyName() => $this->keyType,
2791 ], $this->casts);
2792 }
2793
2794 return $this->casts;
2795 }
2796
2797 /**
2798 * Determine whether a value is Date / DateTime castable for inbound manipulation.
2799 *
2800 * @param string $key
2801 * @return bool
2802 */
2803 protected function isDateCastable($key)
2804 {
2805 return $this->hasCast($key, ['date', 'datetime']);
2806 }
2807
2808 /**
2809 * Determine whether a value is JSON castable for inbound manipulation.
2810 *
2811 * @param string $key
2812 * @return bool
2813 */
2814 protected function isJsonCastable($key)
2815 {
2816 return $this->hasCast($key, ['array', 'json', 'object', 'collection']);
2817 }
2818
2819 /**
2820 * Get the type of cast for a model attribute.
2821 *
2822 * @param string $key
2823 * @return string
2824 */
2825 protected function getCastType($key)
2826 {
2827 return trim(strtolower($this->getCasts()[$key]));
2828 }
2829
2830 /**
2831 * Cast an attribute to a native PHP type.
2832 *
2833 * @param string $key
2834 * @param mixed $value
2835 * @return mixed
2836 */
2837 protected function castAttribute($key, $value)
2838 {
2839 if (is_null($value)) {
2840 return $value;
2841 }
2842
2843 switch ($this->getCastType($key)) {
2844 case 'int':
2845 case 'integer':
2846 return (int) $value;
2847 case 'real':
2848 case 'float':
2849 case 'double':
2850 return (float) $value;
2851 case 'string':
2852 return (string) $value;
2853 case 'bool':
2854 case 'boolean':
2855 return (bool) $value;
2856 case 'object':
2857 return $this->fromJson($value, true);
2858 case 'array':
2859 case 'json':
2860 return $this->fromJson($value);
2861 case 'collection':
2862 return new BaseCollection($this->fromJson($value));
2863 case 'date':
2864 case 'datetime':
2865 return $this->asDateTime($value);
2866 case 'timestamp':
2867 return $this->asTimeStamp($value);
2868 default:
2869 return $value;
2870 }
2871 }
2872
2873 /**
2874 * Set a given attribute on the model.
2875 *
2876 * @param string $key
2877 * @param mixed $value
2878 * @return $this
2879 */
2880 public function setAttribute($key, $value)
2881 {
2882 // First we will check for the presence of a mutator for the set operation
2883 // which simply lets the developers tweak the attribute as it is set on
2884 // the model, such as "json_encoding" an listing of data for storage.
2885 if ($this->hasSetMutator($key)) {
2886 $method = 'set'.Str::studly($key).'Attribute';
2887
2888 return $this->{$method}($value);
2889 }
2890
2891 // If an attribute is listed as a "date", we'll convert it from a DateTime
2892 // instance into a form proper for storage on the database tables using
2893 // the connection grammar's date format. We will auto set the values.
2894 elseif ($value && (in_array($key, $this->getDates()) || $this->isDateCastable($key))) {
2895 $value = $this->fromDateTime($value);
2896 }
2897
2898 if ($this->isJsonCastable($key) && ! is_null($value)) {
2899 $value = $this->asJson($value);
2900 }
2901
2902 // If this attribute contains a JSON ->, we'll set the proper value in the
2903 // attribute's underlying array. This takes care of properly nesting an
2904 // attribute in the array's value in the case of deeply nested items.
2905 if (Str::contains($key, '->')) {
2906 return $this->fillJsonAttribute($key, $value);
2907 }
2908
2909 $this->attributes[$key] = $value;
2910
2911 return $this;
2912 }
2913
2914 /**
2915 * Set a given JSON attribute on the model.
2916 *
2917 * @param string $key
2918 * @param mixed $value
2919 * @return $this
2920 */
2921 public function fillJsonAttribute($key, $value)
2922 {
2923 list($key, $path) = explode('->', $key, 2);
2924
2925 $arrayValue = isset($this->attributes[$key]) ? $this->fromJson($this->attributes[$key]) : [];
2926
2927 Arr::set($arrayValue, str_replace('->', '.', $path), $value);
2928
2929 $this->attributes[$key] = $this->asJson($arrayValue);
2930
2931 return $this;
2932 }
2933
2934 /**
2935 * Determine if a set mutator exists for an attribute.
2936 *
2937 * @param string $key
2938 * @return bool
2939 */
2940 public function hasSetMutator($key)
2941 {
2942 return method_exists($this, 'set'.Str::studly($key).'Attribute');
2943 }
2944
2945 /**
2946 * Get the attributes that should be converted to dates.
2947 *
2948 * @return array
2949 */
2950 public function getDates()
2951 {
2952 $defaults = [static::CREATED_AT, static::UPDATED_AT];
2953
2954 return $this->timestamps ? array_merge($this->dates, $defaults) : $this->dates;
2955 }
2956
2957 /**
2958 * Convert a DateTime to a storable string.
2959 *
2960 * @param \DateTime|int $value
2961 * @return string
2962 */
2963 public function fromDateTime($value)
2964 {
2965 $format = $this->getDateFormat();
2966
2967 $value = $this->asDateTime($value);
2968
2969 return $value->format($format);
2970 }
2971
2972 /**
2973 * Return a timestamp as DateTime object.
2974 *
2975 * @param mixed $value
2976 * @return \Carbon\Carbon
2977 */
2978 protected function asDateTime($value)
2979 {
2980 // If this value is already a Carbon instance, we shall just return it as is.
2981 // This prevents us having to re-instantiate a Carbon instance when we know
2982 // it already is one, which wouldn't be fulfilled by the DateTime check.
2983 if ($value instanceof Carbon) {
2984 return $value;
2985 }
2986
2987 // If the value is already a DateTime instance, we will just skip the rest of
2988 // these checks since they will be a waste of time, and hinder performance
2989 // when checking the field. We will just return the DateTime right away.
2990 if ($value instanceof DateTimeInterface) {
2991 return new Carbon(
2992 $value->format('Y-m-d H:i:s.u'), $value->getTimeZone()
2993 );
2994 }
2995
2996 // If this value is an integer, we will assume it is a UNIX timestamp's value
2997 // and format a Carbon object from this timestamp. This allows flexibility
2998 // when defining your date fields as they might be UNIX timestamps here.
2999 if (is_numeric($value)) {
3000 return Carbon::createFromTimestamp($value);
3001 }
3002
3003 // If the value is in simply year, month, day format, we will instantiate the
3004 // Carbon instances from that format. Again, this provides for simple date
3005 // fields on the database, while still supporting Carbonized conversion.
3006 if (preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value)) {
3007 return Carbon::createFromFormat('Y-m-d', $value)->startOfDay();
3008 }
3009
3010 // Finally, we will just assume this date is in the format used by default on
3011 // the database connection and use that format to create the Carbon object
3012 // that is returned back out to the developers after we convert it here.
3013 return Carbon::createFromFormat($this->getDateFormat(), $value);
3014 }
3015
3016 /**
3017 * Return a timestamp as unix timestamp.
3018 *
3019 * @param mixed $value
3020 * @return int
3021 */
3022 protected function asTimeStamp($value)
3023 {
3024 return $this->asDateTime($value)->getTimestamp();
3025 }
3026
3027 /**
3028 * Prepare a date for array / JSON serialization.
3029 *
3030 * @param \DateTimeInterface $date
3031 * @return string
3032 */
3033 protected function serializeDate(DateTimeInterface $date)
3034 {
3035 return $date->format($this->getDateFormat());
3036 }
3037
3038 /**
3039 * Get the format for database stored dates.
3040 *
3041 * @return string
3042 */
3043 protected function getDateFormat()
3044 {
3045 return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
3046 }
3047
3048 /**
3049 * Set the date format used by the model.
3050 *
3051 * @param string $format
3052 * @return $this
3053 */
3054 public function setDateFormat($format)
3055 {
3056 $this->dateFormat = $format;
3057
3058 return $this;
3059 }
3060
3061 /**
3062 * Encode the given value as JSON.
3063 *
3064 * @param mixed $value
3065 * @return string
3066 */
3067 protected function asJson($value)
3068 {
3069 return json_encode($value);
3070 }
3071
3072 /**
3073 * Decode the given JSON back into an array or object.
3074 *
3075 * @param string $value
3076 * @param bool $asObject
3077 * @return mixed
3078 */
3079 public function fromJson($value, $asObject = false)
3080 {
3081 return json_decode($value, ! $asObject);
3082 }
3083
3084 /**
3085 * Clone the model into a new, non-existing instance.
3086 *
3087 * @param array|null $except
3088 * @return \Illuminate\Database\Eloquent\Model
3089 */
3090 public function replicate(array $except = null)
3091 {
3092 $defaults = [
3093 $this->getKeyName(),
3094 $this->getCreatedAtColumn(),
3095 $this->getUpdatedAtColumn(),
3096 ];
3097
3098 $except = $except ? array_unique(array_merge($except, $defaults)) : $defaults;
3099
3100 $attributes = Arr::except($this->attributes, $except);
3101
3102 $instance = new static;
3103
3104 $instance->setRawAttributes($attributes);
3105
3106 return $instance->setRelations($this->relations);
3107 }
3108
3109 /**
3110 * Determine if two models have the same ID and belong to the same table.
3111 *
3112 * @param \Illuminate\Database\Eloquent\Model $model
3113 * @return bool
3114 */
3115 public function is(Model $model)
3116 {
3117 return $this->getKey() === $model->getKey() &&
3118 $this->getTable() === $model->getTable() &&
3119 $this->getConnectionName() === $model->getConnectionName();
3120 }
3121
3122 /**
3123 * Get all of the current attributes on the model.
3124 *
3125 * @return array
3126 */
3127 public function getAttributes()
3128 {
3129 return $this->attributes;
3130 }
3131
3132 /**
3133 * Set the array of model attributes. No checking is done.
3134 *
3135 * @param array $attributes
3136 * @param bool $sync
3137 * @return $this
3138 */
3139 public function setRawAttributes(array $attributes, $sync = false)
3140 {
3141 $this->attributes = $attributes;
3142
3143 if ($sync) {
3144 $this->syncOriginal();
3145 }
3146
3147 return $this;
3148 }
3149
3150 /**
3151 * Get the model's original attribute values.
3152 *
3153 * @param string|null $key
3154 * @param mixed $default
3155 * @return mixed|array
3156 */
3157 public function getOriginal($key = null, $default = null)
3158 {
3159 return Arr::get($this->original, $key, $default);
3160 }
3161
3162 /**
3163 * Sync the original attributes with the current.
3164 *
3165 * @return $this
3166 */
3167 public function syncOriginal()
3168 {
3169 $this->original = $this->attributes;
3170
3171 return $this;
3172 }
3173
3174 /**
3175 * Sync a single original attribute with its current value.
3176 *
3177 * @param string $attribute
3178 * @return $this
3179 */
3180 public function syncOriginalAttribute($attribute)
3181 {
3182 $this->original[$attribute] = $this->attributes[$attribute];
3183
3184 return $this;
3185 }
3186
3187 /**
3188 * Determine if the model or given attribute(s) have been modified.
3189 *
3190 * @param array|string|null $attributes
3191 * @return bool
3192 */
3193 public function isDirty($attributes = null)
3194 {
3195 $dirty = $this->getDirty();
3196
3197 if (is_null($attributes)) {
3198 return count($dirty) > 0;
3199 }
3200
3201 if (! is_array($attributes)) {
3202 $attributes = func_get_args();
3203 }
3204
3205 foreach ($attributes as $attribute) {
3206 if (array_key_exists($attribute, $dirty)) {
3207 return true;
3208 }
3209 }
3210
3211 return false;
3212 }
3213
3214 /**
3215 * Determine if the model or given attribute(s) have remained the same.
3216 *
3217 * @param array|string|null $attributes
3218 * @return bool
3219 */
3220 public function isClean($attributes = null)
3221 {
3222 return ! $this->isDirty(...func_get_args());
3223 }
3224
3225 /**
3226 * Get the attributes that have been changed since last sync.
3227 *
3228 * @return array
3229 */
3230 public function getDirty()
3231 {
3232 $dirty = [];
3233
3234 foreach ($this->attributes as $key => $value) {
3235 if (! array_key_exists($key, $this->original)) {
3236 $dirty[$key] = $value;
3237 } elseif ($value !== $this->original[$key] &&
3238 ! $this->originalIsNumericallyEquivalent($key)) {
3239 $dirty[$key] = $value;
3240 }
3241 }
3242
3243 return $dirty;
3244 }
3245
3246 /**
3247 * Determine if the new and old values for a given key are numerically equivalent.
3248 *
3249 * @param string $key
3250 * @return bool
3251 */
3252 protected function originalIsNumericallyEquivalent($key)
3253 {
3254 $current = $this->attributes[$key];
3255
3256 $original = $this->original[$key];
3257
3258 return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
3259 }
3260
3261 /**
3262 * Get all the loaded relations for the instance.
3263 *
3264 * @return array
3265 */
3266 public function getRelations()
3267 {
3268 return $this->relations;
3269 }
3270
3271 /**
3272 * Get a specified relationship.
3273 *
3274 * @param string $relation
3275 * @return mixed
3276 */
3277 public function getRelation($relation)
3278 {
3279 return $this->relations[$relation];
3280 }
3281
3282 /**
3283 * Determine if the given relation is loaded.
3284 *
3285 * @param string $key
3286 * @return bool
3287 */
3288 public function relationLoaded($key)
3289 {
3290 return array_key_exists($key, $this->relations);
3291 }
3292
3293 /**
3294 * Set the specific relationship in the model.
3295 *
3296 * @param string $relation
3297 * @param mixed $value
3298 * @return $this
3299 */
3300 public function setRelation($relation, $value)
3301 {
3302 $this->relations[$relation] = $value;
3303
3304 return $this;
3305 }
3306
3307 /**
3308 * Set the entire relations array on the model.
3309 *
3310 * @param array $relations
3311 * @return $this
3312 */
3313 public function setRelations(array $relations)
3314 {
3315 $this->relations = $relations;
3316
3317 return $this;
3318 }
3319
3320 /**
3321 * Get the database connection for the model.
3322 *
3323 * @return \Illuminate\Database\Connection
3324 */
3325 public function getConnection()
3326 {
3327 return static::resolveConnection($this->getConnectionName());
3328 }
3329
3330 /**
3331 * Get the current connection name for the model.
3332 *
3333 * @return string
3334 */
3335 public function getConnectionName()
3336 {
3337 return $this->connection;
3338 }
3339
3340 /**
3341 * Set the connection associated with the model.
3342 *
3343 * @param string $name
3344 * @return $this
3345 */
3346 public function setConnection($name)
3347 {
3348 $this->connection = $name;
3349
3350 return $this;
3351 }
3352
3353 /**
3354 * Resolve a connection instance.
3355 *
3356 * @param string|null $connection
3357 * @return \Illuminate\Database\Connection
3358 */
3359 public static function resolveConnection($connection = null)
3360 {
3361 return static::$resolver->connection($connection);
3362 }
3363
3364 /**
3365 * Get the connection resolver instance.
3366 *
3367 * @return \Illuminate\Database\ConnectionResolverInterface
3368 */
3369 public static function getConnectionResolver()
3370 {
3371 return static::$resolver;
3372 }
3373
3374 /**
3375 * Set the connection resolver instance.
3376 *
3377 * @param \Illuminate\Database\ConnectionResolverInterface $resolver
3378 * @return void
3379 */
3380 public static function setConnectionResolver(Resolver $resolver)
3381 {
3382 static::$resolver = $resolver;
3383 }
3384
3385 /**
3386 * Unset the connection resolver for models.
3387 *
3388 * @return void
3389 */
3390 public static function unsetConnectionResolver()
3391 {
3392 static::$resolver = null;
3393 }
3394
3395 /**
3396 * Get the event dispatcher instance.
3397 *
3398 * @return \Illuminate\Contracts\Events\Dispatcher
3399 */
3400 public static function getEventDispatcher()
3401 {
3402 return static::$dispatcher;
3403 }
3404
3405 /**
3406 * Set the event dispatcher instance.
3407 *
3408 * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
3409 * @return void
3410 */
3411 public static function setEventDispatcher(Dispatcher $dispatcher)
3412 {
3413 static::$dispatcher = $dispatcher;
3414 }
3415
3416 /**
3417 * Unset the event dispatcher for models.
3418 *
3419 * @return void
3420 */
3421 public static function unsetEventDispatcher()
3422 {
3423 static::$dispatcher = null;
3424 }
3425
3426 /**
3427 * Get the mutated attributes for a given instance.
3428 *
3429 * @return array
3430 */
3431 public function getMutatedAttributes()
3432 {
3433 $class = static::class;
3434
3435 if (! isset(static::$mutatorCache[$class])) {
3436 static::cacheMutatedAttributes($class);
3437 }
3438
3439 return static::$mutatorCache[$class];
3440 }
3441
3442 /**
3443 * Extract and cache all the mutated attributes of a class.
3444 *
3445 * @param string $class
3446 * @return void
3447 */
3448 public static function cacheMutatedAttributes($class)
3449 {
3450 $mutatedAttributes = [];
3451
3452 // Here we will extract all of the mutated attributes so that we can quickly
3453 // spin through them after we export models to their array form, which we
3454 // need to be fast. This'll let us know the attributes that can mutate.
3455 if (preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($class)), $matches)) {
3456 foreach ($matches[1] as $match) {
3457 if (static::$snakeAttributes) {
3458 $match = Str::snake($match);
3459 }
3460
3461 $mutatedAttributes[] = lcfirst($match);
3462 }
3463 }
3464
3465 static::$mutatorCache[$class] = $mutatedAttributes;
3466 }
3467
3468 /**
3469 * Dynamically retrieve attributes on the model.
3470 *
3471 * @param string $key
3472 * @return mixed
3473 */
3474 public function __get($key)
3475 {
3476 return $this->getAttribute($key);
3477 }
3478
3479 /**
3480 * Dynamically set attributes on the model.
3481 *
3482 * @param string $key
3483 * @param mixed $value
3484 * @return void
3485 */
3486 public function __set($key, $value)
3487 {
3488 $this->setAttribute($key, $value);
3489 }
3490
3491 /**
3492 * Determine if the given attribute exists.
3493 *
3494 * @param mixed $offset
3495 * @return bool
3496 */
3497 public function offsetExists($offset)
3498 {
3499 return isset($this->$offset);
3500 }
3501
3502 /**
3503 * Get the value for a given offset.
3504 *
3505 * @param mixed $offset
3506 * @return mixed
3507 */
3508 public function offsetGet($offset)
3509 {
3510 return $this->$offset;
3511 }
3512
3513 /**
3514 * Set the value for a given offset.
3515 *
3516 * @param mixed $offset
3517 * @param mixed $value
3518 * @return void
3519 */
3520 public function offsetSet($offset, $value)
3521 {
3522 $this->$offset = $value;
3523 }
3524
3525 /**
3526 * Unset the value for a given offset.
3527 *
3528 * @param mixed $offset
3529 * @return void
3530 */
3531 public function offsetUnset($offset)
3532 {
3533 unset($this->$offset);
3534 }
3535
3536 /**
3537 * Determine if an attribute or relation exists on the model.
3538 *
3539 * @param string $key
3540 * @return bool
3541 */
3542 public function __isset($key)
3543 {
3544 return ! is_null($this->getAttribute($key));
3545 }
3546
3547 /**
3548 * Unset an attribute on the model.
3549 *
3550 * @param string $key
3551 * @return void
3552 */
3553 public function __unset($key)
3554 {
3555 unset($this->attributes[$key], $this->relations[$key]);
3556 }
3557
3558 /**
3559 * Handle dynamic method calls into the model.
3560 *
3561 * @param string $method
3562 * @param array $parameters
3563 * @return mixed
3564 */
3565 public function __call($method, $parameters)
3566 {
3567 if (in_array($method, ['increment', 'decrement'])) {
3568 return call_user_func_array([$this, $method], $parameters);
3569 }
3570
3571 $query = $this->newQuery();
3572
3573 return call_user_func_array([$query, $method], $parameters);
3574 }
3575
3576 /**
3577 * Handle dynamic static method calls into the method.
3578 *
3579 * @param string $method
3580 * @param array $parameters
3581 * @return mixed
3582 */
3583 public static function __callStatic($method, $parameters)
3584 {
3585 $instance = new static;
3586
3587 return call_user_func_array([$instance, $method], $parameters);
3588 }
3589
3590 /**
3591 * Convert the model to its string representation.
3592 *
3593 * @return string
3594 */
3595 public function __toString()
3596 {
3597 return $this->toJson();
3598 }
3599
3600 /**
3601 * When a model is being unserialized, check if it needs to be booted.
3602 *
3603 * @return void
3604 */
3605 public function __wakeup()
3606 {
3607 $this->bootIfNotBooted();
3608 }
3609}