Open sidebar
<?php namespace Illuminate\Database\Eloquent; use DateTime; use Exception; use ArrayAccess; use Carbon\Carbon; use LogicException; use JsonSerializable; use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Contracts\Support\Jsonable; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Routing\UrlRoutable; use Illuminate\Contracts\Queue\QueueableEntity; use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Support\Collection as BaseCollection; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\ConnectionResolverInterface as Resolver; abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable { /** * The connection name for the model. * * @var string */ protected $connection; /** * The table associated with the model. * * @var string */ protected $table; /** * The primary key for the model. * * @var string */ protected $primaryKey = 'id'; /** * The number of models to return for pagination. * * @var int */ protected $perPage = 15; /** * Indicates if the IDs are auto-incrementing. * * @var bool */ public $incrementing = true; /** * Indicates if the model should be timestamped. * * @var bool */ public $timestamps = true; /** * The model's attributes. * * @var array */ protected $attributes = []; /** * The model attribute's original state. * * @var array */ protected $original = []; /** * The loaded relationships for the model. * * @var array */ protected $relations = []; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = []; /** * The attributes that should be visible in arrays. * * @var array */ protected $visible = []; /** * The accessors to append to the model's array form. * * @var array */ protected $appends = []; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = []; /** * The attributes that aren't mass assignable. * * @var array */ protected $guarded = ['*']; /** * The attributes that should be mutated to dates. * * @var array */ protected $dates = []; /** * The storage format of the model's date columns. * * @var string */ protected $dateFormat; /** * The attributes that should be casted to native types. * * @var array */ protected $casts = []; /** * The relationships that should be touched on save. * * @var array */ protected $touches = []; /** * User exposed observable events. * * @var array */ protected $observables = []; /** * The relations to eager load on every query. * * @var array */ protected $with = []; /** * The class name to be used in polymorphic relations. * * @var string */ protected $morphClass; /** * Indicates if the model exists. * * @var bool */ public $exists = false; /** * Indicates if the model mutated attributes can have different case from ex. User_comments instead of user_comments. * * @var bool */ public $mutatedGetFirsCapitalLetter = false; /** * Indicates if the model was inserted during the current request lifecycle. * * @var bool */ public $wasRecentlyCreated = false; /** * Indicates whether attributes are snake cased on arrays. * * @var bool */ public static $snakeAttributes = true; /** * The connection resolver instance. * * @var \Illuminate\Database\ConnectionResolverInterface */ protected static $resolver; /** * The event dispatcher instance. * * @var \Illuminate\Contracts\Events\Dispatcher */ protected static $dispatcher; /** * The array of booted models. * * @var array */ protected static $booted = []; /** * The array of global scopes on the model. * * @var array */ protected static $globalScopes = []; /** * Indicates if all mass assignment is enabled. * * @var bool */ protected static $unguarded = false; /** * The cache of the mutated attributes for each class. * * @var array */ protected static $mutatorCache = []; /** * The many to many relationship methods. * * @var array */ public static $manyMethods = ['belongsToMany', 'morphToMany', 'morphedByMany']; /** * The name of the "created at" column. * * @var string */ const CREATED_AT = 'created_at'; /** * The name of the "updated at" column. * * @var string */ const UPDATED_AT = 'updated_at'; /** * Create a new Eloquent model instance. * * @param array $attributes * @return void */ public function __construct(array $attributes = []) { $this->bootIfNotBooted(); $this->syncOriginal(); $this->fill($attributes); } /** * Check if the model needs to be booted and if so, do it. * * @return void */ protected function bootIfNotBooted() { $class = get_class($this); if (! isset(static::$booted[$class])) { static::$booted[$class] = true; $this->fireModelEvent('booting', false); static::boot(); $this->fireModelEvent('booted', false); } } /** * The "booting" method of the model. * * @return void */ protected static function boot() { static::bootTraits(); } /** * Boot all of the bootable traits on the model. * * @return void */ protected static function bootTraits() { foreach (class_uses_recursive(get_called_class()) as $trait) { if (method_exists(get_called_class(), $method = 'boot'.class_basename($trait))) { forward_static_call([get_called_class(), $method]); } } } /** * Clear the list of booted models so they will be re-booted. * * @return void */ public static function clearBootedModels() { static::$booted = []; } /** * Register a new global scope on the model. * * @param \Illuminate\Database\Eloquent\ScopeInterface $scope * @return void */ public static function addGlobalScope(ScopeInterface $scope) { static::$globalScopes[get_called_class()][get_class($scope)] = $scope; } /** * Determine if a model has a global scope. * * @param \Illuminate\Database\Eloquent\ScopeInterface $scope * @return bool */ public static function hasGlobalScope($scope) { return ! is_null(static::getGlobalScope($scope)); } /** * Get a global scope registered with the model. * * @param \Illuminate\Database\Eloquent\ScopeInterface $scope * @return \Illuminate\Database\Eloquent\ScopeInterface|null */ public static function getGlobalScope($scope) { return Arr::first(static::$globalScopes[get_called_class()], function ($key, $value) use ($scope) { return $scope instanceof $value; }); } /** * Get the global scopes for this class instance. * * @return \Illuminate\Database\Eloquent\ScopeInterface[] */ public function getGlobalScopes() { return Arr::get(static::$globalScopes, get_class($this), []); } /** * Register an observer with the Model. * * @param object|string $class * @param int $priority * @return void */ public static function observe($class, $priority = 0) { $instance = new static; $className = is_string($class) ? $class : get_class($class); // When registering a model observer, we will spin through the possible events // and determine if this observer has that method. If it does, we will hook // it into the model's event system, making it convenient to watch these. foreach ($instance->getObservableEvents() as $event) { if (method_exists($class, $event)) { static::registerModelEvent($event, $className.'@'.$event, $priority); } } } /** * Fill the model with an array of attributes. * * @param array $attributes * @return $this * * @throws \Illuminate\Database\Eloquent\MassAssignmentException */ public function fill(array $attributes) { $totallyGuarded = $this->totallyGuarded(); foreach ($this->fillableFromArray($attributes) as $key => $value) { $key = $this->removeTableFromKey($key); // The developers may choose to place some attributes in the "fillable" // array, which means only those attributes may be set through mass // assignment to the model, and all others will just be ignored. if ($this->isFillable($key)) { $this->setAttribute($key, $value); } elseif ($totallyGuarded) { throw new MassAssignmentException($key); } } return $this; } /** * Fill the model with an array of attributes. Force mass assignment. * * @param array $attributes * @return $this */ public function forceFill(array $attributes) { // Since some versions of PHP have a bug that prevents it from properly // binding the late static context in a closure, we will first store // the model in a variable, which we will then use in the closure. $model = $this; return static::unguarded(function () use ($model, $attributes) { return $model->fill($attributes); }); } /** * Get the fillable attributes of a given array. * * @param array $attributes * @return array */ protected function fillableFromArray(array $attributes) { if (count($this->fillable) > 0 && ! static::$unguarded) { return array_intersect_key($attributes, array_flip($this->fillable)); } return $attributes; } /** * Create a new instance of the given model. * * @param array $attributes * @param bool $exists * @return static */ public function newInstance($attributes = [], $exists = false) { // This method just provides a convenient way for us to generate fresh model // instances of this current model. It is particularly useful during the // hydration of new objects via the Eloquent query builder instances. $model = new static((array) $attributes); $model->exists = $exists; return $model; } /** * Create a new model instance that is existing. * * @param array $attributes * @param string|null $connection * @return static */ public function newFromBuilder($attributes = [], $connection = null) { $model = $this->newInstance([], true); $model->setRawAttributes((array) $attributes, true); $model->setConnection($connection ?: $this->connection); return $model; } /** * Create a collection of models from plain arrays. * * @param array $items * @param string|null $connection * @return \Illuminate\Database\Eloquent\Collection */ public static function hydrate(array $items, $connection = null) { $instance = (new static)->setConnection($connection); $items = array_map(function ($item) use ($instance) { return $instance->newFromBuilder($item); }, $items); return $instance->newCollection($items); } /** * Create a collection of models from a raw query. * * @param string $query * @param array $bindings * @param string|null $connection * @return \Illuminate\Database\Eloquent\Collection */ public static function hydrateRaw($query, $bindings = [], $connection = null) { $instance = (new static)->setConnection($connection); $items = $instance->getConnection()->select($query, $bindings); return static::hydrate($items, $connection); } /** * Save a new model and return the instance. * * @param array $attributes * @return static */ public static function create(array $attributes = []) { $model = new static($attributes); $model->save(); return $model; } /** * Save a new model and return the instance. Allow mass-assignment. * * @param array $attributes * @return static */ public static function forceCreate(array $attributes) { // Since some versions of PHP have a bug that prevents it from properly // binding the late static context in a closure, we will first store // the model in a variable, which we will then use in the closure. $model = new static; return static::unguarded(function () use ($model, $attributes) { return $model->create($attributes); }); } /** * Get the first record matching the attributes or create it. * * @param array $attributes * @return static */ public static function firstOrCreate(array $attributes) { if (! is_null($instance = static::where($attributes)->first())) { return $instance; } return static::create($attributes); } /** * Get the first record matching the attributes or instantiate it. * * @param array $attributes * @return static */ public static function firstOrNew(array $attributes) { if (! is_null($instance = static::where($attributes)->first())) { return $instance; } return new static($attributes); } /** * Create or update a record matching the attributes, and fill it with values. * * @param array $attributes * @param array $values * @return static */ public static function updateOrCreate(array $attributes, array $values = []) { $instance = static::firstOrNew($attributes); $instance->fill($values)->save(); return $instance; } /** * Begin querying the model. * * @return \Illuminate\Database\Eloquent\Builder */ public static function query() { return (new static)->newQuery(); } /** * Begin querying the model on a given connection. * * @param string|null $connection * @return \Illuminate\Database\Eloquent\Builder */ public static function on($connection = null) { // First we will just create a fresh instance of this model, and then we can // set the connection on the model so that it is be used for the queries // we execute, as well as being set on each relationship we retrieve. $instance = new static; $instance->setConnection($connection); return $instance->newQuery(); } /** * Begin querying the model on the write connection. * * @return \Illuminate\Database\Query\Builder */ public static function onWriteConnection() { $instance = new static; return $instance->newQuery()->useWritePdo(); } /** * Get all of the models from the database. * * @param array|mixed $columns * @return \Illuminate\Database\Eloquent\Collection|static[] */ public static function all($columns = ['*']) { $columns = is_array($columns) ? $columns : func_get_args(); $instance = new static; return $instance->newQuery()->get($columns); } /** * Find a model by its primary key or return new static. * * @param mixed $id * @param array $columns * @return \Illuminate\Support\Collection|static */ public static function findOrNew($id, $columns = ['*']) { if (! is_null($model = static::find($id, $columns))) { return $model; } return new static; } /** * Reload a fresh model instance from the database. * * @param array $with * @return $this|null */ public function fresh(array $with = []) { if (! $this->exists) { return; } $key = $this->getKeyName(); return static::with($with)->where($key, $this->getKey())->first(); } /** * Eager load relations on the model. * * @param array|string $relations * @return $this */ public function load($relations) { if (is_string($relations)) { $relations = func_get_args(); } $query = $this->newQuery()->with($relations); $query->eagerLoadRelations([$this]); return $this; } /** * Begin querying a model with eager loading. * * @param array|string $relations * @return \Illuminate\Database\Eloquent\Builder|static */ public static function with($relations) { if (is_string($relations)) { $relations = func_get_args(); } $instance = new static; return $instance->newQuery()->with($relations); } /** * Append attributes to query when building a query. * * @param array|string $attributes * @return $this */ public function append($attributes) { if (is_string($attributes)) { $attributes = func_get_args(); } $this->appends = array_unique( array_merge($this->appends, $attributes) ); return $this; } /** * Define a one-to-one relationship. * * @param string $related * @param string $foreignKey * @param string $localKey * @return \Illuminate\Database\Eloquent\Relations\HasOne */ public function hasOne($related, $foreignKey = null, $localKey = null) { $foreignKey = $foreignKey ?: $this->getForeignKey(); $instance = new $related; $localKey = $localKey ?: $this->getKeyName(); return new HasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey); } /** * Define a polymorphic one-to-one relationship. * * @param string $related * @param string $name * @param string $type * @param string $id * @param string $localKey * @return \Illuminate\Database\Eloquent\Relations\MorphOne */ public function morphOne($related, $name, $type = null, $id = null, $localKey = null) { $instance = new $related; list($type, $id) = $this->getMorphs($name, $type, $id); $table = $instance->getTable(); $localKey = $localKey ?: $this->getKeyName(); return new MorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey); } /** * Define an inverse one-to-one or many relationship. * * @param string $related * @param string $foreignKey * @param string $otherKey * @param string $relation * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null) { // If no relation name was given, we will use this debug backtrace to extract // the calling method's name and use that as the relationship name as most // of the time this will be what we desire to use for the relationships. if (is_null($relation)) { list($current, $caller) = debug_backtrace(false, 2); $relation = $caller['function']; } // If no foreign key was supplied, we can use a backtrace to guess the proper // foreign key name by using the name of the relationship function, which // when combined with an "_id" should conventionally match the columns. if (is_null($foreignKey)) { $foreignKey = Str::snake($relation).'_id'; } $instance = new $related; // Once we have the foreign key names, we'll just create a new Eloquent query // for the related models and returns the relationship instance which will // actually be responsible for retrieving and hydrating every relations. $query = $instance->newQuery(); $otherKey = $otherKey ?: $instance->getKeyName(); return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation); } /** * Define a polymorphic, inverse one-to-one or many relationship. * * @param string $name * @param string $type * @param string $id * @return \Illuminate\Database\Eloquent\Relations\MorphTo */ public function morphTo($name = null, $type = null, $id = null) { // If no name is provided, we will use the backtrace to get the function name // since that is most likely the name of the polymorphic interface. We can // use that to get both the class and foreign key that will be utilized. if (is_null($name)) { list($current, $caller) = debug_backtrace(false, 2); $name = Str::snake($caller['function']); } list($type, $id) = $this->getMorphs($name, $type, $id); // If the type value is null it is probably safe to assume we're eager loading // the relationship. When that is the case we will pass in a dummy query as // there are multiple types in the morph and we can't use single queries. if (is_null($class = $this->$type)) { return new MorphTo( $this->newQuery(), $this, $id, null, $type, $name ); } // If we are not eager loading the relationship we will essentially treat this // as a belongs-to style relationship since morph-to extends that class and // we will pass in the appropriate values so that it behaves as expected. else { $class = $this->getActualClassNameForMorph($class); $instance = new $class; return new MorphTo( $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name ); } } /** * Retrieve the fully qualified class name from a slug. * * @param string $class * @return string */ public function getActualClassNameForMorph($class) { return Arr::get(Relation::morphMap(), $class, $class); } /** * Define a one-to-many relationship. * * @param string $related * @param string $foreignKey * @param string $localKey * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function hasMany($related, $foreignKey = null, $localKey = null) { $foreignKey = $foreignKey ?: $this->getForeignKey(); $instance = new $related; $localKey = $localKey ?: $this->getKeyName(); return new HasMany($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey); } /** * Define a has-many-through relationship. * * @param string $related * @param string $through * @param string|null $firstKey * @param string|null $secondKey * @param string|null $localKey * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough */ public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null) { $through = new $through; $firstKey = $firstKey ?: $this->getForeignKey(); $secondKey = $secondKey ?: $through->getForeignKey(); $localKey = $localKey ?: $this->getKeyName(); return new HasManyThrough((new $related)->newQuery(), $this, $through, $firstKey, $secondKey, $localKey); } /** * Define a polymorphic one-to-many relationship. * * @param string $related * @param string $name * @param string $type * @param string $id * @param string $localKey * @return \Illuminate\Database\Eloquent\Relations\MorphMany */ public function morphMany($related, $name, $type = null, $id = null, $localKey = null) { $instance = new $related; // Here we will gather up the morph type and ID for the relationship so that we // can properly query the intermediate table of a relation. Finally, we will // get the table and create the relationship instances for the developers. list($type, $id) = $this->getMorphs($name, $type, $id); $table = $instance->getTable(); $localKey = $localKey ?: $this->getKeyName(); return new MorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey); } /** * Define a many-to-many relationship. * * @param string $related * @param string $table * @param string $foreignKey * @param string $otherKey * @param string $relation * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null) { // If no relationship name was passed, we will pull backtraces to get the // name of the calling function. We will use that function name as the // title of this relation since that is a great convention to apply. if (is_null($relation)) { $relation = $this->getBelongsToManyCaller(); } // First, we'll need to determine the foreign key and "other key" for the // relationship. Once we have determined the keys we'll make the query // instances as well as the relationship instances we need for this. $foreignKey = $foreignKey ?: $this->getForeignKey(); $instance = new $related; $otherKey = $otherKey ?: $instance->getForeignKey(); // If no table name was provided, we can guess it by concatenating the two // models using underscores in alphabetical order. The two model names // are transformed to snake case from their default CamelCase also. if (is_null($table)) { $table = $this->joiningTable($related); } // Now we're ready to create a new query builder for the related model and // the relationship instances for the relation. The relations will set // appropriate query constraint and entirely manages the hydrations. $query = $instance->newQuery(); return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation); } /** * Define a polymorphic many-to-many relationship. * * @param string $related * @param string $name * @param string $table * @param string $foreignKey * @param string $otherKey * @param bool $inverse * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ public function morphToMany($related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false) { $caller = $this->getBelongsToManyCaller(); // First, we will need to determine the foreign key and "other key" for the // relationship. Once we have determined the keys we will make the query // instances, as well as the relationship instances we need for these. $foreignKey = $foreignKey ?: $name.'_id'; $instance = new $related; $otherKey = $otherKey ?: $instance->getForeignKey(); // Now we're ready to create a new query builder for this related model and // the relationship instances for this relation. This relations will set // appropriate query constraints then entirely manages the hydrations. $query = $instance->newQuery(); $table = $table ?: Str::plural($name); return new MorphToMany( $query, $this, $name, $table, $foreignKey, $otherKey, $caller, $inverse ); } /** * Define a polymorphic, inverse many-to-many relationship. * * @param string $related * @param string $name * @param string $table * @param string $foreignKey * @param string $otherKey * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ public function morphedByMany($related, $name, $table = null, $foreignKey = null, $otherKey = null) { $foreignKey = $foreignKey ?: $this->getForeignKey(); // For the inverse of the polymorphic many-to-many relations, we will change // the way we determine the foreign and other keys, as it is the opposite // of the morph-to-many method since we're figuring out these inverses. $otherKey = $otherKey ?: $name.'_id'; return $this->morphToMany($related, $name, $table, $foreignKey, $otherKey, true); } /** * Get the relationship name of the belongs to many. * * @return string */ protected function getBelongsToManyCaller() { $self = __FUNCTION__; $caller = Arr::first(debug_backtrace(false), function ($key, $trace) use ($self) { $caller = $trace['function']; return ! in_array($caller, Model::$manyMethods) && $caller != $self; }); return ! is_null($caller) ? $caller['function'] : null; } /** * Get the joining table name for a many-to-many relation. * * @param string $related * @return string */ public function joiningTable($related) { // The joining table name, by convention, is simply the snake cased models // sorted alphabetically and concatenated with an underscore, so we can // just sort the models and join them together to get the table name. $base = Str::snake(class_basename($this)); $related = Str::snake(class_basename($related)); $models = [$related, $base]; // Now that we have the model names in an array we can just sort them and // use the implode function to join them together with an underscores, // which is typically used by convention within the database system. sort($models); return strtolower(implode('_', $models)); } /** * Destroy the models for the given IDs. * * @param array|int $ids * @return int */ public static function destroy($ids) { // We'll initialize a count here so we will return the total number of deletes // for the operation. The developers can then check this number as a boolean // type value or get this total count of records deleted for logging, etc. $count = 0; $ids = is_array($ids) ? $ids : func_get_args(); $instance = new static; // We will actually pull the models from the database table and call delete on // each of them individually so that their events get fired properly with a // correct set of attributes in case the developers wants to check these. $key = $instance->getKeyName(); foreach ($instance->whereIn($key, $ids)->get() as $model) { if ($model->delete()) { $count++; } } return $count; } /** * Delete the model from the database. * * @return bool|null * @throws \Exception */ public function delete() { if (is_null($this->getKeyName())) { throw new Exception('No primary key defined on model.'); } if ($this->exists) { if ($this->fireModelEvent('deleting') === false) { return false; } // Here, we'll touch the owning models, verifying these timestamps get updated // for the models. This will allow any caching to get broken on the parents // by the timestamp. Then we will go ahead and delete the model instance. $this->touchOwners(); $this->performDeleteOnModel(); $this->exists = false; // Once the model has been deleted, we will fire off the deleted event so that // the developers may hook into post-delete operations. We will then return // a boolean true as the delete is presumably successful on the database. $this->fireModelEvent('deleted', false); return true; } } /** * Force a hard delete on a soft deleted model. * * This method protects developers from running forceDelete when trait is missing. * * @return bool|null */ public function forceDelete() { return $this->delete(); } /** * Perform the actual delete query on this model instance. * * @return void */ protected function performDeleteOnModel() { $this->setKeysForSaveQuery($this->newQueryWithoutScopes())->delete(); } /** * Register a saving model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function saving($callback, $priority = 0) { static::registerModelEvent('saving', $callback, $priority); } /** * Register a saved model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function saved($callback, $priority = 0) { static::registerModelEvent('saved', $callback, $priority); } /** * Register an updating model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function updating($callback, $priority = 0) { static::registerModelEvent('updating', $callback, $priority); } /** * Register an updated model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function updated($callback, $priority = 0) { static::registerModelEvent('updated', $callback, $priority); } /** * Register a creating model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function creating($callback, $priority = 0) { static::registerModelEvent('creating', $callback, $priority); } /** * Register a created model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function created($callback, $priority = 0) { static::registerModelEvent('created', $callback, $priority); } /** * Register a deleting model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function deleting($callback, $priority = 0) { static::registerModelEvent('deleting', $callback, $priority); } /** * Register a deleted model event with the dispatcher. * * @param \Closure|string $callback * @param int $priority * @return void */ public static function deleted($callback, $priority = 0) { static::registerModelEvent('deleted', $callback, $priority); } /** * Remove all of the event listeners for the model. * * @return void */ public static function flushEventListeners() { if (! isset(static::$dispatcher)) { return; } $instance = new static; foreach ($instance->getObservableEvents() as $event) { static::$dispatcher->forget("eloquent.{$event}: ".get_called_class()); } } /** * Register a model event with the dispatcher. * * @param string $event * @param \Closure|string $callback * @param int $priority * @return void */ protected static function registerModelEvent($event, $callback, $priority = 0) { if (isset(static::$dispatcher)) { $name = get_called_class(); static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback, $priority); } } /** * Get the observable event names. * * @return array */ public function getObservableEvents() { return array_merge( [ 'creating', 'created', 'updating', 'updated', 'deleting', 'deleted', 'saving', 'saved', 'restoring', 'restored', ], $this->observables ); } /** * Set the observable event names. * * @param array $observables * @return $this */ public function setObservableEvents(array $observables) { $this->observables = $observables; return $this; } /** * Add an observable event name. * * @param array|mixed $observables * @return void */ public function addObservableEvents($observables) { $observables = is_array($observables) ? $observables : func_get_args(); $this->observables = array_unique(array_merge($this->observables, $observables)); } /** * Remove an observable event name. * * @param array|mixed $observables * @return void */ public function removeObservableEvents($observables) { $observables = is_array($observables) ? $observables : func_get_args(); $this->observables = array_diff($this->observables, $observables); } /** * Increment a column's value by a given amount. * * @param string $column * @param int $amount * @return int */ protected function increment($column, $amount = 1) { return $this->incrementOrDecrement($column, $amount, 'increment'); } /** * Decrement a column's value by a given amount. * * @param string $column * @param int $amount * @return int */ protected function decrement($column, $amount = 1) { return $this->incrementOrDecrement($column, $amount, 'decrement'); } /** * Run the increment or decrement method on the model. * * @param string $column * @param int $amount * @param string $method * @return int */ protected function incrementOrDecrement($column, $amount, $method) { $query = $this->newQuery(); if (! $this->exists) { return $query->{$method}($column, $amount); } $this->incrementOrDecrementAttributeValue($column, $amount, $method); return $query->where($this->getKeyName(), $this->getKey())->{$method}($column, $amount); } /** * Increment the underlying attribute value and sync with original. * * @param string $column * @param int $amount * @param string $method * @return void */ protected function incrementOrDecrementAttributeValue($column, $amount, $method) { $this->{$column} = $this->{$column} + ($method == 'increment' ? $amount : $amount * -1); $this->syncOriginalAttribute($column); } /** * Update the model in the database. * * @param array $attributes * @return bool|int */ public function update(array $attributes = []) { if (! $this->exists) { return $this->newQuery()->update($attributes); } return $this->fill($attributes)->save(); } /** * Save the model and all of its relationships. * * @return bool */ public function push() { if (! $this->save()) { return false; } // To sync all of the relationships to the database, we will simply spin through // the relationships and save each model via this "push" method, which allows // us to recurse into all of these nested relations for the model instance. foreach ($this->relations as $models) { $models = $models instanceof Collection ? $models->all() : [$models]; foreach (array_filter($models) as $model) { if (! $model->push()) { return false; } } } return true; } /** * Save the model to the database. * * @param array $options * @return bool */ public function save(array $options = []) { $query = $this->newQueryWithoutScopes(); // If the "saving" event returns false we'll bail out of the save and return // false, indicating that the save failed. This provides a chance for any // listeners to cancel save operations if validations fail or whatever. if ($this->fireModelEvent('saving') === false) { return false; } // If the model already exists in the database we can just update our record // that is already in this database using the current IDs in this "where" // clause to only update this model. Otherwise, we'll just insert them. if ($this->exists) { $saved = $this->performUpdate($query, $options); } // If the model is brand new, we'll insert it into our database and set the // ID attribute on the model to the value of the newly inserted row's ID // which is typically an auto-increment value managed by the database. else { $saved = $this->performInsert($query, $options); } if ($saved) { $this->finishSave($options); } return $saved; } /** * Finish processing on a successful save operation. * * @param array $options * @return void */ protected function finishSave(array $options) { $this->fireModelEvent('saved', false); $this->syncOriginal(); if (Arr::get($options, 'touch', true)) { $this->touchOwners(); } } /** * Perform a model update operation. * * @param \Illuminate\Database\Eloquent\Builder $query * @param array $options * @return bool */ protected function performUpdate(Builder $query, array $options = []) { $dirty = $this->getDirty(); if (count($dirty) > 0) { // If the updating event returns false, we will cancel the update operation so // developers can hook Validation systems into their models and cancel this // operation if the model does not pass validation. Otherwise, we update. if ($this->fireModelEvent('updating') === false) { return false; } // First we need to create a fresh query instance and touch the creation and // update timestamp on the model which are maintained by us for developer // convenience. Then we will just continue saving the model instances. if ($this->timestamps && Arr::get($options, 'timestamps', true)) { $this->updateTimestamps(); } // Once we have run the update operation, we will fire the "updated" event for // this model instance. This will allow developers to hook into these after // models are updated, giving them a chance to do any special processing. $dirty = $this->getDirty(); if (count($dirty) > 0) { $numRows = $this->setKeysForSaveQuery($query)->update($dirty); $this->fireModelEvent('updated', false); } } return true; } /** * Perform a model insert operation. * * @param \Illuminate\Database\Eloquent\Builder $query * @param array $options * @return bool */ protected function performInsert(Builder $query, array $options = []) { if ($this->fireModelEvent('creating') === false) { return false; } // First we'll need to create a fresh query instance and touch the creation and // update timestamps on this model, which are maintained by us for developer // convenience. After, we will just continue saving these model instances. if ($this->timestamps && Arr::get($options, 'timestamps', true)) { $this->updateTimestamps(); } // If the model has an incrementing key, we can use the "insertGetId" method on // the query builder, which will give us back the final inserted ID for this // table from the database. Not all tables have to be incrementing though. $attributes = $this->attributes; if ($this->incrementing) { $this->insertAndSetId($query, $attributes); } // If the table isn't incrementing we'll simply insert these attributes as they // are. These attribute arrays must contain an "id" column previously placed // there by the developer as the manually determined key for these models. else { $query->insert($attributes); } // We will go ahead and set the exists property to true, so that it is set when // the created event is fired, just in case the developer tries to update it // during the event. This will allow them to do so and run an update here. $this->exists = true; $this->wasRecentlyCreated = true; $this->fireModelEvent('created', false); return true; } /** * Insert the given attributes and set the ID on the model. * * @param \Illuminate\Database\Eloquent\Builder $query * @param array $attributes * @return void */ protected function insertAndSetId(Builder $query, $attributes) { $id = $query->insertGetId($attributes, $keyName = $this->getKeyName()); $this->setAttribute($keyName, $id); } /** * Touch the owning relations of the model. * * @return void */ public function touchOwners() { foreach ($this->touches as $relation) { $this->$relation()->touch(); if ($this->$relation instanceof self) { $this->$relation->touchOwners(); } elseif ($this->$relation instanceof Collection) { $this->$relation->each(function (Model $relation) { $relation->touchOwners(); }); } } } /** * Determine if the model touches a given relation. * * @param string $relation * @return bool */ public function touches($relation) { return in_array($relation, $this->touches); } /** * Fire the given event for the model. * * @param string $event * @param bool $halt * @return mixed */ protected function fireModelEvent($event, $halt = true) { if (! isset(static::$dispatcher)) { return true; } // We will append the names of the class to the event to distinguish it from // other model events that are fired, allowing us to listen on each model // event set individually instead of catching event for all the models. $event = "eloquent.{$event}: ".get_class($this); $method = $halt ? 'until' : 'fire'; return static::$dispatcher->$method($event, $this); } /** * Set the keys for a save update query. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ protected function setKeysForSaveQuery(Builder $query) { $query->where($this->getKeyName(), '=', $this->getKeyForSaveQuery()); return $query; } /** * Get the primary key value for a save query. * * @return mixed */ protected function getKeyForSaveQuery() { if (isset($this->original[$this->getKeyName()])) { return $this->original[$this->getKeyName()]; } return $this->getAttribute($this->getKeyName()); } /** * Update the model's update timestamp. * * @return bool */ public function touch() { if (! $this->timestamps) { return false; } $this->updateTimestamps(); return $this->save(); } /** * Update the creation and update timestamps. * * @return void */ protected function updateTimestamps() { $time = $this->freshTimestamp(); if (! $this->isDirty(static::UPDATED_AT)) { $this->setUpdatedAt($time); } if (! $this->exists && ! $this->isDirty(static::CREATED_AT)) { $this->setCreatedAt($time); } } /** * Set the value of the "created at" attribute. * * @param mixed $value * @return $this */ public function setCreatedAt($value) { $this->{static::CREATED_AT} = $value; return $this; } /** * Set the value of the "updated at" attribute. * * @param mixed $value * @return $this */ public function setUpdatedAt($value) { $this->{static::UPDATED_AT} = $value; return $this; } /** * Get the name of the "created at" column. * * @return string */ public function getCreatedAtColumn() { return static::CREATED_AT; } /** * Get the name of the "updated at" column. * * @return string */ public function getUpdatedAtColumn() { return static::UPDATED_AT; } /** * Get a fresh timestamp for the model. * * @return \Carbon\Carbon */ public function freshTimestamp() { return new Carbon; } /** * Get a fresh timestamp for the model. * * @return string */ public function freshTimestampString() { return $this->fromDateTime($this->freshTimestamp()); } /** * Get a new query builder for the model's table. * * @return \Illuminate\Database\Eloquent\Builder */ public function newQuery() { $builder = $this->newQueryWithoutScopes(); return $this->applyGlobalScopes($builder); } /** * Get a new query instance without a given scope. * * @param \Illuminate\Database\Eloquent\ScopeInterface $scope * @return \Illuminate\Database\Eloquent\Builder */ public function newQueryWithoutScope($scope) { $this->getGlobalScope($scope)->remove($builder = $this->newQuery(), $this); return $builder; } /** * Get a new query builder that doesn't have any global scopes. * * @return \Illuminate\Database\Eloquent\Builder|static */ public function newQueryWithoutScopes() { $builder = $this->newEloquentBuilder( $this->newBaseQueryBuilder() ); // Once we have the query builders, we will set the model instances so the // builder can easily access any information it may need from the model // while it is constructing and executing various queries against it. return $builder->setModel($this)->with($this->with); } /** * Apply all of the global scopes to an Eloquent builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @return \Illuminate\Database\Eloquent\Builder */ public function applyGlobalScopes($builder) { foreach ($this->getGlobalScopes() as $scope) { $scope->apply($builder, $this); } return $builder; } /** * Remove all of the global scopes from an Eloquent builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @return \Illuminate\Database\Eloquent\Builder */ public function removeGlobalScopes($builder) { foreach ($this->getGlobalScopes() as $scope) { $scope->remove($builder, $this); } return $builder; } /** * Create a new Eloquent query builder for the model. * * @param \Illuminate\Database\Query\Builder $query * @return \Illuminate\Database\Eloquent\Builder|static */ public function newEloquentBuilder($query) { return new Builder($query); } /** * Get a new query builder instance for the connection. * * @return \Illuminate\Database\Query\Builder */ protected function newBaseQueryBuilder() { $conn = $this->getConnection(); $grammar = $conn->getQueryGrammar(); return new QueryBuilder($conn, $grammar, $conn->getPostProcessor()); } /** * Create a new Eloquent Collection instance. * * @param array $models * @return \Illuminate\Database\Eloquent\Collection */ public function newCollection(array $models = []) { return new Collection($models); } /** * Create a new pivot model instance. * * @param \Illuminate\Database\Eloquent\Model $parent * @param array $attributes * @param string $table * @param bool $exists * @return \Illuminate\Database\Eloquent\Relations\Pivot */ public function newPivot(Model $parent, array $attributes, $table, $exists) { return new Pivot($parent, $attributes, $table, $exists); } /** * Get the table associated with the model. * * @return string */ public function getTable() { if (isset($this->table)) { return $this->table; } return str_replace('\\', '', Str::snake(Str::plural(class_basename($this)))); } /** * Set the table associated with the model. * * @param string $table * @return $this */ public function setTable($table) { $this->table = $table; return $this; } /** * Get the value of the model's primary key. * * @return mixed */ public function getKey() { return $this->getAttribute($this->getKeyName()); } /** * Get the queueable identity for the entity. * * @return mixed */ public function getQueueableId() { return $this->getKey(); } /** * Get the primary key for the model. * * @return string */ public function getKeyName() { return $this->primaryKey; } /** * Set the primary key for the model. * * @param string $key * @return $this */ public function setKeyName($key) { $this->primaryKey = $key; return $this; } /** * Get the table qualified key name. * * @return string */ public function getQualifiedKeyName() { return $this->getTable().'.'.$this->getKeyName(); } /** * Get the value of the model's route key. * * @return mixed */ public function getRouteKey() { return $this->getAttribute($this->getRouteKeyName()); } /** * Get the route key for the model. * * @return string */ public function getRouteKeyName() { return $this->getKeyName(); } /** * Determine if the model uses timestamps. * * @return bool */ public function usesTimestamps() { return $this->timestamps; } /** * Get the polymorphic relationship columns. * * @param string $name * @param string $type * @param string $id * @return array */ protected function getMorphs($name, $type, $id) { $type = $type ?: $name.'_type'; $id = $id ?: $name.'_id'; return [$type, $id]; } /** * Get the class name for polymorphic relations. * * @return string */ public function getMorphClass() { $morphMap = Relation::morphMap(); $class = get_class($this); if (! empty($morphMap) && in_array($class, $morphMap)) { return array_search($class, $morphMap, true); } return $this->morphClass ?: $class; } /** * Get the number of models to return per page. * * @return int */ public function getPerPage() { return $this->perPage; } /** * Set the number of models to return per page. * * @param int $perPage * @return $this */ public function setPerPage($perPage) { $this->perPage = $perPage; return $this; } /** * Get the default foreign key name for the model. * * @return string */ public function getForeignKey() { return Str::snake(class_basename($this)).'_id'; } /** * Get the hidden attributes for the model. * * @return array */ public function getHidden() { return $this->hidden; } /** * Set the hidden attributes for the model. * * @param array $hidden * @return $this */ public function setHidden(array $hidden) { $this->hidden = $hidden; return $this; } /** * Add hidden attributes for the model. * * @param array|string|null $attributes * @return void */ public function addHidden($attributes = null) { $attributes = is_array($attributes) ? $attributes : func_get_args(); $this->hidden = array_merge($this->hidden, $attributes); } /** * Make the given, typically hidden, attributes visible. * * @param array|string $attributes * @return $this */ public function withHidden($attributes) { $this->hidden = array_diff($this->hidden, (array) $attributes); return $this; } /** * Get the visible attributes for the model. * * @return array */ public function getVisible() { return $this->visible; } /** * Set the visible attributes for the model. * * @param array $visible * @return $this */ public function setVisible(array $visible) { $this->visible = $visible; return $this; } /** * Add visible attributes for the model. * * @param array|string|null $attributes * @return void */ public function addVisible($attributes = null) { $attributes = is_array($attributes) ? $attributes : func_get_args(); $this->visible = array_merge($this->visible, $attributes); } /** * Set the accessors to append to model arrays. * * @param array $appends * @return $this */ public function setAppends(array $appends) { $this->appends = $appends; return $this; } /** * Get the fillable attributes for the model. * * @return array */ public function getFillable() { return $this->fillable; } /** * Set the fillable attributes for the model. * * @param array $fillable * @return $this */ public function fillable(array $fillable) { $this->fillable = $fillable; return $this; } /** * Get the guarded attributes for the model. * * @return array */ public function getGuarded() { return $this->guarded; } /** * Set the guarded attributes for the model. * * @param array $guarded * @return $this */ public function guard(array $guarded) { $this->guarded = $guarded; return $this; } /** * Disable all mass assignable restrictions. * * @param bool $state * @return void */ public static function unguard($state = true) { static::$unguarded = $state; } /** * Enable the mass assignment restrictions. * * @return void */ public static function reguard() { static::$unguarded = false; } /** * Determine if current state is "unguarded". * * @return bool */ public static function isUnguarded() { return static::$unguarded; } /** * Run the given callable while being unguarded. * * @param callable $callback * @return mixed */ public static function unguarded(callable $callback) { if (static::$unguarded) { return $callback(); } static::unguard(); $result = $callback(); static::reguard(); return $result; } /** * Determine if the given attribute may be mass assigned. * * @param string $key * @return bool */ public function isFillable($key) { if (static::$unguarded) { return true; } // If the key is in the "fillable" array, we can of course assume that it's // a fillable attribute. Otherwise, we will check the guarded array when // we need to determine if the attribute is black-listed on the model. if (in_array($key, $this->fillable)) { return true; } if ($this->isGuarded($key)) { return false; } return empty($this->fillable) && ! Str::startsWith($key, '_'); } /** * Determine if the given key is guarded. * * @param string $key * @return bool */ public function isGuarded($key) { return in_array($key, $this->guarded) || $this->guarded == ['*']; } /** * Determine if the model is totally guarded
Close sidebar
Back
Please note that all pasted data is publicly available.
Twitter
GitHub
Use setting
Back
Please note that all pasted data is publicly available.
Twitter
GitHub
Use setting