Вход Регистрация
Файл: system/vendor/illuminate/database/Eloquent/Concerns/QueriesRelationships.php
Строк: 803
<?php

namespace IlluminateDatabaseEloquentConcerns;

use 
Closure;
use 
IlluminateDatabaseEloquentBuilder;
use 
IlluminateDatabaseEloquentRelationsMorphTo;
use 
IlluminateDatabaseEloquentRelationsRelation;
use 
IlluminateDatabaseQueryBuilder as QueryBuilder;
use 
IlluminateDatabaseQueryExpression;
use 
IlluminateSupportStr;
use 
RuntimeException;

trait 
QueriesRelationships
{
    
/**
     * Add a relationship count / exists condition to the query.
     *
     * @param  IlluminateDatabaseEloquentRelationsRelation|string  $relation
     * @param  string  $operator
     * @param  int  $count
     * @param  string  $boolean
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     *
     * @throws RuntimeException
     */
    
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
    {
        if (
is_string($relation)) {
            if (
strpos($relation, '.') !== false) {
                return 
$this->hasNested($relation, $operator, $count, $boolean, $callback);
            }

            
$relation = $this->getRelationWithoutConstraints($relation);
        }

        if (
$relation instanceof MorphTo) {
            throw new 
RuntimeException('Please use whereHasMorph() for MorphTo relationships.');
        }

        
// If we only need to check for the existence of the relation, then we can optimize
        // the subquery to only run a "where exists" clause instead of this full "count"
        // clause. This will make these queries run much faster compared with a count.
        
$method = $this->canUseExistsForExistenceCheck($operator, $count)
                        ? 
'getRelationExistenceQuery'
                        
: 'getRelationExistenceCountQuery';

        
$hasQuery = $relation->{$method}(
            
$relation->getRelated()->newQueryWithoutRelationships(), $this
        
);

        
// Next we will call any given callback as an "anonymous" scope so they can get the
        // proper logical grouping of the where clauses if needed by this Eloquent query
        // builder. Then, we will be ready to finalize and return this query instance.
        
if ($callback) {
            
$hasQuery->callScope($callback);
        }

        return 
$this->addHasWhere(
            
$hasQuery, $relation, $operator, $count, $boolean
        
);
    }

    
/**
     * Add nested relationship count / exists conditions to the query.
     *
     * Sets up recursive call to whereHas until we finish the nested relation.
     *
     * @param  string  $relations
     * @param  string  $operator
     * @param  int  $count
     * @param  string  $boolean
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
protected function hasNested($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
    {
        
$relations = explode('.', $relations);

        
$doesntHave = $operator === '<' && $count === 1;

        if (
$doesntHave) {
            
$operator = '>=';
            
$count = 1;
        }

        
$closure = function ($q) use (&$closure, &$relations, $operator, $count, $callback) {
            
// In order to nest "has", we need to add count relation constraints on the
            // callback Closure. We'll do this by simply passing the Closure its own
            // reference to itself so it calls itself recursively on each segment.
            
count($relations) > 1
                
? $q->whereHas(array_shift($relations), $closure)
                : 
$q->has(array_shift($relations), $operator, $count, 'and', $callback);
        };

        return 
$this->has(array_shift($relations), $doesntHave ? '<' : '>=', 1, $boolean, $closure);
    }

    
/**
     * Add a relationship count / exists condition to the query with an "or".
     *
     * @param  string  $relation
     * @param  string  $operator
     * @param  int  $count
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orHas($relation, $operator = '>=', $count = 1)
    {
        return 
$this->has($relation, $operator, $count, 'or');
    }

    
/**
     * Add a relationship count / exists condition to the query.
     *
     * @param  string  $relation
     * @param  string  $boolean
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function doesntHave($relation, $boolean = 'and', Closure $callback = null)
    {
        return 
$this->has($relation, '<', 1, $boolean, $callback);
    }

    
/**
     * Add a relationship count / exists condition to the query with an "or".
     *
     * @param  string  $relation
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orDoesntHave($relation)
    {
        return 
$this->doesntHave($relation, 'or');
    }

    
/**
     * Add a relationship count / exists condition to the query with where clauses.
     *
     * @param  string  $relation
     * @param  Closure|null  $callback
     * @param  string  $operator
     * @param  int  $count
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function whereHas($relation, Closure $callback = null, $operator = '>=', $count = 1)
    {
        return 
$this->has($relation, $operator, $count, 'and', $callback);
    }

    
/**
     * Add a relationship count / exists condition to the query with where clauses and an "or".
     *
     * @param  string  $relation
     * @param  Closure|null  $callback
     * @param  string  $operator
     * @param  int  $count
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orWhereHas($relation, Closure $callback = null, $operator = '>=', $count = 1)
    {
        return 
$this->has($relation, $operator, $count, 'or', $callback);
    }

    
/**
     * Add a relationship count / exists condition to the query with where clauses.
     *
     * @param  string  $relation
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function whereDoesntHave($relation, Closure $callback = null)
    {
        return 
$this->doesntHave($relation, 'and', $callback);
    }

    
/**
     * Add a relationship count / exists condition to the query with where clauses and an "or".
     *
     * @param  string  $relation
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orWhereDoesntHave($relation, Closure $callback = null)
    {
        return 
$this->doesntHave($relation, 'or', $callback);
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query.
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  string  $operator
     * @param  int  $count
     * @param  string  $boolean
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
    {
        
$relation = $this->getRelationWithoutConstraints($relation);

        
$types = (array) $types;

        if (
$types === ['*']) {
            
$types = $this->model->newModelQuery()->distinct()->pluck($relation->getMorphType())->all();

            foreach (
$types as &$type) {
                
$type = Relation::getMorphedModel($type) ?? $type;
            }
        }

        return 
$this->where(function ($query) use ($relation, $callback, $operator, $count, $types) {
            foreach (
$types as $type) {
                
$query->orWhere(function ($query) use ($relation, $callback, $operator, $count, $type) {
                    
$belongsTo = $this->getBelongsToRelation($relation, $type);

                    if (
$callback) {
                        
$callback = function ($query) use ($callback, $type) {
                            return 
$callback($query, $type);
                        };
                    }

                    
$query->where($this->query->from.'.'.$relation->getMorphType(), '=', (new $type)->getMorphClass())
                                ->
whereHas($belongsTo, $callback, $operator, $count);
                });
            }
        }, 
null, null, $boolean);
    }

    
/**
     * Get the BelongsTo relationship for a single polymorphic type.
     *
     * @param  IlluminateDatabaseEloquentRelationsMorphTo  $relation
     * @param  string  $type
     * @return IlluminateDatabaseEloquentRelationsBelongsTo
     */
    
protected function getBelongsToRelation(MorphTo $relation, $type)
    {
        
$belongsTo = Relation::noConstraints(function () use ($relation, $type) {
            return 
$this->model->belongsTo(
                
$type,
                
$relation->getForeignKeyName(),
                
$relation->getOwnerKeyName()
            );
        });

        
$belongsTo->getQuery()->mergeConstraintsFrom($relation->getQuery());

        return 
$belongsTo;
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query with an "or".
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  string  $operator
     * @param  int  $count
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orHasMorph($relation, $types, $operator = '>=', $count = 1)
    {
        return 
$this->hasMorph($relation, $types, $operator, $count, 'or');
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query.
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  string  $boolean
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function doesntHaveMorph($relation, $types, $boolean = 'and', Closure $callback = null)
    {
        return 
$this->hasMorph($relation, $types, '<', 1, $boolean, $callback);
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query with an "or".
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orDoesntHaveMorph($relation, $types)
    {
        return 
$this->doesntHaveMorph($relation, $types, 'or');
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query with where clauses.
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  Closure|null  $callback
     * @param  string  $operator
     * @param  int  $count
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function whereHasMorph($relation, $types, Closure $callback = null, $operator = '>=', $count = 1)
    {
        return 
$this->hasMorph($relation, $types, $operator, $count, 'and', $callback);
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query with where clauses and an "or".
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  Closure|null  $callback
     * @param  string  $operator
     * @param  int  $count
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orWhereHasMorph($relation, $types, Closure $callback = null, $operator = '>=', $count = 1)
    {
        return 
$this->hasMorph($relation, $types, $operator, $count, 'or', $callback);
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query with where clauses.
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function whereDoesntHaveMorph($relation, $types, Closure $callback = null)
    {
        return 
$this->doesntHaveMorph($relation, $types, 'and', $callback);
    }

    
/**
     * Add a polymorphic relationship count / exists condition to the query with where clauses and an "or".
     *
     * @param  string  $relation
     * @param  string|array  $types
     * @param  Closure|null  $callback
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function orWhereDoesntHaveMorph($relation, $types, Closure $callback = null)
    {
        return 
$this->doesntHaveMorph($relation, $types, 'or', $callback);
    }

    
/**
     * Add subselect queries to count the relations.
     *
     * @param  mixed  $relations
     * @return $this
     */
    
public function withCount($relations)
    {
        if (empty(
$relations)) {
            return 
$this;
        }

        if (
is_null($this->query->columns)) {
            
$this->query->select([$this->query->from.'.*']);
        }

        
$relations = is_array($relations) ? $relations : func_get_args();

        foreach (
$this->parseWithRelations($relations) as $name => $constraints) {
            
// First we will determine if the name has been aliased using an "as" clause on the name
            // and if it has we will extract the actual relationship name and the desired name of
            // the resulting column. This allows multiple counts on the same relationship name.
            
$segments = explode(' ', $name);

            unset(
$alias);

            if (
count($segments) === 3 && Str::lower($segments[1]) === 'as') {
                [
$name, $alias] = [$segments[0], $segments[2]];
            }

            
$relation = $this->getRelationWithoutConstraints($name);

            
// Here we will get the relationship count query and prepare to add it to the main query
            // as a sub-select. First, we'll get the "has" query and use that to get the relation
            // count query. We will normalize the relation name then append _count as the name.
            
$query = $relation->getRelationExistenceCountQuery(
                
$relation->getRelated()->newQuery(), $this
            
);

            
$query->callScope($constraints);

            
$query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();

            
$query->orders = null;

            
$query->setBindings([], 'order');

            if (
count($query->columns) > 1) {
                
$query->columns = [$query->columns[0]];

                
$query->bindings['select'] = [];
            }

            
// Finally we will add the proper result column alias to the query and run the subselect
            // statement against the query builder. Then we will return the builder instance back
            // to the developer for further constraint chaining that needs to take place on it.
            
$column = $alias ?? Str::snake($name.'_count');

            
$this->selectSub($query, $column);
        }

        return 
$this;
    }

    
/**
     * Add the "has" condition where clause to the query.
     *
     * @param  IlluminateDatabaseEloquentBuilder  $hasQuery
     * @param  IlluminateDatabaseEloquentRelationsRelation  $relation
     * @param  string  $operator
     * @param  int  $count
     * @param  string  $boolean
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
protected function addHasWhere(Builder $hasQuery, Relation $relation, $operator, $count, $boolean)
    {
        
$hasQuery->mergeConstraintsFrom($relation->getQuery());

        return 
$this->canUseExistsForExistenceCheck($operator, $count)
                ? 
$this->addWhereExistsQuery($hasQuery->toBase(), $boolean, $operator === '<' && $count === 1)
                : 
$this->addWhereCountQuery($hasQuery->toBase(), $operator, $count, $boolean);
    }

    
/**
     * Merge the where constraints from another query to the current query.
     *
     * @param  IlluminateDatabaseEloquentBuilder  $from
     * @return IlluminateDatabaseEloquentBuilder|static
     */
    
public function mergeConstraintsFrom(Builder $from)
    {
        
$whereBindings = $from->getQuery()->getRawBindings()['where'] ?? [];

        
// Here we have some other query that we want to merge the where constraints from. We will
        // copy over any where constraints on the query as well as remove any global scopes the
        // query might have removed. Then we will return ourselves with the finished merging.
        
return $this->withoutGlobalScopes(
            
$from->removedScopes()
        )->
mergeWheres(
            
$from->getQuery()->wheres, $whereBindings
        
);
    }

    
/**
     * Add a sub-query count clause to this query.
     *
     * @param  IlluminateDatabaseQueryBuilder  $query
     * @param  string  $operator
     * @param  int  $count
     * @param  string  $boolean
     * @return $this
     */
    
protected function addWhereCountQuery(QueryBuilder $query, $operator = '>=', $count = 1, $boolean = 'and')
    {
        
$this->query->addBinding($query->getBindings(), 'where');

        return 
$this->where(
            new 
Expression('('.$query->toSql().')'),
            
$operator,
            
is_numeric($count) ? new Expression($count) : $count,
            
$boolean
        
);
    }

    
/**
     * Get the "has relation" base query instance.
     *
     * @param  string  $relation
     * @return IlluminateDatabaseEloquentRelationsRelation
     */
    
protected function getRelationWithoutConstraints($relation)
    {
        return 
Relation::noConstraints(function () use ($relation) {
            return 
$this->getModel()->{$relation}();
        });
    }

    
/**
     * Check if we can run an "exists" query to optimize performance.
     *
     * @param  string  $operator
     * @param  int  $count
     * @return bool
     */
    
protected function canUseExistsForExistenceCheck($operator, $count)
    {
        return (
$operator === '>=' || $operator === '<') && $count === 1;
    }
}
Онлайн: 0
Реклама