Revision 148 | Blame | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed
<?phpnamespace Illuminate\Database\Eloquent;use BadMethodCallException;use Closure;use Exception;use Illuminate\Contracts\Database\Eloquent\Builder as BuilderContract;use Illuminate\Contracts\Support\Arrayable;use Illuminate\Database\Concerns\BuildsQueries;use Illuminate\Database\Eloquent\Concerns\QueriesRelationships;use Illuminate\Database\Eloquent\Relations\BelongsToMany;use Illuminate\Database\Eloquent\Relations\Relation;use Illuminate\Database\Query\Builder as QueryBuilder;use Illuminate\Database\RecordsNotFoundException;use Illuminate\Pagination\Paginator;use Illuminate\Support\Arr;use Illuminate\Support\Str;use Illuminate\Support\Traits\ForwardsCalls;use ReflectionClass;use ReflectionMethod;/*** @property-read HigherOrderBuilderProxy $orWhere* @property-read HigherOrderBuilderProxy $whereNot* @property-read HigherOrderBuilderProxy $orWhereNot** @mixin \Illuminate\Database\Query\Builder*/class Builder implements BuilderContract{use BuildsQueries, ForwardsCalls, QueriesRelationships {BuildsQueries::sole as baseSole;}/*** The base query builder instance.** @var \Illuminate\Database\Query\Builder*/protected $query;/*** The model being queried.** @var \Illuminate\Database\Eloquent\Model*/protected $model;/*** The relationships that should be eager loaded.** @var array*/protected $eagerLoad = [];/*** All of the globally registered builder macros.** @var array*/protected static $macros = [];/*** All of the locally registered builder macros.** @var array*/protected $localMacros = [];/*** A replacement for the typical delete function.** @var \Closure*/protected $onDelete;/*** The properties that should be returned from query builder.** @var string[]*/protected $propertyPassthru = ['from',];/*** The methods that should be returned from query builder.** @var string[]*/protected $passthru = ['aggregate','average','avg','count','dd','doesntExist','doesntExistOr','dump','exists','existsOr','explain','getBindings','getConnection','getGrammar','implode','insert','insertGetId','insertOrIgnore','insertUsing','max','min','raw','rawValue','sum','toSql',];/*** Applied global scopes.** @var array*/protected $scopes = [];/*** Removed global scopes.** @var array*/protected $removedScopes = [];/*** Create a new Eloquent query builder instance.** @param \Illuminate\Database\Query\Builder $query* @return void*/public function __construct(QueryBuilder $query){$this->query = $query;}/*** Create and return an un-saved model instance.** @param array $attributes* @return \Illuminate\Database\Eloquent\Model|static*/public function make(array $attributes = []){return $this->newModelInstance($attributes);}/*** Register a new global scope.** @param string $identifier* @param \Illuminate\Database\Eloquent\Scope|\Closure $scope* @return $this*/public function withGlobalScope($identifier, $scope){$this->scopes[$identifier] = $scope;if (method_exists($scope, 'extend')) {$scope->extend($this);}return $this;}/*** Remove a registered global scope.** @param \Illuminate\Database\Eloquent\Scope|string $scope* @return $this*/public function withoutGlobalScope($scope){if (! is_string($scope)) {$scope = get_class($scope);}unset($this->scopes[$scope]);$this->removedScopes[] = $scope;return $this;}/*** Remove all or passed registered global scopes.** @param array|null $scopes* @return $this*/public function withoutGlobalScopes(array $scopes = null){if (! is_array($scopes)) {$scopes = array_keys($this->scopes);}foreach ($scopes as $scope) {$this->withoutGlobalScope($scope);}return $this;}/*** Get an array of global scopes that were removed from the query.** @return array*/public function removedScopes(){return $this->removedScopes;}/*** Add a where clause on the primary key to the query.** @param mixed $id* @return $this*/public function whereKey($id){if ($id instanceof Model) {$id = $id->getKey();}if (is_array($id) || $id instanceof Arrayable) {if (in_array($this->model->getKeyType(), ['int', 'integer'])) {$this->query->whereIntegerInRaw($this->model->getQualifiedKeyName(), $id);} else {$this->query->whereIn($this->model->getQualifiedKeyName(), $id);}return $this;}if ($id !== null && $this->model->getKeyType() === 'string') {$id = (string) $id;}return $this->where($this->model->getQualifiedKeyName(), '=', $id);}/*** Add a where clause on the primary key to the query.** @param mixed $id* @return $this*/public function whereKeyNot($id){if ($id instanceof Model) {$id = $id->getKey();}if (is_array($id) || $id instanceof Arrayable) {if (in_array($this->model->getKeyType(), ['int', 'integer'])) {$this->query->whereIntegerNotInRaw($this->model->getQualifiedKeyName(), $id);} else {$this->query->whereNotIn($this->model->getQualifiedKeyName(), $id);}return $this;}if ($id !== null && $this->model->getKeyType() === 'string') {$id = (string) $id;}return $this->where($this->model->getQualifiedKeyName(), '!=', $id);}/*** Add a basic where clause to the query.** @param \Closure|string|array|\Illuminate\Database\Query\Expression $column* @param mixed $operator* @param mixed $value* @param string $boolean* @return $this*/public function where($column, $operator = null, $value = null, $boolean = 'and'){if ($column instanceof Closure && is_null($operator)) {$column($query = $this->model->newQueryWithoutRelationships());$this->query->addNestedWhereQuery($query->getQuery(), $boolean);} else {$this->query->where(...func_get_args());}return $this;}/*** Add a basic where clause to the query, and return the first result.** @param \Closure|string|array|\Illuminate\Database\Query\Expression $column* @param mixed $operator* @param mixed $value* @param string $boolean* @return \Illuminate\Database\Eloquent\Model|static|null*/public function firstWhere($column, $operator = null, $value = null, $boolean = 'and'){return $this->where(...func_get_args())->first();}/*** Add an "or where" clause to the query.** @param \Closure|array|string|\Illuminate\Database\Query\Expression $column* @param mixed $operator* @param mixed $value* @return $this*/public function orWhere($column, $operator = null, $value = null){[$value, $operator] = $this->query->prepareValueAndOperator($value, $operator, func_num_args() === 2);return $this->where($column, $operator, $value, 'or');}/*** Add a basic "where not" clause to the query.** @param \Closure|string|array|\Illuminate\Database\Query\Expression $column* @param mixed $operator* @param mixed $value* @param string $boolean* @return $this*/public function whereNot($column, $operator = null, $value = null, $boolean = 'and'){return $this->where($column, $operator, $value, $boolean.' not');}/*** Add an "or where not" clause to the query.** @param \Closure|array|string|\Illuminate\Database\Query\Expression $column* @param mixed $operator* @param mixed $value* @return $this*/public function orWhereNot($column, $operator = null, $value = null){return $this->whereNot($column, $operator, $value, 'or');}/*** Add an "order by" clause for a timestamp to the query.** @param string|\Illuminate\Database\Query\Expression $column* @return $this*/public function latest($column = null){if (is_null($column)) {$column = $this->model->getCreatedAtColumn() ?? 'created_at';}$this->query->latest($column);return $this;}/*** Add an "order by" clause for a timestamp to the query.** @param string|\Illuminate\Database\Query\Expression $column* @return $this*/public function oldest($column = null){if (is_null($column)) {$column = $this->model->getCreatedAtColumn() ?? 'created_at';}$this->query->oldest($column);return $this;}/*** Create a collection of models from plain arrays.** @param array $items* @return \Illuminate\Database\Eloquent\Collection*/public function hydrate(array $items){$instance = $this->newModelInstance();return $instance->newCollection(array_map(function ($item) use ($items, $instance) {$model = $instance->newFromBuilder($item);if (count($items) > 1) {$model->preventsLazyLoading = Model::preventsLazyLoading();}return $model;}, $items));}/*** Create a collection of models from a raw query.** @param string $query* @param array $bindings* @return \Illuminate\Database\Eloquent\Collection*/public function fromQuery($query, $bindings = []){return $this->hydrate($this->query->getConnection()->select($query, $bindings));}/*** Find a model by its primary key.** @param mixed $id* @param array|string $columns* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null*/public function find($id, $columns = ['*']){if (is_array($id) || $id instanceof Arrayable) {return $this->findMany($id, $columns);}return $this->whereKey($id)->first($columns);}/*** Find multiple models by their primary keys.** @param \Illuminate\Contracts\Support\Arrayable|array $ids* @param array|string $columns* @return \Illuminate\Database\Eloquent\Collection*/public function findMany($ids, $columns = ['*']){$ids = $ids instanceof Arrayable ? $ids->toArray() : $ids;if (empty($ids)) {return $this->model->newCollection();}return $this->whereKey($ids)->get($columns);}/*** Find a model by its primary key or throw an exception.** @param mixed $id* @param array|string $columns* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static|static[]** @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>*/public function findOrFail($id, $columns = ['*']){$result = $this->find($id, $columns);$id = $id instanceof Arrayable ? $id->toArray() : $id;if (is_array($id)) {if (count($result) !== count(array_unique($id))) {throw (new ModelNotFoundException)->setModel(get_class($this->model), array_diff($id, $result->modelKeys()));}return $result;}if (is_null($result)) {throw (new ModelNotFoundException)->setModel(get_class($this->model), $id);}return $result;}/*** Find a model by its primary key or return fresh model instance.** @param mixed $id* @param array|string $columns* @return \Illuminate\Database\Eloquent\Model|static*/public function findOrNew($id, $columns = ['*']){if (! is_null($model = $this->find($id, $columns))) {return $model;}return $this->newModelInstance();}/*** Find a model by its primary key or call a callback.** @param mixed $id* @param \Closure|array|string $columns* @param \Closure|null $callback* @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|mixed*/public function findOr($id, $columns = ['*'], Closure $callback = null){if ($columns instanceof Closure) {$callback = $columns;$columns = ['*'];}if (! is_null($model = $this->find($id, $columns))) {return $model;}return $callback();}/*** Get the first record matching the attributes or instantiate it.** @param array $attributes* @param array $values* @return \Illuminate\Database\Eloquent\Model|static*/public function firstOrNew(array $attributes = [], array $values = []){if (! is_null($instance = $this->where($attributes)->first())) {return $instance;}return $this->newModelInstance(array_merge($attributes, $values));}/*** Get the first record matching the attributes or create it.** @param array $attributes* @param array $values* @return \Illuminate\Database\Eloquent\Model|static*/public function firstOrCreate(array $attributes = [], array $values = []){if (! is_null($instance = $this->where($attributes)->first())) {return $instance;}return tap($this->newModelInstance(array_merge($attributes, $values)), function ($instance) {$instance->save();});}/*** Create or update a record matching the attributes, and fill it with values.** @param array $attributes* @param array $values* @return \Illuminate\Database\Eloquent\Model|static*/public function updateOrCreate(array $attributes, array $values = []){return tap($this->firstOrNew($attributes), function ($instance) use ($values) {$instance->fill($values)->save();});}/*** Execute the query and get the first result or throw an exception.** @param array|string $columns* @return \Illuminate\Database\Eloquent\Model|static** @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>*/public function firstOrFail($columns = ['*']){if (! is_null($model = $this->first($columns))) {return $model;}throw (new ModelNotFoundException)->setModel(get_class($this->model));}/*** Execute the query and get the first result or call a callback.** @param \Closure|array|string $columns* @param \Closure|null $callback* @return \Illuminate\Database\Eloquent\Model|static|mixed*/public function firstOr($columns = ['*'], Closure $callback = null){if ($columns instanceof Closure) {$callback = $columns;$columns = ['*'];}if (! is_null($model = $this->first($columns))) {return $model;}return $callback();}/*** Execute the query and get the first result if it's the sole matching record.** @param array|string $columns* @return \Illuminate\Database\Eloquent\Model** @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>* @throws \Illuminate\Database\MultipleRecordsFoundException*/public function sole($columns = ['*']){try {return $this->baseSole($columns);} catch (RecordsNotFoundException $exception) {throw (new ModelNotFoundException)->setModel(get_class($this->model));}}/*** Get a single column's value from the first result of a query.** @param string|\Illuminate\Database\Query\Expression $column* @return mixed*/public function value($column){if ($result = $this->first([$column])) {return $result->{Str::afterLast($column, '.')};}}/*** Get a single column's value from the first result of a query if it's the sole matching record.** @param string|\Illuminate\Database\Query\Expression $column* @return mixed** @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>* @throws \Illuminate\Database\MultipleRecordsFoundException*/public function soleValue($column){return $this->sole([$column])->{Str::afterLast($column, '.')};}/*** Get a single column's value from the first result of the query or throw an exception.** @param string|\Illuminate\Database\Query\Expression $column* @return mixed** @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>*/public function valueOrFail($column){return $this->firstOrFail([$column])->{Str::afterLast($column, '.')};}/*** Execute the query as a "select" statement.** @param array|string $columns* @return \Illuminate\Database\Eloquent\Collection|static[]*/public function get($columns = ['*']){$builder = $this->applyScopes();// If we actually found models we will also eager load any relationships that// have been specified as needing to be eager loaded, which will solve the// n+1 query issue for the developers to avoid running a lot of queries.if (count($models = $builder->getModels($columns)) > 0) {$models = $builder->eagerLoadRelations($models);}return $builder->getModel()->newCollection($models);}/*** Get the hydrated models without eager loading.** @param array|string $columns* @return \Illuminate\Database\Eloquent\Model[]|static[]*/public function getModels($columns = ['*']){return $this->model->hydrate($this->query->get($columns)->all())->all();}/*** Eager load the relationships for the models.** @param array $models* @return array*/public function eagerLoadRelations(array $models){foreach ($this->eagerLoad as $name => $constraints) {// For nested eager loads we'll skip loading them here and they will be set as an// eager load on the query to retrieve the relation so that they will be eager// loaded on that query, because that is where they get hydrated as models.if (! str_contains($name, '.')) {$models = $this->eagerLoadRelation($models, $name, $constraints);}}return $models;}/*** Eagerly load the relationship on a set of models.** @param array $models* @param string $name* @param \Closure $constraints* @return array*/protected function eagerLoadRelation(array $models, $name, Closure $constraints){// First we will "back up" the existing where conditions on the query so we can// add our eager constraints. Then we will merge the wheres that were on the// query back to it in order that any where conditions might be specified.$relation = $this->getRelation($name);$relation->addEagerConstraints($models);$constraints($relation);// Once we have the results, we just match those back up to their parent models// using the relationship instance. Then we just return the finished arrays// of models which have been eagerly hydrated and are readied for return.return $relation->match($relation->initRelation($models, $name),$relation->getEager(), $name);}/*** Get the relation instance for the given relation name.** @param string $name* @return \Illuminate\Database\Eloquent\Relations\Relation*/public function getRelation($name){// We want to run a relationship query without any constrains so that we will// not have to remove these where clauses manually which gets really hacky// and error prone. We don't want constraints because we add eager ones.$relation = Relation::noConstraints(function () use ($name) {try {return $this->getModel()->newInstance()->$name();} catch (BadMethodCallException $e) {throw RelationNotFoundException::make($this->getModel(), $name);}});$nested = $this->relationsNestedUnder($name);// If there are nested relationships set on the query, we will put those onto// the query instances so that they can be handled after this relationship// is loaded. In this way they will all trickle down as they are loaded.if (count($nested) > 0) {$relation->getQuery()->with($nested);}return $relation;}/*** Get the deeply nested relations for a given top-level relation.** @param string $relation* @return array*/protected function relationsNestedUnder($relation){$nested = [];// We are basically looking for any relationships that are nested deeper than// the given top-level relationship. We will just check for any relations// that start with the given top relations and adds them to our arrays.foreach ($this->eagerLoad as $name => $constraints) {if ($this->isNestedUnder($relation, $name)) {$nested[substr($name, strlen($relation.'.'))] = $constraints;}}return $nested;}/*** Determine if the relationship is nested.** @param string $relation* @param string $name* @return bool*/protected function isNestedUnder($relation, $name){return str_contains($name, '.') && str_starts_with($name, $relation.'.');}/*** Get a lazy collection for the given query.** @return \Illuminate\Support\LazyCollection*/public function cursor(){return $this->applyScopes()->query->cursor()->map(function ($record) {return $this->newModelInstance()->newFromBuilder($record);});}/*** Add a generic "order by" clause if the query doesn't already have one.** @return void*/protected function enforceOrderBy(){if (empty($this->query->orders) && empty($this->query->unionOrders)) {$this->orderBy($this->model->getQualifiedKeyName(), 'asc');}}/*** Get a collection with the values of a given column.** @param string|\Illuminate\Database\Query\Expression $column* @param string|null $key* @return \Illuminate\Support\Collection*/public function pluck($column, $key = null){$results = $this->toBase()->pluck($column, $key);// If the model has a mutator for the requested column, we will spin through// the results and mutate the values so that the mutated version of these// columns are returned as you would expect from these Eloquent models.if (! $this->model->hasGetMutator($column) &&! $this->model->hasCast($column) &&! in_array($column, $this->model->getDates())) {return $results;}return $results->map(function ($value) use ($column) {return $this->model->newFromBuilder([$column => $value])->{$column};});}/*** Paginate the given query.** @param int|null|\Closure $perPage* @param array|string $columns* @param string $pageName* @param int|null $page* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator** @throws \InvalidArgumentException*/public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null){$page = $page ?: Paginator::resolveCurrentPage($pageName);$total = $this->toBase()->getCountForPagination();$perPage = ($perPage instanceof Closure? $perPage($total): $perPage) ?: $this->model->getPerPage();$results = $total? $this->forPage($page, $perPage)->get($columns): $this->model->newCollection();return $this->paginator($results, $total, $perPage, $page, ['path' => Paginator::resolveCurrentPath(),'pageName' => $pageName,]);}/*** Paginate the given query into a simple paginator.** @param int|null $perPage* @param array|string $columns* @param string $pageName* @param int|null $page* @return \Illuminate\Contracts\Pagination\Paginator*/public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null){$page = $page ?: Paginator::resolveCurrentPage($pageName);$perPage = $perPage ?: $this->model->getPerPage();// Next we will set the limit and offset for this query so that when we get the// results we get the proper section of results. Then, we'll create the full// paginator instances for these results with the given page and per page.$this->skip(($page - 1) * $perPage)->take($perPage + 1);return $this->simplePaginator($this->get($columns), $perPage, $page, ['path' => Paginator::resolveCurrentPath(),'pageName' => $pageName,]);}/*** Paginate the given query into a cursor paginator.** @param int|null $perPage* @param array|string $columns* @param string $cursorName* @param \Illuminate\Pagination\Cursor|string|null $cursor* @return \Illuminate\Contracts\Pagination\CursorPaginator*/public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null){$perPage = $perPage ?: $this->model->getPerPage();return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);}/*** Ensure the proper order by required for cursor pagination.** @param bool $shouldReverse* @return \Illuminate\Support\Collection*/protected function ensureOrderForCursorPagination($shouldReverse = false){if (empty($this->query->orders) && empty($this->query->unionOrders)) {$this->enforceOrderBy();}if ($shouldReverse) {$this->query->orders = collect($this->query->orders)->map(function ($order) {$order['direction'] = $order['direction'] === 'asc' ? 'desc' : 'asc';return $order;})->toArray();}if ($this->query->unionOrders) {return collect($this->query->unionOrders);}return collect($this->query->orders);}/*** Save a new model and return the instance.** @param array $attributes* @return \Illuminate\Database\Eloquent\Model|$this*/public function create(array $attributes = []){return tap($this->newModelInstance($attributes), function ($instance) {$instance->save();});}/*** Save a new model and return the instance. Allow mass-assignment.** @param array $attributes* @return \Illuminate\Database\Eloquent\Model|$this*/public function forceCreate(array $attributes){return $this->model->unguarded(function () use ($attributes) {return $this->newModelInstance()->create($attributes);});}/*** Update records in the database.** @param array $values* @return int*/public function update(array $values){return $this->toBase()->update($this->addUpdatedAtColumn($values));}/*** Insert new records or update the existing ones.** @param array $values* @param array|string $uniqueBy* @param array|null $update* @return int*/public function upsert(array $values, $uniqueBy, $update = null){if (empty($values)) {return 0;}if (! is_array(reset($values))) {$values = [$values];}if (is_null($update)) {$update = array_keys(reset($values));}return $this->toBase()->upsert($this->addTimestampsToUpsertValues($values),$uniqueBy,$this->addUpdatedAtToUpsertColumns($update));}/*** Update the column's update timestamp.** @param string|null $column* @return int|false*/public function touch($column = null){$time = $this->model->freshTimestamp();if ($column) {return $this->toBase()->update([$column => $time]);}$column = $this->model->getUpdatedAtColumn();if (! $this->model->usesTimestamps() || is_null($column)) {return false;}return $this->toBase()->update([$column => $time]);}/*** Increment a column's value by a given amount.** @param string|\Illuminate\Database\Query\Expression $column* @param float|int $amount* @param array $extra* @return int*/public function increment($column, $amount = 1, array $extra = []){return $this->toBase()->increment($column, $amount, $this->addUpdatedAtColumn($extra));}/*** Decrement a column's value by a given amount.** @param string|\Illuminate\Database\Query\Expression $column* @param float|int $amount* @param array $extra* @return int*/public function decrement($column, $amount = 1, array $extra = []){return $this->toBase()->decrement($column, $amount, $this->addUpdatedAtColumn($extra));}/*** Add the "updated at" column to an array of values.** @param array $values* @return array*/protected function addUpdatedAtColumn(array $values){if (! $this->model->usesTimestamps() ||is_null($this->model->getUpdatedAtColumn())) {return $values;}$column = $this->model->getUpdatedAtColumn();$values = array_merge([$column => $this->model->freshTimestampString()],$values);$segments = preg_split('/\s+as\s+/i', $this->query->from);$qualifiedColumn = end($segments).'.'.$column;$values[$qualifiedColumn] = Arr::get($values, $qualifiedColumn, $values[$column]);unset($values[$column]);return $values;}/*** Add timestamps to the inserted values.** @param array $values* @return array*/protected function addTimestampsToUpsertValues(array $values){if (! $this->model->usesTimestamps()) {return $values;}$timestamp = $this->model->freshTimestampString();$columns = array_filter([$this->model->getCreatedAtColumn(),$this->model->getUpdatedAtColumn(),]);foreach ($columns as $column) {foreach ($values as &$row) {$row = array_merge([$column => $timestamp], $row);}}return $values;}/*** Add the "updated at" column to the updated columns.** @param array $update* @return array*/protected function addUpdatedAtToUpsertColumns(array $update){if (! $this->model->usesTimestamps()) {return $update;}$column = $this->model->getUpdatedAtColumn();if (! is_null($column) &&! array_key_exists($column, $update) &&! in_array($column, $update)) {$update[] = $column;}return $update;}/*** Delete records from the database.** @return mixed*/public function delete(){if (isset($this->onDelete)) {return call_user_func($this->onDelete, $this);}return $this->toBase()->delete();}/*** Run the default delete function on the builder.** Since we do not apply scopes here, the row will actually be deleted.** @return mixed*/public function forceDelete(){return $this->query->delete();}/*** Register a replacement for the default delete function.** @param \Closure $callback* @return void*/public function onDelete(Closure $callback){$this->onDelete = $callback;}/*** Determine if the given model has a scope.** @param string $scope* @return bool*/public function hasNamedScope($scope){return $this->model && $this->model->hasNamedScope($scope);}/*** Call the given local model scopes.** @param array|string $scopes* @return static|mixed*/public function scopes($scopes){$builder = $this;foreach (Arr::wrap($scopes) as $scope => $parameters) {// If the scope key is an integer, then the scope was passed as the value and// the parameter list is empty, so we will format the scope name and these// parameters here. Then, we'll be ready to call the scope on the model.if (is_int($scope)) {[$scope, $parameters] = [$parameters, []];}// Next we'll pass the scope callback to the callScope method which will take// care of grouping the "wheres" properly so the logical order doesn't get// messed up when adding scopes. Then we'll return back out the builder.$builder = $builder->callNamedScope($scope, Arr::wrap($parameters));}return $builder;}/*** Apply the scopes to the Eloquent builder instance and return it.** @return static*/public function applyScopes(){if (! $this->scopes) {return $this;}$builder = clone $this;foreach ($this->scopes as $identifier => $scope) {if (! isset($builder->scopes[$identifier])) {continue;}$builder->callScope(function (self $builder) use ($scope) {// If the scope is a Closure we will just go ahead and call the scope with the// builder instance. The "callScope" method will properly group the clauses// that are added to this query so "where" clauses maintain proper logic.if ($scope instanceof Closure) {$scope($builder);}// If the scope is a scope object, we will call the apply method on this scope// passing in the builder and the model instance. After we run all of these// scopes we will return back the builder instance to the outside caller.if ($scope instanceof Scope) {$scope->apply($builder, $this->getModel());}});}return $builder;}/*** Apply the given scope on the current builder instance.** @param callable $scope* @param array $parameters* @return mixed*/protected function callScope(callable $scope, array $parameters = []){array_unshift($parameters, $this);$query = $this->getQuery();// We will keep track of how many wheres are on the query before running the// scope so that we can properly group the added scope constraints in the// query as their own isolated nested where statement and avoid issues.$originalWhereCount = is_null($query->wheres)? 0 : count($query->wheres);$result = $scope(...$parameters) ?? $this;if (count((array) $query->wheres) > $originalWhereCount) {$this->addNewWheresWithinGroup($query, $originalWhereCount);}return $result;}/*** Apply the given named scope on the current builder instance.** @param string $scope* @param array $parameters* @return mixed*/protected function callNamedScope($scope, array $parameters = []){return $this->callScope(function (...$parameters) use ($scope) {return $this->model->callNamedScope($scope, $parameters);}, $parameters);}/*** Nest where conditions by slicing them at the given where count.** @param \Illuminate\Database\Query\Builder $query* @param int $originalWhereCount* @return void*/protected function addNewWheresWithinGroup(QueryBuilder $query, $originalWhereCount){// Here, we totally remove all of the where clauses since we are going to// rebuild them as nested queries by slicing the groups of wheres into// their own sections. This is to prevent any confusing logic order.$allWheres = $query->wheres;$query->wheres = [];$this->groupWhereSliceForScope($query, array_slice($allWheres, 0, $originalWhereCount));$this->groupWhereSliceForScope($query, array_slice($allWheres, $originalWhereCount));}/*** Slice where conditions at the given offset and add them to the query as a nested condition.** @param \Illuminate\Database\Query\Builder $query* @param array $whereSlice* @return void*/protected function groupWhereSliceForScope(QueryBuilder $query, $whereSlice){$whereBooleans = collect($whereSlice)->pluck('boolean');// Here we'll check if the given subset of where clauses contains any "or"// booleans and in this case create a nested where expression. That way// we don't add any unnecessary nesting thus keeping the query clean.if ($whereBooleans->contains('or')) {$query->wheres[] = $this->createNestedWhere($whereSlice, $whereBooleans->first());} else {$query->wheres = array_merge($query->wheres, $whereSlice);}}/*** Create a where array with nested where conditions.** @param array $whereSlice* @param string $boolean* @return array*/protected function createNestedWhere($whereSlice, $boolean = 'and'){$whereGroup = $this->getQuery()->forNestedWhere();$whereGroup->wheres = $whereSlice;return ['type' => 'Nested', 'query' => $whereGroup, 'boolean' => $boolean];}/*** Set the relationships that should be eager loaded.** @param string|array $relations* @param string|\Closure|null $callback* @return $this*/public function with($relations, $callback = null){if ($callback instanceof Closure) {$eagerLoad = $this->parseWithRelations([$relations => $callback]);} else {$eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);}$this->eagerLoad = array_merge($this->eagerLoad, $eagerLoad);return $this;}/*** Prevent the specified relations from being eager loaded.** @param mixed $relations* @return $this*/public function without($relations){$this->eagerLoad = array_diff_key($this->eagerLoad, array_flip(is_string($relations) ? func_get_args() : $relations));return $this;}/*** Set the relationships that should be eager loaded while removing any previously added eager loading specifications.** @param mixed $relations* @return $this*/public function withOnly($relations){$this->eagerLoad = [];return $this->with($relations);}/*** Create a new instance of the model being queried.** @param array $attributes* @return \Illuminate\Database\Eloquent\Model|static*/public function newModelInstance($attributes = []){return $this->model->newInstance($attributes)->setConnection($this->query->getConnection()->getName());}/*** Parse a list of relations into individuals.** @param array $relations* @return array*/protected function parseWithRelations(array $relations){if ($relations === []) {return [];}$results = [];foreach ($this->prepareNestedWithRelationships($relations) as $name => $constraints) {// We need to separate out any nested includes, which allows the developers// to load deep relationships using "dots" without stating each level of// the relationship with its own key in the array of eager-load names.$results = $this->addNestedWiths($name, $results);$results[$name] = $constraints;}return $results;}/*** Prepare nested with relationships.** @param array $relations* @param string $prefix* @return array*/protected function prepareNestedWithRelationships($relations, $prefix = ''){$preparedRelationships = [];if ($prefix !== '') {$prefix .= '.';}// If any of the relationships are formatted with the [$attribute => array()]// syntax, we shall loop over the nested relations and prepend each key of// this array while flattening into the traditional dot notation format.foreach ($relations as $key => $value) {if (! is_string($key) || ! is_array($value)) {continue;}[$attribute, $attributeSelectConstraint] = $this->parseNameAndAttributeSelectionConstraint($key);$preparedRelationships = array_merge($preparedRelationships,["{$prefix}{$attribute}" => $attributeSelectConstraint],$this->prepareNestedWithRelationships($value, "{$prefix}{$attribute}"),);unset($relations[$key]);}// We now know that the remaining relationships are in a dot notation format// and may be a string or Closure. We'll loop over them and ensure all of// the present Closures are merged + strings are made into constraints.foreach ($relations as $key => $value) {if (is_numeric($key) && is_string($value)) {[$key, $value] = $this->parseNameAndAttributeSelectionConstraint($value);}$preparedRelationships[$prefix.$key] = $this->combineConstraints([$value,$preparedRelationships[$prefix.$key] ?? static function () {//},]);}return $preparedRelationships;}/*** Combine an array of constraints into a single constraint.** @param array $constraints* @return \Closure*/protected function combineConstraints(array $constraints){return function ($builder) use ($constraints) {foreach ($constraints as $constraint) {$builder = $constraint($builder) ?? $builder;}return $builder;};}/*** Parse the attribute select constraints from the name.** @param string $name* @return array*/protected function parseNameAndAttributeSelectionConstraint($name){return str_contains($name, ':')? $this->createSelectWithConstraint($name): [$name, static function () {//}];}/*** Create a constraint to select the given columns for the relation.** @param string $name* @return array*/protected function createSelectWithConstraint($name){return [explode(':', $name)[0], static function ($query) use ($name) {$query->select(array_map(static function ($column) use ($query) {if (str_contains($column, '.')) {return $column;}return $query instanceof BelongsToMany? $query->getRelated()->getTable().'.'.$column: $column;}, explode(',', explode(':', $name)[1])));}];}/*** Parse the nested relationships in a relation.** @param string $name* @param array $results* @return array*/protected function addNestedWiths($name, $results){$progress = [];// If the relation has already been set on the result array, we will not set it// again, since that would override any constraints that were already placed// on the relationships. We will only set the ones that are not specified.foreach (explode('.', $name) as $segment) {$progress[] = $segment;if (! isset($results[$last = implode('.', $progress)])) {$results[$last] = static function () {//};}}return $results;}/*** Apply query-time casts to the model instance.** @param array $casts* @return $this*/public function withCasts($casts){$this->model->mergeCasts($casts);return $this;}/*** Get the underlying query builder instance.** @return \Illuminate\Database\Query\Builder*/public function getQuery(){return $this->query;}/*** Set the underlying query builder instance.** @param \Illuminate\Database\Query\Builder $query* @return $this*/public function setQuery($query){$this->query = $query;return $this;}/*** Get a base query builder instance.** @return \Illuminate\Database\Query\Builder*/public function toBase(){return $this->applyScopes()->getQuery();}/*** Get the relationships being eagerly loaded.** @return array*/public function getEagerLoads(){return $this->eagerLoad;}/*** Set the relationships being eagerly loaded.** @param array $eagerLoad* @return $this*/public function setEagerLoads(array $eagerLoad){$this->eagerLoad = $eagerLoad;return $this;}/*** Indicate that the given relationships should not be eagerly loaded.** @param array $relations* @return $this*/public function withoutEagerLoad(array $relations){$relations = array_diff(array_keys($this->model->getRelations()), $relations);return $this->with($relations);}/*** Flush the relationships being eagerly loaded.** @return $this*/public function withoutEagerLoads(){return $this->setEagerLoads([]);}/*** Get the default key name of the table.** @return string*/protected function defaultKeyName(){return $this->getModel()->getKeyName();}/*** Get the model instance being queried.** @return \Illuminate\Database\Eloquent\Model|static*/public function getModel(){return $this->model;}/*** Set a model instance for the model being queried.** @param \Illuminate\Database\Eloquent\Model $model* @return $this*/public function setModel(Model $model){$this->model = $model;$this->query->from($model->getTable());return $this;}/*** Qualify the given column name by the model's table.** @param string|\Illuminate\Database\Query\Expression $column* @return string*/public function qualifyColumn($column){return $this->model->qualifyColumn($column);}/*** Qualify the given columns with the model's table.** @param array|\Illuminate\Database\Query\Expression $columns* @return array*/public function qualifyColumns($columns){return $this->model->qualifyColumns($columns);}/*** Get the given macro by name.** @param string $name* @return \Closure*/public function getMacro($name){return Arr::get($this->localMacros, $name);}/*** Checks if a macro is registered.** @param string $name* @return bool*/public function hasMacro($name){return isset($this->localMacros[$name]);}/*** Get the given global macro by name.** @param string $name* @return \Closure*/public static function getGlobalMacro($name){return Arr::get(static::$macros, $name);}/*** Checks if a global macro is registered.** @param string $name* @return bool*/public static function hasGlobalMacro($name){return isset(static::$macros[$name]);}/*** Dynamically access builder proxies.** @param string $key* @return mixed** @throws \Exception*/public function __get($key){if (in_array($key, ['orWhere', 'whereNot', 'orWhereNot'])) {return new HigherOrderBuilderProxy($this, $key);}if (in_array($key, $this->propertyPassthru)) {return $this->toBase()->{$key};}throw new Exception("Property [{$key}] does not exist on the Eloquent builder instance.");}/*** Dynamically handle calls into the query instance.** @param string $method* @param array $parameters* @return mixed*/public function __call($method, $parameters){if ($method === 'macro') {$this->localMacros[$parameters[0]] = $parameters[1];return;}if ($this->hasMacro($method)) {array_unshift($parameters, $this);return $this->localMacros[$method](...$parameters);}if (static::hasGlobalMacro($method)) {$callable = static::$macros[$method];if ($callable instanceof Closure) {$callable = $callable->bindTo($this, static::class);}return $callable(...$parameters);}if ($this->hasNamedScope($method)) {return $this->callNamedScope($method, $parameters);}if (in_array($method, $this->passthru)) {return $this->toBase()->{$method}(...$parameters);}$this->forwardCallTo($this->query, $method, $parameters);return $this;}/*** Dynamically handle calls into the query instance.** @param string $method* @param array $parameters* @return mixed** @throws \BadMethodCallException*/public static function __callStatic($method, $parameters){if ($method === 'macro') {static::$macros[$parameters[0]] = $parameters[1];return;}if ($method === 'mixin') {return static::registerMixin($parameters[0], $parameters[1] ?? true);}if (! static::hasGlobalMacro($method)) {static::throwBadMethodCallException($method);}$callable = static::$macros[$method];if ($callable instanceof Closure) {$callable = $callable->bindTo(null, static::class);}return $callable(...$parameters);}/*** Register the given mixin with the builder.** @param string $mixin* @param bool $replace* @return void*/protected static function registerMixin($mixin, $replace){$methods = (new ReflectionClass($mixin))->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);foreach ($methods as $method) {if ($replace || ! static::hasGlobalMacro($method->name)) {$method->setAccessible(true);static::macro($method->name, $method->invoke($mixin));}}}/*** Clone the Eloquent query builder.** @return static*/public function clone(){return clone $this;}/*** Force a clone of the underlying query builder when cloning.** @return void*/public function __clone(){$this->query = clone $this->query;}}