· 7 years ago · Mar 04, 2019, 09:38 AM
1<?php
2
3namespace Illuminate\Database\Eloquent;
4
5use Exception;
6use ArrayAccess;
7use JsonSerializable;
8use Illuminate\Support\Arr;
9use Illuminate\Support\Str;
10use Illuminate\Contracts\Support\Jsonable;
11use Illuminate\Contracts\Support\Arrayable;
12use Illuminate\Support\Traits\ForwardsCalls;
13use Illuminate\Contracts\Routing\UrlRoutable;
14use Illuminate\Contracts\Queue\QueueableEntity;
15use Illuminate\Database\Eloquent\Relations\Pivot;
16use Illuminate\Contracts\Queue\QueueableCollection;
17use Illuminate\Support\Collection as BaseCollection;
18use Illuminate\Database\Query\Builder as QueryBuilder;
19use Illuminate\Database\ConnectionResolverInterface as Resolver;
20
21abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
22{
23 use Concerns\HasAttributes,
24 Concerns\HasEvents,
25 Concerns\HasGlobalScopes,
26 Concerns\HasRelationships,
27 Concerns\HasTimestamps,
28 Concerns\HidesAttributes,
29 Concerns\GuardsAttributes,
30 ForwardsCalls;
31
32 /**
33 * The connection name for the model.
34 *
35 * @var string
36 */
37 protected $connection;
38
39 /**
40 * The table associated with the model.
41 *
42 * @var string
43 */
44 protected $table;
45
46 /**
47 * The primary key for the model.
48 *
49 * @var string
50 */
51 protected $primaryKey = 'id';
52
53 /**
54 * The "type" of the auto-incrementing ID.
55 *
56 * @var string
57 */
58 protected $keyType = 'int';
59
60 /**
61 * Indicates if the IDs are auto-incrementing.
62 *
63 * @var bool
64 */
65 public $incrementing = true;
66
67 /**
68 * The relations to eager load on every query.
69 *
70 * @var array
71 */
72 protected $with = [];
73
74 /**
75 * The relationship counts that should be eager loaded on every query.
76 *
77 * @var array
78 */
79 protected $withCount = [];
80
81 /**
82 * The number of models to return for pagination.
83 *
84 * @var int
85 */
86 protected $perPage = 15;
87
88 /**
89 * Indicates if the model exists.
90 *
91 * @var bool
92 */
93 public $exists = false;
94
95 /**
96 * Indicates if the model was inserted during the current request lifecycle.
97 *
98 * @var bool
99 */
100 public $wasRecentlyCreated = false;
101
102 /**
103 * The connection resolver instance.
104 *
105 * @var \Illuminate\Database\ConnectionResolverInterface
106 */
107 protected static $resolver;
108
109 /**
110 * The event dispatcher instance.
111 *
112 * @var \Illuminate\Contracts\Events\Dispatcher
113 */
114 protected static $dispatcher;
115
116 /**
117 * The array of booted models.
118 *
119 * @var array
120 */
121 protected static $booted = [];
122
123 /**
124 * The array of trait initializers that will be called on each new instance.
125 *
126 * @var array
127 */
128 protected static $traitInitializers = [];
129
130 /**
131 * The array of global scopes on the model.
132 *
133 * @var array
134 */
135 protected static $globalScopes = [];
136
137 /**
138 * The list of models classes that should not be affected with touch.
139 *
140 * @var array
141 */
142 protected static $ignoreOnTouch = [];
143
144 /**
145 * The name of the "created at" column.
146 *
147 * @var string
148 */
149 const CREATED_AT = 'created_at';
150
151 /**
152 * The name of the "updated at" column.
153 *
154 * @var string
155 */
156 const UPDATED_AT = 'updated_at';
157
158 /**
159 * Create a new Eloquent model instance.
160 *
161 * @param array $attributes
162 * @return void
163 */
164 public function __construct(array $attributes = [])
165 {
166 $this->bootIfNotBooted();
167
168 $this->initializeTraits();
169
170 $this->syncOriginal();
171
172 $this->fill($attributes);
173 }
174
175 /**
176 * Check if the model needs to be booted and if so, do it.
177 *
178 * @return void
179 */
180 protected function bootIfNotBooted()
181 {
182 if (! isset(static::$booted[static::class])) {
183 static::$booted[static::class] = true;
184
185 $this->fireModelEvent('booting', false);
186
187 static::boot();
188
189 $this->fireModelEvent('booted', false);
190 }
191 }
192
193 /**
194 * The "booting" method of the model.
195 *
196 * @return void
197 */
198 protected static function boot()
199 {
200 static::bootTraits();
201 }
202
203 /**
204 * Boot all of the bootable traits on the model.
205 *
206 * @return void
207 */
208 protected static function bootTraits()
209 {
210 $class = static::class;
211
212 $booted = [];
213
214 static::$traitInitializers[$class] = [];
215
216 foreach (class_uses_recursive($class) as $trait) {
217 $method = 'boot'.class_basename($trait);
218
219 if (method_exists($class, $method) && ! in_array($method, $booted)) {
220 forward_static_call([$class, $method]);
221
222 $booted[] = $method;
223 }
224
225 if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
226 static::$traitInitializers[$class][] = $method;
227
228 static::$traitInitializers[$class] = array_unique(
229 static::$traitInitializers[$class]
230 );
231 }
232 }
233 }
234
235 /**
236 * Initialize any initializable traits on the model.
237 *
238 * @return void
239 */
240 protected function initializeTraits()
241 {
242 foreach (static::$traitInitializers[static::class] as $method) {
243 $this->{$method}();
244 }
245 }
246
247 /**
248 * Clear the list of booted models so they will be re-booted.
249 *
250 * @return void
251 */
252 public static function clearBootedModels()
253 {
254 static::$booted = [];
255
256 static::$globalScopes = [];
257 }
258
259 /**
260 * Disables relationship model touching for the current class during given callback scope.
261 *
262 * @param callable $callback
263 * @return void
264 */
265 public static function withoutTouching(callable $callback)
266 {
267 static::withoutTouchingOn([static::class], $callback);
268 }
269
270 /**
271 * Disables relationship model touching for the given model classes during given callback scope.
272 *
273 * @param array $models
274 * @param callable $callback
275 * @return void
276 */
277 public static function withoutTouchingOn(array $models, callable $callback)
278 {
279 static::$ignoreOnTouch = array_values(array_merge(static::$ignoreOnTouch, $models));
280
281 try {
282 call_user_func($callback);
283 } finally {
284 static::$ignoreOnTouch = array_values(array_diff(static::$ignoreOnTouch, $models));
285 }
286 }
287
288 /**
289 * Determine if the given model is ignoring touches.
290 *
291 * @param string|null $class
292 * @return bool
293 */
294 public static function isIgnoringTouch($class = null)
295 {
296 $class = $class ?: static::class;
297
298 foreach (static::$ignoreOnTouch as $ignoredClass) {
299 if ($class === $ignoredClass || is_subclass_of($class, $ignoredClass)) {
300 return true;
301 }
302 }
303
304 return false;
305 }
306
307 /**
308 * Fill the model with an array of attributes.
309 *
310 * @param array $attributes
311 * @return $this
312 *
313 * @throws \Illuminate\Database\Eloquent\MassAssignmentException
314 */
315 public function fill(array $attributes)
316 {
317 $totallyGuarded = $this->totallyGuarded();
318
319 foreach ($this->fillableFromArray($attributes) as $key => $value) {
320 $key = $this->removeTableFromKey($key);
321
322 // The developers may choose to place some attributes in the "fillable" array
323 // which means only those attributes may be set through mass assignment to
324 // the model, and all others will just get ignored for security reasons.
325 if ($this->isFillable($key)) {
326 $this->setAttribute($key, $value);
327 } elseif ($totallyGuarded) {
328 throw new MassAssignmentException(sprintf(
329 'Add [%s] to fillable property to allow mass assignment on [%s].',
330 $key, get_class($this)
331 ));
332 }
333 }
334
335 return $this;
336 }
337
338 /**
339 * Fill the model with an array of attributes. Force mass assignment.
340 *
341 * @param array $attributes
342 * @return $this
343 */
344 public function forceFill(array $attributes)
345 {
346 return static::unguarded(function () use ($attributes) {
347 return $this->fill($attributes);
348 });
349 }
350
351 /**
352 * Qualify the given column name by the model's table.
353 *
354 * @param string $column
355 * @return string
356 */
357 public function qualifyColumn($column)
358 {
359 if (Str::contains($column, '.')) {
360 return $column;
361 }
362
363 return $this->getTable().'.'.$column;
364 }
365
366 /**
367 * Remove the table name from a given key.
368 *
369 * @param string $key
370 * @return string
371 */
372 protected function removeTableFromKey($key)
373 {
374 return Str::contains($key, '.') ? last(explode('.', $key)) : $key;
375 }
376
377 /**
378 * Create a new instance of the given model.
379 *
380 * @param array $attributes
381 * @param bool $exists
382 * @return static
383 */
384 public function newInstance($attributes = [], $exists = false)
385 {
386 // This method just provides a convenient way for us to generate fresh model
387 // instances of this current model. It is particularly useful during the
388 // hydration of new objects via the Eloquent query builder instances.
389 $model = new static((array) $attributes);
390
391 $model->exists = $exists;
392
393 $model->setConnection(
394 $this->getConnectionName()
395 );
396
397 $model->setTable($this->getTable());
398
399 return $model;
400 }
401
402 /**
403 * Create a new model instance that is existing.
404 *
405 * @param array $attributes
406 * @param string|null $connection
407 * @return static
408 */
409 public function newFromBuilder($attributes = [], $connection = null)
410 {
411 $model = $this->newInstance([], true);
412
413 $model->setRawAttributes((array) $attributes, true);
414
415 $model->setConnection($connection ?: $this->getConnectionName());
416
417 $model->fireModelEvent('retrieved', false);
418
419 return $model;
420 }
421
422 /**
423 * Begin querying the model on a given connection.
424 *
425 * @param string|null $connection
426 * @return \Illuminate\Database\Eloquent\Builder
427 */
428 public static function on($connection = null)
429 {
430 // First we will just create a fresh instance of this model, and then we can set the
431 // connection on the model so that it is used for the queries we execute, as well
432 // as being set on every relation we retrieve without a custom connection name.
433 $instance = new static;
434
435 $instance->setConnection($connection);
436
437 return $instance->newQuery();
438 }
439
440 /**
441 * Begin querying the model on the write connection.
442 *
443 * @return \Illuminate\Database\Query\Builder
444 */
445 public static function onWriteConnection()
446 {
447 $instance = new static;
448
449 return $instance->newQuery()->useWritePdo();
450 }
451
452 /**
453 * Get all of the models from the database.
454 *
455 * @param array|mixed $columns
456 * @return \Illuminate\Database\Eloquent\Collection|static[]
457 */
458 public static function all($columns = ['*'])
459 {
460 return (new static)->newQuery()->get(
461 is_array($columns) ? $columns : func_get_args()
462 );
463 }
464
465 /**
466 * Begin querying a model with eager loading.
467 *
468 * @param array|string $relations
469 * @return \Illuminate\Database\Eloquent\Builder|static
470 */
471 public static function with($relations)
472 {
473 return (new static)->newQuery()->with(
474 is_string($relations) ? func_get_args() : $relations
475 );
476 }
477
478 /**
479 * Eager load relations on the model.
480 *
481 * @param array|string $relations
482 * @return $this
483 */
484 public function load($relations)
485 {
486 $query = $this->newQueryWithoutRelationships()->with(
487 is_string($relations) ? func_get_args() : $relations
488 );
489
490 $query->eagerLoadRelations([$this]);
491
492 return $this;
493 }
494
495 /**
496 * Eager load relations on the model if they are not already eager loaded.
497 *
498 * @param array|string $relations
499 * @return $this
500 */
501 public function loadMissing($relations)
502 {
503 $relations = is_string($relations) ? func_get_args() : $relations;
504
505 $this->newCollection([$this])->loadMissing($relations);
506
507 return $this;
508 }
509
510 /**
511 * Increment a column's value by a given amount.
512 *
513 * @param string $column
514 * @param float|int $amount
515 * @param array $extra
516 * @return int
517 */
518 protected function increment($column, $amount = 1, array $extra = [])
519 {
520 return $this->incrementOrDecrement($column, $amount, $extra, 'increment');
521 }
522
523 /**
524 * Decrement a column's value by a given amount.
525 *
526 * @param string $column
527 * @param float|int $amount
528 * @param array $extra
529 * @return int
530 */
531 protected function decrement($column, $amount = 1, array $extra = [])
532 {
533 return $this->incrementOrDecrement($column, $amount, $extra, 'decrement');
534 }
535
536 /**
537 * Run the increment or decrement method on the model.
538 *
539 * @param string $column
540 * @param float|int $amount
541 * @param array $extra
542 * @param string $method
543 * @return int
544 */
545 protected function incrementOrDecrement($column, $amount, $extra, $method)
546 {
547 $query = $this->newQueryWithoutRelationships();
548
549 if (! $this->exists) {
550 return $query->{$method}($column, $amount, $extra);
551 }
552
553 $this->incrementOrDecrementAttributeValue($column, $amount, $extra, $method);
554
555 return $query->where(
556 $this->getKeyName(), $this->getKey()
557 )->{$method}($column, $amount, $extra);
558 }
559
560 /**
561 * Increment the underlying attribute value and sync with original.
562 *
563 * @param string $column
564 * @param float|int $amount
565 * @param array $extra
566 * @param string $method
567 * @return void
568 */
569 protected function incrementOrDecrementAttributeValue($column, $amount, $extra, $method)
570 {
571 $this->{$column} = $this->{$column} + ($method === 'increment' ? $amount : $amount * -1);
572
573 $this->forceFill($extra);
574
575 $this->syncOriginalAttribute($column);
576 }
577
578 /**
579 * Update the model in the database.
580 *
581 * @param array $attributes
582 * @param array $options
583 * @return bool
584 */
585 public function update(array $attributes = [], array $options = [])
586 {
587 if (! $this->exists) {
588 return false;
589 }
590
591 return $this->fill($attributes)->save($options);
592 }
593
594 /**
595 * Save the model and all of its relationships.
596 *
597 * @return bool
598 */
599 public function push()
600 {
601 if (! $this->save()) {
602 return false;
603 }
604
605 // To sync all of the relationships to the database, we will simply spin through
606 // the relationships and save each model via this "push" method, which allows
607 // us to recurse into all of these nested relations for the model instance.
608 foreach ($this->relations as $models) {
609 $models = $models instanceof Collection
610 ? $models->all() : [$models];
611
612 foreach (array_filter($models) as $model) {
613 if (! $model->push()) {
614 return false;
615 }
616 }
617 }
618
619 return true;
620 }
621
622 /**
623 * Save the model to the database.
624 *
625 * @param array $options
626 * @return bool
627 */
628 public function save(array $options = [])
629 {
630 $query = $this->newModelQuery();
631
632 // If the "saving" event returns false we'll bail out of the save and return
633 // false, indicating that the save failed. This provides a chance for any
634 // listeners to cancel save operations if validations fail or whatever.
635 if ($this->fireModelEvent('saving') === false) {
636 dd('fail');
637 return false;
638 }
639
640 // If the model already exists in the database we can just update our record
641 // that is already in this database using the current IDs in this "where"
642 // clause to only update this model. Otherwise, we'll just insert them.
643 if ($this->exists) {
644 $saved = $this->isDirty() ?
645 $this->performUpdate($query) : true;
646 }
647
648 // If the model is brand new, we'll insert it into our database and set the
649 // ID attribute on the model to the value of the newly inserted row's ID
650 // which is typically an auto-increment value managed by the database.
651 else {
652 $saved = $this->performInsert($query);
653
654 dd([$query, $saved]);
655 if (! $this->getConnectionName() &&
656 $connection = $query->getConnection()) {
657 $this->setConnection($connection->getName());
658 }
659 }
660
661 dd('success');
662
663 // If the model is successfully saved, we need to do a few more things once
664 // that is done. We will call the "saved" method here to run any actions
665 // we need to happen after a model gets successfully saved right here.
666 if ($saved) {
667 $this->finishSave($options);
668 }
669
670 return $saved;
671 }
672
673 /**
674 * Save the model to the database using transaction.
675 *
676 * @param array $options
677 * @return bool
678 *
679 * @throws \Throwable
680 */
681 public function saveOrFail(array $options = [])
682 {
683 return $this->getConnection()->transaction(function () use ($options) {
684 return $this->save($options);
685 });
686 }
687
688 /**
689 * Perform any actions that are necessary after the model is saved.
690 *
691 * @param array $options
692 * @return void
693 */
694 protected function finishSave(array $options)
695 {
696 $this->fireModelEvent('saved', false);
697
698 if ($this->isDirty() && ($options['touch'] ?? true)) {
699 $this->touchOwners();
700 }
701
702 $this->syncOriginal();
703 }
704
705 /**
706 * Perform a model update operation.
707 *
708 * @param \Illuminate\Database\Eloquent\Builder $query
709 * @return bool
710 */
711 protected function performUpdate(Builder $query)
712 {
713 // If the updating event returns false, we will cancel the update operation so
714 // developers can hook Validation systems into their models and cancel this
715 // operation if the model does not pass validation. Otherwise, we update.
716 if ($this->fireModelEvent('updating') === false) {
717 return false;
718 }
719
720 // First we need to create a fresh query instance and touch the creation and
721 // update timestamp on the model which are maintained by us for developer
722 // convenience. Then we will just continue saving the model instances.
723 if ($this->usesTimestamps()) {
724 $this->updateTimestamps();
725 }
726
727 // Once we have run the update operation, we will fire the "updated" event for
728 // this model instance. This will allow developers to hook into these after
729 // models are updated, giving them a chance to do any special processing.
730 $dirty = $this->getDirty();
731
732 if (count($dirty) > 0) {
733 $this->setKeysForSaveQuery($query)->update($dirty);
734
735 $this->syncChanges();
736
737 $this->fireModelEvent('updated', false);
738 }
739
740 return true;
741 }
742
743 /**
744 * Set the keys for a save update query.
745 *
746 * @param \Illuminate\Database\Eloquent\Builder $query
747 * @return \Illuminate\Database\Eloquent\Builder
748 */
749 protected function setKeysForSaveQuery(Builder $query)
750 {
751 $query->where($this->getKeyName(), '=', $this->getKeyForSaveQuery());
752
753 return $query;
754 }
755
756 /**
757 * Get the primary key value for a save query.
758 *
759 * @return mixed
760 */
761 protected function getKeyForSaveQuery()
762 {
763 return $this->original[$this->getKeyName()]
764 ?? $this->getKey();
765 }
766
767 /**
768 * Perform a model insert operation.
769 *
770 * @param \Illuminate\Database\Eloquent\Builder $query
771 * @return bool
772 */
773 protected function performInsert(Builder $query)
774 {
775 if ($this->fireModelEvent('creating') === false) {
776 return false;
777 }
778
779 // First we'll need to create a fresh query instance and touch the creation and
780 // update timestamps on this model, which are maintained by us for developer
781 // convenience. After, we will just continue saving these model instances.
782 if ($this->usesTimestamps()) {
783 $this->updateTimestamps();
784 }
785
786 // If the model has an incrementing key, we can use the "insertGetId" method on
787 // the query builder, which will give us back the final inserted ID for this
788 // table from the database. Not all tables have to be incrementing though.
789 $attributes = $this->getAttributes();
790
791 if ($this->getIncrementing()) {
792 $this->insertAndSetId($query, $attributes);
793 }
794
795 // If the table isn't incrementing we'll simply insert these attributes as they
796 // are. These attribute arrays must contain an "id" column previously placed
797 // there by the developer as the manually determined key for these models.
798 else {
799 if (empty($attributes)) {
800 return true;
801 }
802
803 $query->insert($attributes);
804 }
805
806 // We will go ahead and set the exists property to true, so that it is set when
807 // the created event is fired, just in case the developer tries to update it
808 // during the event. This will allow them to do so and run an update here.
809 $this->exists = true;
810
811 $this->wasRecentlyCreated = true;
812
813 $this->fireModelEvent('created', false);
814
815 return true;
816 }
817
818 /**
819 * Insert the given attributes and set the ID on the model.
820 *
821 * @param \Illuminate\Database\Eloquent\Builder $query
822 * @param array $attributes
823 * @return void
824 */
825 protected function insertAndSetId(Builder $query, $attributes)
826 {
827 $id = $query->insertGetId($attributes, $keyName = $this->getKeyName());
828
829 $this->setAttribute($keyName, $id);
830 }
831
832 /**
833 * Destroy the models for the given IDs.
834 *
835 * @param \Illuminate\Support\Collection|array|int $ids
836 * @return int
837 */
838 public static function destroy($ids)
839 {
840 // We'll initialize a count here so we will return the total number of deletes
841 // for the operation. The developers can then check this number as a boolean
842 // type value or get this total count of records deleted for logging, etc.
843 $count = 0;
844
845 if ($ids instanceof BaseCollection) {
846 $ids = $ids->all();
847 }
848
849 $ids = is_array($ids) ? $ids : func_get_args();
850
851 // We will actually pull the models from the database table and call delete on
852 // each of them individually so that their events get fired properly with a
853 // correct set of attributes in case the developers wants to check these.
854 $key = ($instance = new static)->getKeyName();
855
856 foreach ($instance->whereIn($key, $ids)->get() as $model) {
857 if ($model->delete()) {
858 $count++;
859 }
860 }
861
862 return $count;
863 }
864
865 /**
866 * Delete the model from the database.
867 *
868 * @return bool|null
869 *
870 * @throws \Exception
871 */
872 public function delete()
873 {
874 if (is_null($this->getKeyName())) {
875 throw new Exception('No primary key defined on model.');
876 }
877
878 // If the model doesn't exist, there is nothing to delete so we'll just return
879 // immediately and not do anything else. Otherwise, we will continue with a
880 // deletion process on the model, firing the proper events, and so forth.
881 if (! $this->exists) {
882 return;
883 }
884
885 if ($this->fireModelEvent('deleting') === false) {
886 return false;
887 }
888
889 // Here, we'll touch the owning models, verifying these timestamps get updated
890 // for the models. This will allow any caching to get broken on the parents
891 // by the timestamp. Then we will go ahead and delete the model instance.
892 $this->touchOwners();
893
894 $this->performDeleteOnModel();
895
896 // Once the model has been deleted, we will fire off the deleted event so that
897 // the developers may hook into post-delete operations. We will then return
898 // a boolean true as the delete is presumably successful on the database.
899 $this->fireModelEvent('deleted', false);
900
901 return true;
902 }
903
904 /**
905 * Force a hard delete on a soft deleted model.
906 *
907 * This method protects developers from running forceDelete when trait is missing.
908 *
909 * @return bool|null
910 */
911 public function forceDelete()
912 {
913 return $this->delete();
914 }
915
916 /**
917 * Perform the actual delete query on this model instance.
918 *
919 * @return void
920 */
921 protected function performDeleteOnModel()
922 {
923 $this->setKeysForSaveQuery($this->newModelQuery())->delete();
924
925 $this->exists = false;
926 }
927
928 /**
929 * Begin querying the model.
930 *
931 * @return \Illuminate\Database\Eloquent\Builder
932 */
933 public static function query()
934 {
935 return (new static)->newQuery();
936 }
937
938 /**
939 * Get a new query builder for the model's table.
940 *
941 * @return \Illuminate\Database\Eloquent\Builder
942 */
943 public function newQuery()
944 {
945 return $this->registerGlobalScopes($this->newQueryWithoutScopes());
946 }
947
948 /**
949 * Get a new query builder that doesn't have any global scopes or eager loading.
950 *
951 * @return \Illuminate\Database\Eloquent\Builder|static
952 */
953 public function newModelQuery()
954 {
955 return $this->newEloquentBuilder(
956 $this->newBaseQueryBuilder()
957 )->setModel($this);
958 }
959
960 /**
961 * Get a new query builder with no relationships loaded.
962 *
963 * @return \Illuminate\Database\Eloquent\Builder
964 */
965 public function newQueryWithoutRelationships()
966 {
967 return $this->registerGlobalScopes($this->newModelQuery());
968 }
969
970 /**
971 * Register the global scopes for this builder instance.
972 *
973 * @param \Illuminate\Database\Eloquent\Builder $builder
974 * @return \Illuminate\Database\Eloquent\Builder
975 */
976 public function registerGlobalScopes($builder)
977 {
978 foreach ($this->getGlobalScopes() as $identifier => $scope) {
979 $builder->withGlobalScope($identifier, $scope);
980 }
981
982 return $builder;
983 }
984
985 /**
986 * Get a new query builder that doesn't have any global scopes.
987 *
988 * @return \Illuminate\Database\Eloquent\Builder|static
989 */
990 public function newQueryWithoutScopes()
991 {
992 return $this->newModelQuery()
993 ->with($this->with)
994 ->withCount($this->withCount);
995 }
996
997 /**
998 * Get a new query instance without a given scope.
999 *
1000 * @param \Illuminate\Database\Eloquent\Scope|string $scope
1001 * @return \Illuminate\Database\Eloquent\Builder
1002 */
1003 public function newQueryWithoutScope($scope)
1004 {
1005 return $this->newQuery()->withoutGlobalScope($scope);
1006 }
1007
1008 /**
1009 * Get a new query to restore one or more models by their queueable IDs.
1010 *
1011 * @param array|int $ids
1012 * @return \Illuminate\Database\Eloquent\Builder
1013 */
1014 public function newQueryForRestoration($ids)
1015 {
1016 return is_array($ids)
1017 ? $this->newQueryWithoutScopes()->whereIn($this->getQualifiedKeyName(), $ids)
1018 : $this->newQueryWithoutScopes()->whereKey($ids);
1019 }
1020
1021 /**
1022 * Create a new Eloquent query builder for the model.
1023 *
1024 * @param \Illuminate\Database\Query\Builder $query
1025 * @return \Illuminate\Database\Eloquent\Builder|static
1026 */
1027 public function newEloquentBuilder($query)
1028 {
1029 return new Builder($query);
1030 }
1031
1032 /**
1033 * Get a new query builder instance for the connection.
1034 *
1035 * @return \Illuminate\Database\Query\Builder
1036 */
1037 protected function newBaseQueryBuilder()
1038 {
1039 $connection = $this->getConnection();
1040
1041 return new QueryBuilder(
1042 $connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
1043 );
1044 }
1045
1046 /**
1047 * Create a new Eloquent Collection instance.
1048 *
1049 * @param array $models
1050 * @return \Illuminate\Database\Eloquent\Collection
1051 */
1052 public function newCollection(array $models = [])
1053 {
1054 return new Collection($models);
1055 }
1056
1057 /**
1058 * Create a new pivot model instance.
1059 *
1060 * @param \Illuminate\Database\Eloquent\Model $parent
1061 * @param array $attributes
1062 * @param string $table
1063 * @param bool $exists
1064 * @param string|null $using
1065 * @return \Illuminate\Database\Eloquent\Relations\Pivot
1066 */
1067 public function newPivot(self $parent, array $attributes, $table, $exists, $using = null)
1068 {
1069 return $using ? $using::fromRawAttributes($parent, $attributes, $table, $exists)
1070 : Pivot::fromAttributes($parent, $attributes, $table, $exists);
1071 }
1072
1073 /**
1074 * Convert the model instance to an array.
1075 *
1076 * @return array
1077 */
1078 public function toArray()
1079 {
1080 return array_merge($this->attributesToArray(), $this->relationsToArray());
1081 }
1082
1083 /**
1084 * Convert the model instance to JSON.
1085 *
1086 * @param int $options
1087 * @return string
1088 *
1089 * @throws \Illuminate\Database\Eloquent\JsonEncodingException
1090 */
1091 public function toJson($options = 0)
1092 {
1093 $json = json_encode($this->jsonSerialize(), $options);
1094
1095 if (JSON_ERROR_NONE !== json_last_error()) {
1096 throw JsonEncodingException::forModel($this, json_last_error_msg());
1097 }
1098
1099 return $json;
1100 }
1101
1102 /**
1103 * Convert the object into something JSON serializable.
1104 *
1105 * @return array
1106 */
1107 public function jsonSerialize()
1108 {
1109 return $this->toArray();
1110 }
1111
1112 /**
1113 * Reload a fresh model instance from the database.
1114 *
1115 * @param array|string $with
1116 * @return static|null
1117 */
1118 public function fresh($with = [])
1119 {
1120 if (! $this->exists) {
1121 return;
1122 }
1123
1124 return static::newQueryWithoutScopes()
1125 ->with(is_string($with) ? func_get_args() : $with)
1126 ->where($this->getKeyName(), $this->getKey())
1127 ->first();
1128 }
1129
1130 /**
1131 * Reload the current model instance with fresh attributes from the database.
1132 *
1133 * @return $this
1134 */
1135 public function refresh()
1136 {
1137 if (! $this->exists) {
1138 return $this;
1139 }
1140
1141 $this->setRawAttributes(
1142 static::newQueryWithoutScopes()->findOrFail($this->getKey())->attributes
1143 );
1144
1145 $this->load(collect($this->relations)->except('pivot')->keys()->toArray());
1146
1147 $this->syncOriginal();
1148
1149 return $this;
1150 }
1151
1152 /**
1153 * Clone the model into a new, non-existing instance.
1154 *
1155 * @param array|null $except
1156 * @return static
1157 */
1158 public function replicate(array $except = null)
1159 {
1160 $defaults = [
1161 $this->getKeyName(),
1162 $this->getCreatedAtColumn(),
1163 $this->getUpdatedAtColumn(),
1164 ];
1165
1166 $attributes = Arr::except(
1167 $this->attributes, $except ? array_unique(array_merge($except, $defaults)) : $defaults
1168 );
1169
1170 return tap(new static, function ($instance) use ($attributes) {
1171 $instance->setRawAttributes($attributes);
1172
1173 $instance->setRelations($this->relations);
1174 });
1175 }
1176
1177 /**
1178 * Determine if two models have the same ID and belong to the same table.
1179 *
1180 * @param \Illuminate\Database\Eloquent\Model|null $model
1181 * @return bool
1182 */
1183 public function is($model)
1184 {
1185 return ! is_null($model) &&
1186 $this->getKey() === $model->getKey() &&
1187 $this->getTable() === $model->getTable() &&
1188 $this->getConnectionName() === $model->getConnectionName();
1189 }
1190
1191 /**
1192 * Determine if two models are not the same.
1193 *
1194 * @param \Illuminate\Database\Eloquent\Model|null $model
1195 * @return bool
1196 */
1197 public function isNot($model)
1198 {
1199 return ! $this->is($model);
1200 }
1201
1202 /**
1203 * Get the database connection for the model.
1204 *
1205 * @return \Illuminate\Database\Connection
1206 */
1207 public function getConnection()
1208 {
1209 return static::resolveConnection($this->getConnectionName());
1210 }
1211
1212 /**
1213 * Get the current connection name for the model.
1214 *
1215 * @return string
1216 */
1217 public function getConnectionName()
1218 {
1219 return $this->connection;
1220 }
1221
1222 /**
1223 * Set the connection associated with the model.
1224 *
1225 * @param string $name
1226 * @return $this
1227 */
1228 public function setConnection($name)
1229 {
1230 $this->connection = $name;
1231
1232 return $this;
1233 }
1234
1235 /**
1236 * Resolve a connection instance.
1237 *
1238 * @param string|null $connection
1239 * @return \Illuminate\Database\Connection
1240 */
1241 public static function resolveConnection($connection = null)
1242 {
1243 return static::$resolver->connection($connection);
1244 }
1245
1246 /**
1247 * Get the connection resolver instance.
1248 *
1249 * @return \Illuminate\Database\ConnectionResolverInterface
1250 */
1251 public static function getConnectionResolver()
1252 {
1253 return static::$resolver;
1254 }
1255
1256 /**
1257 * Set the connection resolver instance.
1258 *
1259 * @param \Illuminate\Database\ConnectionResolverInterface $resolver
1260 * @return void
1261 */
1262 public static function setConnectionResolver(Resolver $resolver)
1263 {
1264 static::$resolver = $resolver;
1265 }
1266
1267 /**
1268 * Unset the connection resolver for models.
1269 *
1270 * @return void
1271 */
1272 public static function unsetConnectionResolver()
1273 {
1274 static::$resolver = null;
1275 }
1276
1277 /**
1278 * Get the table associated with the model.
1279 *
1280 * @return string
1281 */
1282 public function getTable()
1283 {
1284 if (! isset($this->table)) {
1285 return str_replace(
1286 '\\', '', Str::snake(Str::plural(class_basename($this)))
1287 );
1288 }
1289
1290 return $this->table;
1291 }
1292
1293 /**
1294 * Set the table associated with the model.
1295 *
1296 * @param string $table
1297 * @return $this
1298 */
1299 public function setTable($table)
1300 {
1301 $this->table = $table;
1302
1303 return $this;
1304 }
1305
1306 /**
1307 * Get the primary key for the model.
1308 *
1309 * @return string
1310 */
1311 public function getKeyName()
1312 {
1313 return $this->primaryKey;
1314 }
1315
1316 /**
1317 * Set the primary key for the model.
1318 *
1319 * @param string $key
1320 * @return $this
1321 */
1322 public function setKeyName($key)
1323 {
1324 $this->primaryKey = $key;
1325
1326 return $this;
1327 }
1328
1329 /**
1330 * Get the table qualified key name.
1331 *
1332 * @return string
1333 */
1334 public function getQualifiedKeyName()
1335 {
1336 return $this->qualifyColumn($this->getKeyName());
1337 }
1338
1339 /**
1340 * Get the auto-incrementing key type.
1341 *
1342 * @return string
1343 */
1344 public function getKeyType()
1345 {
1346 return $this->keyType;
1347 }
1348
1349 /**
1350 * Set the data type for the primary key.
1351 *
1352 * @param string $type
1353 * @return $this
1354 */
1355 public function setKeyType($type)
1356 {
1357 $this->keyType = $type;
1358
1359 return $this;
1360 }
1361
1362 /**
1363 * Get the value indicating whether the IDs are incrementing.
1364 *
1365 * @return bool
1366 */
1367 public function getIncrementing()
1368 {
1369 return $this->incrementing;
1370 }
1371
1372 /**
1373 * Set whether IDs are incrementing.
1374 *
1375 * @param bool $value
1376 * @return $this
1377 */
1378 public function setIncrementing($value)
1379 {
1380 $this->incrementing = $value;
1381
1382 return $this;
1383 }
1384
1385 /**
1386 * Get the value of the model's primary key.
1387 *
1388 * @return mixed
1389 */
1390 public function getKey()
1391 {
1392 return $this->getAttribute($this->getKeyName());
1393 }
1394
1395 /**
1396 * Get the queueable identity for the entity.
1397 *
1398 * @return mixed
1399 */
1400 public function getQueueableId()
1401 {
1402 return $this->getKey();
1403 }
1404
1405 /**
1406 * Get the queueable relationships for the entity.
1407 *
1408 * @return array
1409 */
1410 public function getQueueableRelations()
1411 {
1412 $relations = [];
1413
1414 foreach ($this->getRelations() as $key => $relation) {
1415 if (! method_exists($this, $key)) {
1416 continue;
1417 }
1418
1419 $relations[] = $key;
1420
1421 if ($relation instanceof QueueableCollection) {
1422 foreach ($relation->getQueueableRelations() as $collectionValue) {
1423 $relations[] = $key.'.'.$collectionValue;
1424 }
1425 }
1426
1427 if ($relation instanceof QueueableEntity) {
1428 foreach ($relation->getQueueableRelations() as $entityKey => $entityValue) {
1429 $relations[] = $key.'.'.$entityValue;
1430 }
1431 }
1432 }
1433
1434 return array_unique($relations);
1435 }
1436
1437 /**
1438 * Get the queueable connection for the entity.
1439 *
1440 * @return mixed
1441 */
1442 public function getQueueableConnection()
1443 {
1444 return $this->getConnectionName();
1445 }
1446
1447 /**
1448 * Get the value of the model's route key.
1449 *
1450 * @return mixed
1451 */
1452 public function getRouteKey()
1453 {
1454 return $this->getAttribute($this->getRouteKeyName());
1455 }
1456
1457 /**
1458 * Get the route key for the model.
1459 *
1460 * @return string
1461 */
1462 public function getRouteKeyName()
1463 {
1464 return $this->getKeyName();
1465 }
1466
1467 /**
1468 * Retrieve the model for a bound value.
1469 *
1470 * @param mixed $value
1471 * @return \Illuminate\Database\Eloquent\Model|null
1472 */
1473 public function resolveRouteBinding($value)
1474 {
1475 return $this->where($this->getRouteKeyName(), $value)->first();
1476 }
1477
1478 /**
1479 * Get the default foreign key name for the model.
1480 *
1481 * @return string
1482 */
1483 public function getForeignKey()
1484 {
1485 return Str::snake(class_basename($this)).'_'.$this->getKeyName();
1486 }
1487
1488 /**
1489 * Get the number of models to return per page.
1490 *
1491 * @return int
1492 */
1493 public function getPerPage()
1494 {
1495 return $this->perPage;
1496 }
1497
1498 /**
1499 * Set the number of models to return per page.
1500 *
1501 * @param int $perPage
1502 * @return $this
1503 */
1504 public function setPerPage($perPage)
1505 {
1506 $this->perPage = $perPage;
1507
1508 return $this;
1509 }
1510
1511 /**
1512 * Dynamically retrieve attributes on the model.
1513 *
1514 * @param string $key
1515 * @return mixed
1516 */
1517 public function __get($key)
1518 {
1519 return $this->getAttribute($key);
1520 }
1521
1522 /**
1523 * Dynamically set attributes on the model.
1524 *
1525 * @param string $key
1526 * @param mixed $value
1527 * @return void
1528 */
1529 public function __set($key, $value)
1530 {
1531 $this->setAttribute($key, $value);
1532 }
1533
1534 /**
1535 * Determine if the given attribute exists.
1536 *
1537 * @param mixed $offset
1538 * @return bool
1539 */
1540 public function offsetExists($offset)
1541 {
1542 return ! is_null($this->getAttribute($offset));
1543 }
1544
1545 /**
1546 * Get the value for a given offset.
1547 *
1548 * @param mixed $offset
1549 * @return mixed
1550 */
1551 public function offsetGet($offset)
1552 {
1553 return $this->getAttribute($offset);
1554 }
1555
1556 /**
1557 * Set the value for a given offset.
1558 *
1559 * @param mixed $offset
1560 * @param mixed $value
1561 * @return void
1562 */
1563 public function offsetSet($offset, $value)
1564 {
1565 $this->setAttribute($offset, $value);
1566 }
1567
1568 /**
1569 * Unset the value for a given offset.
1570 *
1571 * @param mixed $offset
1572 * @return void
1573 */
1574 public function offsetUnset($offset)
1575 {
1576 unset($this->attributes[$offset], $this->relations[$offset]);
1577 }
1578
1579 /**
1580 * Determine if an attribute or relation exists on the model.
1581 *
1582 * @param string $key
1583 * @return bool
1584 */
1585 public function __isset($key)
1586 {
1587 return $this->offsetExists($key);
1588 }
1589
1590 /**
1591 * Unset an attribute on the model.
1592 *
1593 * @param string $key
1594 * @return void
1595 */
1596 public function __unset($key)
1597 {
1598 $this->offsetUnset($key);
1599 }
1600
1601 /**
1602 * Handle dynamic method calls into the model.
1603 *
1604 * @param string $method
1605 * @param array $parameters
1606 * @return mixed
1607 */
1608 public function __call($method, $parameters)
1609 {
1610 if (in_array($method, ['increment', 'decrement'])) {
1611 return $this->$method(...$parameters);
1612 }
1613
1614 return $this->forwardCallTo($this->newQuery(), $method, $parameters);
1615 }
1616
1617 /**
1618 * Handle dynamic static method calls into the method.
1619 *
1620 * @param string $method
1621 * @param array $parameters
1622 * @return mixed
1623 */
1624 public static function __callStatic($method, $parameters)
1625 {
1626 return (new static)->$method(...$parameters);
1627 }
1628
1629 /**
1630 * Convert the model to its string representation.
1631 *
1632 * @return string
1633 */
1634 public function __toString()
1635 {
1636 return $this->toJson();
1637 }
1638
1639 /**
1640 * When a model is being unserialized, check if it needs to be booted.
1641 *
1642 * @return void
1643 */
1644 public function __wakeup()
1645 {
1646 $this->bootIfNotBooted();
1647 }
1648}