ttribute($scope); } /** * Apply the given named scope if possible. * * @param string $scope * @param array $parameters * @return mixed */ public function callNamedScope($scope, array $parameters = []) { if ($this->isScopeMethodWithAttribute($scope)) { return $this->{$scope}(...$parameters); } return $this->{'scope'.ucfirst($scope)}(...$parameters); } /** * Determine if the given method has a scope attribute. * * @param string $method * @return bool */ protected static function isScopeMethodWithAttribute(string $method) { return method_exists(static::class, $method) && (new ReflectionMethod(static::class, $method)) ->getAttributes(LocalScope::class) !== []; } /** * Convert the model instance to an array. * * @return array */ public function toArray() { return $this->withoutRecursion( fn () => array_merge($this->attributesToArray(), $this->relationsToArray()), fn () => $this->attributesToArray(), ); } /** * Convert the model instance to JSON. * * @param int $options * @return string * * @throws \Illuminate\Database\Eloquent\JsonEncodingException */ public function toJson($options = 0) { try { $json = json_encode($this->jsonSerialize(), $options | JSON_THROW_ON_ERROR); } catch (JsonException $e) { throw JsonEncodingException::forModel($this, $e->getMessage()); } return $json; } /** * Convert the model instance to pretty print formatted JSON. * * @param int $options * @return string * * @throws \Illuminate\Database\Eloquent\JsonEncodingException */ public function toPrettyJson(int $options = 0) { return $this->toJson(JSON_PRETTY_PRINT | $options); } /** * Convert the object into something JSON serializable. * * @return mixed */ public function jsonSerialize(): mixed { return $this->toArray(); } /** * Reload a fresh model instance from the database. * * @param array|string $with * @return static|null */ public function fresh($with = []) { if (! $this->exists) { return; } return $this->setKeysForSelectQuery($this->newQueryWithoutScopes()) ->useWritePdo() ->with(is_string($with) ? func_get_args() : $with) ->first(); } /** * Reload the current model instance with fresh attributes from the database. * * @return $this */ public function refresh() { if (! $this->exists) { return $this; } $this->setRawAttributes( $this->setKeysForSelectQuery($this->newQueryWithoutScopes()) ->useWritePdo() ->firstOrFail() ->attributes ); $this->load((new BaseCollection($this->relations))->reject( fn ($relation) => $relation instanceof Pivot || (is_object($relation) && in_array(AsPivot::class, class_uses_recursive($relation), true)) )->keys()->all()); $this->syncOriginal(); return $this; } /** * Clone the model into a new, non-existing instance. * * @param array|null $except * @return static */ public function replicate(?array $except = null) { $defaults = array_values(array_filter([ $this->getKeyName(), $this->getCreatedAtColumn(), $this->getUpdatedAtColumn(), ...$this->uniqueIds(), 'laravel_through_key', ])); $attributes = Arr::except( $this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults ); return tap(new static, function ($instance) use ($attributes) { $instance->setRawAttributes($attributes); $instance->setRelations($this->relations); $instance->fireModelEvent('replicating', false); }); } /** * Clone the model into a new, non-existing instance without raising any events. * * @param array|null $except * @return static */ public function replicateQuietly(?array $except = null) { return static::withoutEvents(fn () => $this->replicate($except)); } /** * Determine if two models have the same ID and belong to the same table. * * @param \Illuminate\Database\Eloquent\Model|null $model * @return bool */ public function is($model) { return ! is_null($model) && $this->getKey() === $model->getKey() && $this->getTable() === $model->getTable() && $this->getConnectionName() === $model->getConnectionName(); } /** * Determine if two models are not the same. * * @param \Illuminate\Database\Eloquent\Model|null $model * @return bool */ public function isNot($model) { return ! $this->is($model); } /** * Get the database connection for the model. * * @return \Illuminate\Database\Connection */ public function getConnection() { return static::resolveConnection($this->getConnectionName()); } /** * Get the current connection name for the model. * * @return string|null */ public function getConnectionName() { return enum_value($this->connection); } /** * Set the connection associated with the model. * * @param \UnitEnum|string|null $name * @return $this */ public function setConnection($name) { $this->connection = $name; return $this; } /** * Resolve a connection instance. * * @param \UnitEnum|string|null $connection * @return \Illuminate\Database\Connection */ public static function resolveConnection($connection = null) { return static::$resolver->connection($connection); } /** * Get the connection resolver instance. * * @return \Illuminate\Database\ConnectionResolverInterface|null */ public static function getConnectionResolver() { return static::$resolver; } /** * Set the connection resolver instance. * * @param \Illuminate\Database\ConnectionResolverInterface $resolver * @return void */ public static function setConnectionResolver(Resolver $resolver) { static::$resolver = $resolver; } /** * Unset the connection resolver for models. * * @return void */ public static function unsetConnectionResolver() { static::$resolver = null; } /** * Get the table associated with the model. * * @return string */ public function getTable() { return $this->table ?? Str::snake(Str::pluralStudly(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 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->qualifyColumn($this->getKeyName()); } /** * Get the auto-incrementing key type. * * @return string */ public function getKeyType() { return $this->keyType; } /** * Set the data type for the primary key. * * @param string $type * @return $this */ public function setKeyType($type) { $this->keyType = $type; return $this; } /** * Get the value indicating whether the IDs are incrementing. * * @return bool */ public function getIncrementing() { return $this->incrementing; } /** * Set whether IDs are incrementing. * * @param bool $value * @return $this */ public function setIncrementing($value) { $this->incrementing = $value; 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 queueable relationships for the entity. * * @return array */ public function getQueueableRelations() { return $this->withoutRecursion(function () { $relations = []; foreach ($this->getRelations() as $key => $relation) { if (! method_exists($this, $key)) { continue; } $relations[] = $key; if ($relation instanceof QueueableCollection) { foreach ($relation->getQueueableRelations() as $collectionValue) { $relations[] = $key.'.'.$collectionValue; } } if ($relation instanceof QueueableEntity) { foreach ($relation->getQueueableRelations() as $entityValue) { $relations[] = $key.'.'.$entityValue; } } } return array_unique($relations); }, []); } /** * Get the queueable connection for the entity. * * @return string|null */ public function getQueueableConnection() { return $this->getConnectionName(); } /** * 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(); } /** * Retrieve the model for a bound value. * * @param mixed $value * @param string|null $field * @return \Illuminate\Database\Eloquent\Model|null */ public function resolveRouteBinding($value, $field = null) { return $this->resolveRouteBindingQuery($this, $value, $field)->first(); } /** * Retrieve the model for a bound value. * * @param mixed $value * @param string|null $field * @return \Illuminate\Database\Eloquent\Model|null */ public function resolveSoftDeletableRouteBinding($value, $field = null) { return $this->resolveRouteBindingQuery($this, $value, $field)->withTrashed()->first(); } /** * Retrieve the child model for a bound value. * * @param string $childType * @param mixed $value * @param string|null $field * @return \Illuminate\Database\Eloquent\Model|null */ public function resolveChildRouteBinding($childType, $value, $field) { return $this->resolveChildRouteBindingQuery($childType, $value, $field)->first(); } /** * Retrieve the child model for a bound value. * * @param string $childType * @param mixed $value * @param string|null $field * @return \Illuminate\Database\Eloquent\Model|null */ public function resolveSoftDeletableChildRouteBinding($childType, $value, $field) { return $this->resolveChildRouteBindingQuery($childType, $value, $field)->withTrashed()->first(); } /** * Retrieve the child model query for a bound value. * * @param string $childType * @param mixed $value * @param string|null $field * @return \Illuminate\Database\Eloquent\Relations\Relation<\Illuminate\Database\Eloquent\Model, $this, *> */ protected function resolveChildRouteBindingQuery($childType, $value, $field) { $relationship = $this->{$this->childRouteBindingRelationshipName($childType)}(); $field = $field ?: $relationship->getRelated()->getRouteKeyName(); if ($relationship instanceof HasManyThrough || $relationship instanceof BelongsToMany) { $field = $relationship->getRelated()->qualifyColumn($field); } return $relationship instanceof Model ? $relationship->resolveRouteBindingQuery($relationship, $value, $field) : $relationship->getRelated()->resolveRouteBindingQuery($relationship, $value, $field); } /** * Retrieve the child route model binding relationship name for the given child type. * * @param string $childType * @return string */ protected function childRouteBindingRelationshipName($childType) { return Str::plural(Str::camel($childType)); } /** * Retrieve the model for a bound value. * * @param \Illuminate\Database\Eloquent\Model|\Illuminate\Contracts\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $query * @param mixed $value * @param string|null $field * @return \Illuminate\Contracts\Database\Eloquent\Builder */ public function resolveRouteBindingQuery($query, $value, $field = null) { return $query->where($field ?? $this->getRouteKeyName(), $value); } /** * Get the default foreign key name for the model. * * @return string */ public function getForeignKey() { return Str::snake(class_basename($this)).'_'.$this->getKeyName(); } /** * 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; } /** * Determine if the model is soft deletable. */ public static function isSoftDeletable(): bool { return static::$isSoftDeletable[static::class] ??= in_array(SoftDeletes::class, class_uses_recursive(static::class)); } /** * Determine if the model is prunable. */ protected function isPrunable(): bool { return self::$isPrunable[static::class] ??= in_array(Prunable::class, class_uses_recursive(static::class)) || static::isMassPrunable(); } /** * Determine if the model is mass prunable. */ protected function isMassPrunable(): bool { return self::$isMassPrunable[static::class] ??= in_array(MassPrunable::class, class_uses_recursive(static::class)); } /** * Determine if lazy loading is disabled. * * @return bool */ public static function preventsLazyLoading() { return static::$modelsShouldPreventLazyLoading; } /** * Determine if relationships are being automatically eager loaded when accessed. * * @return bool */ public static function isAutomaticallyEagerLoadingRelationships() { return static::$modelsShouldAutomaticallyEagerLoadRelationships; } /** * Determine if discarding guarded attribute fills is disabled. * * @return bool */ public static function preventsSilentlyDiscardingAttributes() { return static::$modelsShouldPreventSilentlyDiscardingAttributes; } /** * Determine if accessing missing attributes is disabled. * * @return bool */ public static function preventsAccessingMissingAttributes() { return static::$modelsShouldPreventAccessingMissingAttributes; } /** * Get the broadcast channel route definition that is associated with the given entity. * * @return string */ public function broadcastChannelRoute() { return str_replace('\\', '.', get_class($this)).'.{'.Str::camel(class_basename($this)).'}'; } /** * Get the broadcast channel name that is associated with the given entity. * * @return string */ public function broadcastChannel() { return str_replace('\\', '.', get_class($this)).'.'.$this->getKey(); } /** * Dynamically retrieve attributes on the model. * * @param string $key * @return mixed */ public function __get($key) { return $this->getAttribute($key); } /** * Dynamically set attributes on the model. * * @param string $key * @param mixed $value * @return void */ public function __set($key, $value) { $this->setAttribute($key, $value); } /** * Determine if the given attribute exists. * * @param mixed $offset * @return bool */ public function offsetExists($offset): bool { $shouldPrevent = static::$modelsShouldPreventAccessingMissingAttributes; static::$modelsShouldPreventAccessingMissingAttributes = false; try { return ! is_null($this->getAttribute($offset)); } finally { static::$modelsShouldPreventAccessingMissingAttributes = $shouldPrevent; } } /** * Get the value for a given offset. * * @param mixed $offset * @return mixed */ public function offsetGet($offset): mixed { return $this->getAttribute($offset); } /** * Set the value for a given offset. * * @param mixed $offset * @param mixed $value * @return void */ public function offsetSet($offset, $value): void { $this->setAttribute($offset, $value); } /** * Unset the value for a given offset. * * @param mixed $offset * @return void */ public function offsetUnset($offset): void { unset( $this->attributes[$offset], $this->relations[$offset], $this->attributeCastCache[$offset], $this->classCastCache[$offset] ); } /** * Determine if an attribute or relation exists on the model. * * @param string $key * @return bool */ public function __isset($key) { return $this->offsetExists($key); } /** * Unset an attribute on the model. * * @param string $key * @return void */ public function __unset($key) { $this->offsetUnset($key); } /** * Handle dynamic method calls into the model. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement', 'incrementQuietly', 'decrementQuietly'])) { return $this->$method(...$parameters); } if ($resolver = $this->relationResolver(static::class, $method)) { return $resolver($this); } if (Str::startsWith($method, 'through') && method_exists($this, $relationMethod = (new SupportStringable($method))->after('through')->lcfirst()->toString())) { return $this->through($relationMethod); } return $this->forwardCallTo($this->newQuery(), $method, $parameters); } /** * Handle dynamic static method calls into the model. * * @param string $method * @param array $parameters * @return mixed */ public static function __callStatic($method, $parameters) { if (static::isScopeMethodWithAttribute($method)) { return static::query()->$method(...$parameters); } return (new static)->$method(...$parameters); } /** * Convert the model to its string representation. * * @return string */ public function __toString() { return $this->escapeWhenCastingToString ? e($this->toJson()) : $this->toJson(); } /** * Indicate that the object's string representation should be escaped when __toString is invoked. * * @param bool $escape * @return $this */ public function escapeWhenCastingToString($escape = true) { $this->escapeWhenCastingToString = $escape; return $this; } /** * Prepare the object for serialization. * * @return array */ public function __sleep() { $this->mergeAttributesFromCachedCasts(); $this->classCastCache = []; $this->attributeCastCache = []; $this->relationAutoloadCallback = null; $this->relationAutoloadContext = null; $keys = get_object_vars($this); if (version_compare(PHP_VERSION, '8.4.0', '>=')) { foreach ((new ReflectionClass($this))->getProperties() as $property) { if ($property->hasHooks()) { unset($keys[$property->getName()]); } } } return array_keys($keys); } /** * When a model is being unserialized, check if it needs to be booted. * * @return void */ public function __wakeup() { $this->bootIfNotBooted(); $this->initializeTraits(); if (static::isAutomaticallyEagerLoadingRelationships()) { $this->withRelationshipAutoloading(); } } }