Failed Conditions
Push — refactor/improve-static-analys... ( 8065ea...92b34c )
by Bas
05:43
created

QueriesAranguentRelationships   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 156
Duplicated Lines 0 %

Test Coverage

Coverage 87.5%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 69
dl 0
loc 156
ccs 35
cts 40
cp 0.875
rs 10
c 2
b 0
f 0
wmc 17

3 Methods

Rating   Name   Duplication   Size   Complexity  
A mergeConstraintsFrom() 0 19 2
A addWhereCountQuery() 0 9 2
C withAggregate() 0 97 13
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Eloquent\Concerns;
6
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Builder as IlluminateEloquentBuilder;
9
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
10
use Illuminate\Database\Query\Expression;
11
use Illuminate\Support\Str;
12
13
trait QueriesAranguentRelationships
14
{
15
    /**
16
     * Add a sub-query count clause to this query.
17
     *
18
     * @param  \Illuminate\Database\Query\Builder  $query
19
     * @param  string  $operator
20
     * @param  int  $count
21
     * @param  string  $boolean
22
     * @return $this
23
     */
24 1
    protected function addWhereCountQuery(IlluminateQueryBuilder $query, $operator = '>=', $count = 1, $boolean = 'and')
25
    {
26
        [$subquery] = $this->getQuery()->createSub($query);
0 ignored issues
show
Bug introduced by
It seems like getQuery() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

26
        [$subquery] = $this->/** @scrutinizer ignore-call */ getQuery()->createSub($query);
Loading history...
27
28
        return $this->where(
0 ignored issues
show
Bug introduced by
It seems like where() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

28
        return $this->/** @scrutinizer ignore-call */ where(
Loading history...
29
            new Expression('LENGTH(' . $subquery . ')'),
30
            $operator,
31 1
            is_numeric($count) ? new Expression($count) : $count,
0 ignored issues
show
introduced by
The condition is_numeric($count) is always true.
Loading history...
32
            $boolean
33 1
        );
34 1
    }
35
36 1
    /**
37
     * Merge the where constraints from another query to the current query.
38
     *
39
     * @param IlluminateEloquentBuilder $from
40
     * @return IlluminateEloquentBuilder|static
41
     */
42
    public function mergeConstraintsFrom(Builder $from)
43
    {
44
        $whereBindings = $this->getQuery()->getBindings() ?? [];
45
46
        $wheres = $from->getQuery()->from !== $this->getQuery()->from
47
            ? $this->requalifyWhereTables(
0 ignored issues
show
Bug introduced by
It seems like requalifyWhereTables() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

47
            ? $this->/** @scrutinizer ignore-call */ requalifyWhereTables(
Loading history...
48
                $from->getQuery()->wheres,
49
                $from->getQuery()->grammar->getValue($from->getQuery()->from),
50
                $this->getModel()->getTable()
0 ignored issues
show
Bug introduced by
It seems like getModel() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

50
                $this->/** @scrutinizer ignore-call */ 
51
                       getModel()->getTable()
Loading history...
51 137
            ) : $from->getQuery()->wheres;
52
53 137
        // Here we have some other query that we want to merge the where constraints from. We will
54
        // copy over any where constraints on the query as well as remove any global scopes the
55 137
        // query might have removed. Then we will return ourselves with the finished merging.
56 137
        return $this->withoutGlobalScopes(
0 ignored issues
show
Bug introduced by
It seems like withoutGlobalScopes() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

56
        return $this->/** @scrutinizer ignore-call */ withoutGlobalScopes(
Loading history...
57
            $from->removedScopes()
58
        )->mergeWheres(
59 1
            $wheres,
60 1
            $whereBindings
61
        );
62
    }
63 1
64
    /**
65 1
     * Add subselect queries to include an aggregate value for a relationship.
66
     *
67
     * @param  mixed  $relations
68
     * @param  string  $column
69 1
     * @param  string  $function
70
     * @return $this
71 1
     */
72
    public function withAggregate($relations, $column, $function = null)
73 1
    {
74
        if (empty($relations)) {
75
            return $this;
76
        }
77 1
78
        if (is_null($this->query->columns)) {
79
            $this->query->select([$this->query->from . '.*']);
80
        }
81
82 1
        $relations = is_array($relations) ? $relations : [$relations];
83 1
84
        foreach ($this->parseWithRelations($relations) as $name => $constraints) {
0 ignored issues
show
Bug introduced by
It seems like parseWithRelations() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

84
        foreach ($this->/** @scrutinizer ignore-call */ parseWithRelations($relations) as $name => $constraints) {
Loading history...
85
            // First we will determine if the name has been aliased using an "as" clause on the name
86
            // and if it has we will extract the actual relationship name and the desired name of
87 1
            // the resulting column. This allows multiple aggregates on the same relationships.
88
            $segments = explode(' ', $name);
89 1
90
            unset($alias);
91
92
            if (count($segments) === 3 && Str::lower($segments[1]) === 'as') {
93
                [$name, $alias] = [$segments[0], $segments[2]];
94 1
            }
95
96 1
            $relation = $this->getRelationWithoutConstraints($name);
0 ignored issues
show
Bug introduced by
It seems like getRelationWithoutConstraints() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

96
            /** @scrutinizer ignore-call */ 
97
            $relation = $this->getRelationWithoutConstraints($name);
Loading history...
97
98
            if ($function) {
99
                $hashedColumn = $this->getRelationHashedColumn($column, $relation);
0 ignored issues
show
Bug introduced by
It seems like getRelationHashedColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

99
                /** @scrutinizer ignore-call */ 
100
                $hashedColumn = $this->getRelationHashedColumn($column, $relation);
Loading history...
100
101 1
                $wrappedColumn = $this->getQuery()->getGrammar()->wrap(
102
                    $column === '*' ? $column : $relation->getRelated()->qualifyColumn($hashedColumn)
103
                );
104
105
                $expression = $function === 'exists' ? $wrappedColumn : sprintf('%s(%s)', $function, $wrappedColumn);
106 1
            } else {
107 1
                $expression = $column;
108
            }
109
110
            // Here, we will grab the relationship sub-query and prepare to add it to the main query
111 1
            // as a sub-select. First, we'll get the "has" query and use that to get the relation
112 1
            // sub-query. We'll format this relationship name and append this column if needed.
113
            $query = $relation->getRelationExistenceQuery(
114
                $relation->getRelated()->newQuery(),
115
                $this,
116
                new Expression($expression)
117 1
            )->setBindings([], 'select');
118
119
            $query->callScope($constraints);
120 1
121
            $query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();
122 1
123 1
            // If the query contains certain elements like orderings / more than one column selected
124 1
            // then we will remove those elements from the query so that it will execute properly
125
            // when given to the database. Otherwise, we may receive SQL errors or poor syntax.
126 1
            $query->orders = null;
127
            $query->setBindings([], 'order');
128
129
            if (count($query->columns) > 1) {
130
                $query->columns = [$query->columns[0]];
131 1
                $query->bindings['select'] = [];
132
            }
133
134
            // Finally, we will make the proper column alias to the query and run this sub-select on
135
            // the query builder. Then, we will return the builder instance back to the developer
136
            // for further constraint chaining that needs to take place on the query as needed.
137
            $alias ??= Str::snake(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $alias seems to be defined later in this foreach loop on line 93. Are you sure it is defined here?
Loading history...
138
                preg_replace('/[^[:alnum:][:space:]_]/u', '', "$name $function $column")
139
            );
140
141
            if ($function === 'exists') {
142
                [$subquery] = $this->getQuery()->createSub($query);
143
144
                $expression = new Expression(sprintf('(COUNT(%s)) > 0 ? true : false ', $subquery));
145
146
                $this->getQuery()->set(
147
                    $alias,
148
                    $expression,
149
                    'postIterationVariables'
150
                )
151
                    ->addSelect($alias);
152
            } else {
153
                if ($function === null) {
154
                    $query->limit(1);
155
                }
156
157
                [$subquery] = $this->getQuery()->createSub($query);
158
159
                $this->getQuery()->set(
160
                    $alias,
161
                    new Expression(strtoupper($function) . '(' . $subquery . ')'),
162
                    'postIterationVariables'
163
                );
164
                $this->addSelect($alias);
0 ignored issues
show
Bug introduced by
It seems like addSelect() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
                $this->/** @scrutinizer ignore-call */ 
165
                       addSelect($alias);
Loading history...
165
            }
166
        }
167
168
        return $this;
169
    }
170
}
171