ery on this model instance. * * @return void */ protected function performDeleteOnModel() { $this->setKeysForSaveQuery($this->newModelQuery())->delete(); $this->exists = false; } /** * Begin querying the model. * * @return \NinjaTables\Framework\Database\Orm\Builder */ public static function query() { return (new static)->newQuery(); } /** * Get a new query builder for the model's table. * * @return \NinjaTables\Framework\Database\Orm\Builder */ public function newQuery() { return $this->registerGlobalScopes($this->newQueryWithoutScopes()); } /** * Get a new query builder that doesn't have any global scopes or eager loading. * * @return \NinjaTables\Framework\Database\Orm\Builder|static */ public function newModelQuery() { return $this->newOrmBuilder( $this->newBaseQueryBuilder() )->setModel($this); } /** * Get a new query builder with no relationships loaded. * * @return \NinjaTables\Framework\Database\Orm\Builder */ public function newQueryWithoutRelationships() { return $this->registerGlobalScopes($this->newModelQuery()); } /** * Register the global scopes for this builder instance. * * @param \NinjaTables\Framework\Database\Orm\Builder $builder * @return \NinjaTables\Framework\Database\Orm\Builder */ public function registerGlobalScopes($builder) { foreach ($this->getGlobalScopes() as $identifier => $scope) { $builder->withGlobalScope($identifier, $scope); } return $builder; } /** * Get a new query builder that doesn't have any global scopes. * * @return \NinjaTables\Framework\Database\Orm\Builder|static */ public function newQueryWithoutScopes() { return $this->newModelQuery() ->with($this->with) ->withCount($this->withCount); } /** * Get a new query instance without a given scope. * * @param \NinjaTables\Framework\Database\Orm\Scope|string $scope * @return \NinjaTables\Framework\Database\Orm\Builder */ public function newQueryWithoutScope($scope) { return $this->newQuery()->withoutGlobalScope($scope); } /** * Get a new query to restore one or more models by their queueable IDs. * * @param array|int $ids * @return \NinjaTables\Framework\Database\Orm\Builder */ public function newQueryForRestoration($ids) { return is_array($ids) ? $this->newQueryWithoutScopes()->whereIn($this->getQualifiedKeyName(), $ids) : $this->newQueryWithoutScopes()->whereKey($ids); } /** * Create a new Orm query builder for the model. * * @param \NinjaTables\Framework\Database\Query\Builder $query * @return \NinjaTables\Framework\Database\Orm\Builder|static */ public function newOrmBuilder($query) { return new Builder($query); } /** * Get a new query builder instance for the connection. * * @return \NinjaTables\Framework\Database\Query\Builder */ protected function newBaseQueryBuilder() { return $this->getConnection()->query(); } /** * Create a new Orm Collection instance. * * @param array $models * @return \NinjaTables\Framework\Database\Orm\Collection */ public function newCollection(array $models = []) { return new Collection($models); } /** * Create a new pivot model instance. * * @param \NinjaTables\Framework\Database\Orm\Model $parent * @param array $attributes * @param string $table * @param bool $exists * @param string|null $using * @return \NinjaTables\Framework\Database\Orm\Relations\Pivot */ public function newPivot( self $parent, array $attributes, $table, $exists, $using = null ) { return $using ? $using::fromRawAttributes( $parent, $attributes, $table, $exists ) : Pivot::fromAttributes($parent, $attributes, $table, $exists); } /** * Determine if the model has a given scope. * * @param string $scope * @return bool */ public function hasNamedScope($scope) { return method_exists($this, 'scope'.ucfirst($scope)); } /** * Apply the given named scope if possible. * * @param string $scope * @param array $parameters * @return mixed */ public function callNamedScope($scope, array $parameters = []) { return $this->{'scope'.ucfirst($scope)}(...$parameters); } /** * Convert the model instance to an array. * * @return array */ public function toArray() { return array_merge($this->attributesToArray(), $this->relationsToArray()); } /** * Convert the model instance to JSON. * * @param int $options * @return string * * @throws \NinjaTables\Framework\Database\Orm\JsonEncodingException */ public function toJson($options = 0) { $json = json_encode($this->jsonSerialize(), $options); if (JSON_ERROR_NONE !== json_last_error()) { throw JsonEncodingException::forModel($this, json_last_error_msg()); } return $json; } /** * Convert the object into something JSON serializable. * * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize() { 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()) ->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())->firstOrFail()->attributes ); $this->load(Helper::collect($this->relations)->reject(function ($relation) { return $relation instanceof Pivot || (is_object($relation) && in_array(AsPivot::class, static::classUsesRecursive($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 = [ $this->getKeyName(), $this->getCreatedAtColumn(), $this->getUpdatedAtColumn(), ]; $attributes = Arr::except( $this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults ); return Helper::tap(new static, function ($instance) use ($attributes) { $instance->setRawAttributes($attributes); $instance->setRelations($this->relations); $instance->fireModelEvent('replicating', false); }); } /** * Determine if two models have the same ID and belong to the same table. * * @param \NinjaTables\Framework\Database\Orm\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 \NinjaTables\Framework\Database\Orm\Model|null $model * @return bool */ public function isNot($model) { return ! $this->is($model); } /** * Get the database connection for the model. * * @return \NinjaTables\Framework\Database\Query\WPDBConnection */ public function getConnection() { return static::resolveConnection($this->getConnectionName()); } /** * Get the current connection name for the model. * * @return string|null */ public function getConnectionName() { return $this->connection; } /** * Set the connection associated with the model. * * @param string|null $name * @return $this */ public function setConnection($name) { $this->connection = $name; return $this; } /** * Resolve a connection instance. * * @param string|null $connection * @return \NinjaTables\Framework\Database\Query\WPDBConnection */ public static function resolveConnection($connection = null) { return static::$resolver->connection($connection); } /** * Get the connection resolver instance. * * @return \NinjaTables\Framework\Database\ConnectionResolverInterface */ public static function getConnectionResolver() { return static::$resolver; } /** * Set the connection resolver instance. * * @param \NinjaTables\Framework\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(static::classBasename($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 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 \NinjaTables\Framework\Database\Orm\Model|null */ public function resolveRouteBinding($value, $field = null) { return $this->resolveRouteBindingQuery( $this, $value, $field )->firstOrFail(); } /** * Retrieve the model for a bound value. * * @param \NinjaTables\Framework\Database\Orm\Model $query * @param mixed $value * @param string|null $field * @return \NinjaTables\Framework\Database\Orm\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(static::classBasename($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 lazy loading is disabled. * * @return bool */ public static function preventsLazyLoading() { return static::$modelsShouldPreventLazyLoading; } /** * 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 columns of the model (optionally with detail). * * @return array */ public static function getColumns($details = false) { $table = (new static)->getTable(); if (!$details) { return Schema::getColumns($table); } return Schema::getColumnsWithTypes($table); } /** * 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 */ #[\ReturnTypeWillChange] public function offsetExists($offset) { return ! is_null($this->getAttribute($offset)); } /** * Get the value for a given offset. * * @param mixed $offset * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->getAttribute($offset); } /** * Set the value for a given offset. * * @param mixed $offset * @param mixed $value * @return void */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->setAttribute($offset, $value); } /** * Unset the value for a given offset. * * @param mixed $offset * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->attributes[$offset], $this->relations[$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'])) { return $this->$method(...$parameters); } if ($resolver = ( static::$relationResolvers[get_class($this)][$method] ?? null )) { return $resolver($this); } 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) { return (new static)->$method(...$parameters); } /** * Convert the model to its string representation. * * @return string */ public function __toString() { return $this->escapeWhenCastingToString ? esc_html($this->toJson()) : $this->toJson(); } /** * Indicate that the object's string representation should be escaped when __toString is invoked. * * @param bool $escape * @return self */ 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 = []; return array_keys(get_object_vars($this)); } /** * When a model is being unserialized, check if it needs to be booted. * * @return void */ public function __wakeup() { $this->bootIfNotBooted(); $this->initializeTraits(); } }