Passed
Push — feature/laravel-11-support ( 6c6616...4492de )
by Bas
04:19 queued 15s
created

QueriesAranguentRelationships::withAggregate()   B

Complexity

Conditions 10
Paths 29

Size

Total Lines 71
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 10.0186

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 32
c 2
b 0
f 0
nc 29
nop 3
dl 0
loc 71
ccs 33
cts 35
cp 0.9429
crap 10.0186
rs 7.6666

How to fix   Long Method    Complexity   

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
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
129
     */
130 97
    public function withAggregate($relations, $column, $function = null)
131
    {
132 97
        if (empty($relations)) {
133 97
            return $this;
134
        }
135
136 2
        $table = (string) $this->query->grammar->getValue($this->query->from);
137
138 2
        if (empty($this->query->columns)) {
139 2
            $this->query->select([$table . '.*']);
140
        }
141
142 2
        $relations = is_array($relations) ? $relations : [$relations];
143
144 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

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

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

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