diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 9bf56a5bc809..6dd1c40c67f2 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -710,7 +710,7 @@ public function getRelation($name) * @param string $relation * @return array */ - protected function nestedRelations($relation) + public function nestedRelations($relation) { $nested = []; @@ -822,7 +822,7 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C $query = $relation->{$queryType}($relation->getRelated()->newQuery(), $this); if ($callback) { - $this->applyCallbackToQuery($callback, [$query], $query->getQuery()); + $query->callScope($callback); } return $this->addHasWhere( @@ -936,7 +936,7 @@ public function orWhereHas($relation, Closure $callback, $operator = '>=', $coun */ protected function addHasWhere(Builder $hasQuery, Relation $relation, $operator, $count, $boolean) { - $this->mergeModelDefinedRelationWheresToHasQuery($hasQuery, $relation); + $hasQuery->mergeModelDefinedRelationConstraints($relation->getQuery()); if ($this->shouldRunExistsQuery($operator, $count)) { $not = ($operator === '<' && $count === 1); @@ -980,22 +980,21 @@ protected function whereCountQuery(QueryBuilder $query, $operator = '>=', $count } /** - * Merge the "wheres" from a relation query to a has query. + * Merge the constraints from a relation query to the current query. * - * @param \Illuminate\Database\Eloquent\Builder $hasQuery - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation + * @param \Illuminate\Database\Eloquent\Builder $relation * @return void */ - protected function mergeModelDefinedRelationWheresToHasQuery(Builder $hasQuery, Relation $relation) + public function mergeModelDefinedRelationConstraints(Builder $relation) { - $removedScopes = $hasQuery->removedScopes(); + $removedScopes = $relation->removedScopes(); - $relationQuery = $relation->withoutGlobalScopes($removedScopes)->toBase(); + $relationQuery = $relation->getQuery(); - // Here we have the "has" query and the original relation. We need to copy over any - // where clauses the developer may have put in the relationship function over to - // the has query, and then copy the bindings from the "has" query to the main. - $hasQuery->withoutGlobalScopes()->mergeWheres( + // Here we have some relation query and the original relation. We need to copy over any + // where clauses that the developer may have put in the relation definition function. + // We need to remove any global scopes that the developer already removed as well. + return $this->withoutGlobalScopes($removedScopes)->mergeWheres( $relationQuery->wheres, $relationQuery->getBindings() ); } @@ -1056,9 +1055,9 @@ public function withCount($relations) $relation->getRelated()->newQuery(), $this ); - call_user_func($constraints, $query); + $query->callScope($constraints); - $this->mergeModelDefinedRelationWheresToHasQuery($query, $relation); + $query->mergeModelDefinedRelationConstraints($relation->getQuery()); $this->selectSub($query->toBase(), snake_case($name).'_count'); } @@ -1127,37 +1126,24 @@ protected function parseNestedWith($name, $results) } /** - * Call the given model scope on the underlying model. - * - * @param string $scope - * @param array $parameters - * @return \Illuminate\Database\Query\Builder - */ - protected function callScope($scope, $parameters) - { - array_unshift($parameters, $this); - - return $this->applyCallbackToQuery([$this->model, $scope], $parameters); - } - - /** - * Apply the given callback to a supplied (or the current) builder instance. + * Apply the given scope on the current builder instance. * - * @param callable $callback + * @param callable $scope * @param array $parameters - * @param \Illuminate\Database\Query\Builder $query * @return mixed */ - protected function applyCallbackToQuery(callable $callback, $parameters = [], $query = null) + protected function callScope(callable $scope, $parameters = []) { - $query = $query ?: $this->getQuery(); + 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 = count($query->wheres); - $result = call_user_func_array($callback, $parameters) ?: $this; + $result = call_user_func_array($scope, $parameters) ?: $this; if ($this->shouldNestWheresForScope($query, $originalWhereCount)) { $this->nestWheresForScope($query, $originalWhereCount); @@ -1179,47 +1165,19 @@ public function applyScopes() $builder = clone $this; - $query = $builder->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 = count($query->wheres); - - $whereCounts = [$originalWhereCount]; - foreach ($this->scopes as $scope) { - $this->applyScope($scope, $builder); - - // Again, we will keep track of the count each time we add where clauses so that - // we will properly isolate each set of scope constraints inside of their own - // nested where clause to avoid any conflicts or issues with logical order. - $whereCounts[] = count($query->wheres); - } - - if ($this->shouldNestWheresForScope($query, $originalWhereCount)) { - $this->nestWheresForScope($query, $whereCounts); + $builder->callScope(function (Builder $builder) use ($scope) { + if ($scope instanceof Closure) { + $scope($builder); + } elseif ($scope instanceof Scope) { + $scope->apply($builder, $this->getModel()); + } + }); } return $builder; } - /** - * Apply a single scope on the given builder instance. - * - * @param \Illuminate\Database\Eloquent\Scope|\Closure $scope - * @param \Illuminate\Database\Eloquent\Builder $builder - * @return void - */ - protected function applyScope($scope, $builder) - { - if ($scope instanceof Closure) { - $scope($builder); - } elseif ($scope instanceof Scope) { - $scope->apply($builder, $this->getModel()); - } - } - /** * Determine if the scope added after the given offset should be nested. * @@ -1424,7 +1382,7 @@ public function __call($method, $parameters) } if (method_exists($this->model, $scope = 'scope'.ucfirst($method))) { - return $this->callScope($scope, $parameters); + return $this->callScope([$this->model, $scope], $parameters); } if (in_array($method, $this->passthru)) { diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 0d80a710b9e9..127713560070 100755 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -381,15 +381,11 @@ public static function hasGlobalScope($scope) */ public static function getGlobalScope($scope) { - $modelScopes = Arr::get(static::$globalScopes, static::class, []); - - if (is_string($scope)) { - return isset($modelScopes[$scope]) ? $modelScopes[$scope] : null; + if (! is_string($scope)) { + $scope = get_class($scope); } - return Arr::first($modelScopes, function ($key, $value) use ($scope) { - return $scope instanceof $value; - }); + return Arr::get(static::$globalScopes, static::class.'.'.$scope); } /** diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php index 242c86d872c0..806df6c46328 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php @@ -2,11 +2,9 @@ namespace Illuminate\Database\Eloquent\Relations; -use Illuminate\Support\Str; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Support\Collection as BaseCollection; class MorphTo extends BelongsTo { @@ -177,48 +175,14 @@ protected function getResultsByType($type) $key = $instance->getTable().'.'.$instance->getKeyName(); - $query = $instance->newQuery(); + $eagerLoads = $this->getQuery()->nestedRelations($this->relation); - $query->setEagerLoads($this->getEagerLoadsForInstance($instance)); - - $this->mergeRelationWheresToMorphQuery($this->query, $query); + $query = $instance->newQuery()->setEagerLoads($eagerLoads) + ->mergeModelDefinedRelationConstraints($this->getQuery()); return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get(); } - /** - * Get the relationships that should be eager loaded for the given model. - * - * @param \Illuminate\Database\Eloquent\Model $instance - * @return array - */ - protected function getEagerLoadsForInstance(Model $instance) - { - $relations = BaseCollection::make($this->query->getEagerLoads()); - - return $relations->filter(function ($constraint, $relation) { - return Str::startsWith($relation, $this->relation.'.'); - })->keyBy(function ($constraint, $relation) { - return Str::replaceFirst($this->relation.'.', '', $relation); - })->merge($instance->getEagerLoads())->all(); - } - - /** - * Merge the "wheres" from a relation query to a morph query. - * - * @param \Illuminate\Database\Eloquent\Builder $relationQuery - * @param \Illuminate\Database\Eloquent\Builder $morphQuery - * @return void - */ - protected function mergeRelationWheresToMorphQuery(Builder $relationQuery, Builder $morphQuery) - { - $removedScopes = $relationQuery->removedScopes(); - - $morphQuery->withoutGlobalScopes($removedScopes)->mergeWheres( - $relationQuery->getQuery()->wheres, $relationQuery->getBindings() - ); - } - /** * Gather all of the foreign keys for a given type. * diff --git a/src/Illuminate/Database/Eloquent/SoftDeletes.php b/src/Illuminate/Database/Eloquent/SoftDeletes.php index 25a36fc3f642..9f6b2439231f 100644 --- a/src/Illuminate/Database/Eloquent/SoftDeletes.php +++ b/src/Illuminate/Database/Eloquent/SoftDeletes.php @@ -103,30 +103,6 @@ public function trashed() return ! is_null($this->{$this->getDeletedAtColumn()}); } - /** - * Get a new query builder that includes soft deletes. - * - * @return \Illuminate\Database\Eloquent\Builder|static - */ - public static function withTrashed() - { - return (new static)->newQueryWithoutScope(new SoftDeletingScope); - } - - /** - * Get a new query builder that only includes soft deletes. - * - * @return \Illuminate\Database\Eloquent\Builder|static - */ - public static function onlyTrashed() - { - $instance = new static; - - $column = $instance->getQualifiedDeletedAtColumn(); - - return $instance->newQueryWithoutScope(new SoftDeletingScope)->whereNotNull($column); - } - /** * Register a restoring model event with the dispatcher. *