Passed
Push — refactor/improve-static-analys... ( efcf20...37f12c )
by Bas
03:04
created

QueriesAranguentRelationships::withAggregate()   B

Complexity

Conditions 9
Paths 29

Size

Total Lines 69
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 9.0164

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 31
c 2
b 0
f 0
nc 29
nop 3
dl 0
loc 69
ccs 32
cts 34
cp 0.9412
crap 9.0164
rs 8.0555

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
     * @param mixed $function
17
     * @param IlluminateQueryBuilder $query
18
     * @param string $alias
19
     */
20 2
    public function handleAggregateFunction(IlluminateQueryBuilder $query, mixed $function, string $alias): void
21
    {
22 2
        if ($function === null) {
23
            $query->limit(1);
24
25
            return;
26
        }
27
28
29 2
        if ($function === 'exists') {
30 1
            [$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

30
            [$subquery] = $this->/** @scrutinizer ignore-call */ getQuery()->createSub($query);
Loading history...
31
32 1
            $expression = new Expression(sprintf('(COUNT(%s)) > 0 ? true : false ', $subquery));
33
34 1
            $this->getQuery()->set(
35 1
                $alias,
36 1
                $expression,
37 1
                'postIterationVariables'
38 1
            )
39 1
                ->addSelect($alias);
40
41 1
            return;
42
        }
43
44
45 1
        [$subquery] = $this->getQuery()->createSub($query);
46
47 1
        $this->getQuery()->set(
48 1
            $alias,
49 1
            new Expression(strtoupper($function) . '(' . $subquery . ')'),
50 1
            'postIterationVariables'
51 1
        );
52
53 1
        $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

53
        $this->/** @scrutinizer ignore-call */ 
54
               addSelect($alias);
Loading history...
54
    }
55
56
    /**
57
     * @param array<string> $segments
58
     * @param string $name
59
     * @return array<int, string|null>
60
     */
61 2
    public function extractNameAndAlias(array $segments, string $name): array
62
    {
63 2
        $alias = null;
64
65 2
        if (count($segments) === 3 && Str::lower($segments[1]) === 'as') {
66
            [$name, $alias] = [$segments[0], $segments[2]];
67
        }
68 2
        return [$name, $alias];
69
    }
70
71
    /**
72
     * Add a sub-query count clause to this query.
73
     *
74
     * @param  \Illuminate\Database\Query\Builder  $query
75
     * @param  string  $operator
76
     * @param  int  $count
77
     * @param  string  $boolean
78
     * @return self
79
     */
80 1
    protected function addWhereCountQuery(IlluminateQueryBuilder $query, $operator = '>=', $count = 1, $boolean = 'and')
81
    {
82 1
        [$subquery] = $this->getQuery()->createSub($query);
83
84 1
        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

84
        return $this->/** @scrutinizer ignore-call */ where(
Loading history...
85 1
            new Expression('LENGTH(' . $subquery . ')'),
86 1
            $operator,
87 1
            new Expression($count),
88 1
            $boolean
89 1
        );
90
    }
91
92
    /**
93
     * Merge the where constraints from another query to the current query.
94
     *
95
     * @param IlluminateEloquentBuilder $from
96
     * @return IlluminateEloquentBuilder|static
97
     */
98 7
    public function mergeConstraintsFrom(Builder $from)
99
    {
100 7
        $whereBindings = $this->getQuery()->getBindings();
101
102 7
        $wheres = $from->getQuery()->from !== $this->getQuery()->from
103 7
            ? $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

103
            ? $this->/** @scrutinizer ignore-call */ requalifyWhereTables(
Loading history...
104 7
                $from->getQuery()->wheres,
105 7
                (string) $from->getQuery()->grammar->getValue($from->getQuery()->from),
106 7
                $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

106
                $this->/** @scrutinizer ignore-call */ 
107
                       getModel()->getTable()
Loading history...
107 7
            ) : $from->getQuery()->wheres;
108
109
        // Here we have some other query that we want to merge the where constraints from. We will
110
        // copy over any where constraints on the query as well as remove any global scopes the
111
        // query might have removed. Then we will return ourselves with the finished merging.
112 7
        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

112
        return $this->/** @scrutinizer ignore-call */ withoutGlobalScopes(
Loading history...
113 7
            $from->removedScopes()
114 7
        )->mergeWheres(
115 7
            $wheres,
116 7
            $whereBindings
117 7
        );
118
    }
119
120
    /**
121
     * Add subselect queries to include an aggregate value for a relationship.
122
     *
123
     * @param  mixed  $relations
124
     * @param  string  $column
125
     * @param  string  $function
126
     * @return $this
127
     */
128 87
    public function withAggregate($relations, $column, $function = null)
129
    {
130 87
        if (empty($relations)) {
131 87
            return $this;
132
        }
133
134 2
        if (empty($this->query->columns)) {
135 2
            $this->query->select([$this->query->from . '.*']);
136
        }
137
138 2
        $relations = is_array($relations) ? $relations : [$relations];
139
140 2
        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

140
        foreach ($this->/** @scrutinizer ignore-call */ parseWithRelations($relations) as $name => $constraints) {
Loading history...
141
            // First we will determine if the name has been aliased using an "as" clause on the name
142
            // and if it has we will extract the actual relationship name and the desired name of
143
            // the resulting column. This allows multiple aggregates on the same relationships.
144 2
            $segments = explode(' ', $name);
145
146 2
            [$name, $alias] = $this->extractNameAndAlias($segments, $name);
147
148 2
            $relation = $this->getRelationWithoutConstraints((string) $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

148
            /** @scrutinizer ignore-call */ 
149
            $relation = $this->getRelationWithoutConstraints((string) $name);
Loading history...
149
150 2
            $expression = $column;
151
152 2
            if ($function) {
153 2
                $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

153
                /** @scrutinizer ignore-call */ 
154
                $hashedColumn = $this->getRelationHashedColumn($column, $relation);
Loading history...
154
155 2
                $wrappedColumn = $this->getQuery()->getGrammar()->wrap(
156 2
                    $column === '*' ? $column : $relation->getRelated()->qualifyColumn($hashedColumn)
157 2
                );
158
159 2
                $expression = $function === 'exists' ? $wrappedColumn : sprintf('%s(%s)', $function, $wrappedColumn);
160
            }
161
162
            // Here, we will grab the relationship sub-query and prepare to add it to the main query
163
            // as a sub-select. First, we'll get the "has" query and use that to get the relation
164
            // sub-query. We'll format this relationship name and append this column if needed.
165 2
            $query = $relation->getRelationExistenceQuery(
166 2
                $relation->getRelated()->newQuery(),
167 2
                $this,
168 2
                new Expression($expression)
169 2
            )->setBindings([], 'select');
170
171 2
            $query->callScope($constraints);
172
173 2
            $query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();
174
175
            // If the query contains certain elements like orderings / more than one column selected
176
            // then we will remove those elements from the query so that it will execute properly
177
            // when given to the database. Otherwise, we may receive SQL errors or poor syntax.
178 2
            unset($query->orders);
179 2
            $query->setBindings([], 'order');
180
181 2
            if (count($query->columns) > 1) {
182
                $query->columns = [$query->columns[0]];
183
                $query->bindings['select'] = [];
184
            }
185
186
            // Finally, we will make the proper column alias to the query and run this sub-select on
187
            // the query builder. Then, we will return the builder instance back to the developer
188
            // for further constraint chaining that needs to take place on the query as needed.
189 2
            $alias = Str::snake(
190 2
                (string) preg_replace('/[^[:alnum:][:space:]_]/u', '', "$name $function $column")
191 2
            );
192
193 2
            $this->handleAggregateFunction($query, $function, $alias);
194
        }
195
196 2
        return $this;
197
    }
198
}
199