Failed Conditions
Push — refactor/improve-static-analys... ( c6edde...45b897 )
by Bas
07:40
created

QueriesAranguentRelationships::withAggregate()   C

Complexity

Conditions 13
Paths 149

Size

Total Lines 89
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 13.8887

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 13
eloc 45
nc 149
nop 3
dl 0
loc 89
ccs 19
cts 23
cp 0.8261
crap 13.8887
rs 6.2083
c 2
b 0
f 0

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
     * 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
27
        $this->getQuery()->exchangeTableAliases($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

27
        $this->/** @scrutinizer ignore-call */ 
28
               getQuery()->exchangeTableAliases($query);
Loading history...
28
29
        $this->getQuery()->importBindings($query);
30
31 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

31
        return $this->/** @scrutinizer ignore-call */ where(
Loading history...
32
            new Expression('LENGTH((' . $query->toSql() . '))'),
33 1
            $operator,
34 1
            is_numeric($count) ? new Expression($count) : $count,
0 ignored issues
show
introduced by
The condition is_numeric($count) is always true.
Loading history...
35
            $boolean
36 1
        );
37
    }
38
39
    /**
40
     * Merge the where constraints from another query to the current query.
41
     *
42
     * @param IlluminateEloquentBuilder $from
43
     * @return IlluminateEloquentBuilder|static
44
     */
45
    public function mergeConstraintsFrom(Builder $from)
46
    {
47
        $whereBindings = $this->getQuery()->getBindings() ?? [];
48
49
        $wheres = $from->getQuery()->from !== $this->getQuery()->from
50
            ? $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

50
            ? $this->/** @scrutinizer ignore-call */ requalifyWhereTables(
Loading history...
51 137
                $from->getQuery()->wheres,
52
                $from->getQuery()->grammar->getValue($from->getQuery()->from),
53 137
                $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

53
                $this->/** @scrutinizer ignore-call */ 
54
                       getModel()->getTable()
Loading history...
54
            ) : $from->getQuery()->wheres;
55 137
56 137
        // Here we have some other query that we want to merge the where constraints from. We will
57
        // copy over any where constraints on the query as well as remove any global scopes the
58
        // query might have removed. Then we will return ourselves with the finished merging.
59 1
        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

59
        return $this->/** @scrutinizer ignore-call */ withoutGlobalScopes(
Loading history...
60 1
            $from->removedScopes()
61
        )->mergeWheres(
62
            $wheres,
63 1
            $whereBindings
64
        );
65 1
    }
66
67
    /**
68
     * Add subselect queries to include an aggregate value for a relationship.
69 1
     *
70
     * @param  mixed  $relations
71 1
     * @param  string  $column
72
     * @param  string  $function
73 1
     * @return $this
74
     */
75
    public function withAggregate($relations, $column, $function = null)
76
    {
77 1
        if (empty($relations)) {
78
            return $this;
79
        }
80
81
        if (is_null($this->query->columns)) {
82 1
            $this->query->select([$this->query->from . '.*']);
83 1
        }
84
85
        $relations = is_array($relations) ? $relations : [$relations];
86
87 1
        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

87
        foreach ($this->/** @scrutinizer ignore-call */ parseWithRelations($relations) as $name => $constraints) {
Loading history...
88
            // First we will determine if the name has been aliased using an "as" clause on the name
89 1
            // and if it has we will extract the actual relationship name and the desired name of
90
            // the resulting column. This allows multiple aggregates on the same relationships.
91
            $segments = explode(' ', $name);
92
93
            unset($alias);
94 1
95
            if (count($segments) === 3 && Str::lower($segments[1]) === 'as') {
96 1
                [$name, $alias] = [$segments[0], $segments[2]];
97
            }
98
99
            $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

99
            /** @scrutinizer ignore-call */ 
100
            $relation = $this->getRelationWithoutConstraints($name);
Loading history...
100
101 1
            if ($function) {
102
                $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

102
                /** @scrutinizer ignore-call */ 
103
                $hashedColumn = $this->getRelationHashedColumn($column, $relation);
Loading history...
103
104
                $wrappedColumn = $this->getQuery()->getGrammar()->wrap(
105
                    $column === '*' ? $column : $relation->getRelated()->qualifyColumn($hashedColumn)
106 1
                );
107 1
108
                $expression = $function === 'exists' ? $wrappedColumn : sprintf('%s(%s)', $function, $wrappedColumn);
109
            } else {
110
                $expression = $column;
111 1
            }
112 1
113
            // Here, we will grab the relationship sub-query and prepare to add it to the main query
114
            // as a sub-select. First, we'll get the "has" query and use that to get the relation
115
            // sub-query. We'll format this relationship name and append this column if needed.
116
            $query = $relation->getRelationExistenceQuery(
117 1
                $relation->getRelated()->newQuery(),
118
                $this,
119
                new Expression($expression)
120 1
            )->setBindings([], 'select');
121
122 1
            $query->callScope($constraints);
123 1
124 1
            $query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();
125
126 1
            // If the query contains certain elements like orderings / more than one column selected
127
            // then we will remove those elements from the query so that it will execute properly
128
            // when given to the database. Otherwise, we may receive SQL errors or poor syntax.
129
            $query->orders = null;
130
            $query->setBindings([], 'order');
131 1
132
            if (count($query->columns) > 1) {
133
                $query->columns = [$query->columns[0]];
134
                $query->bindings['select'] = [];
135
            }
136
137
            // Finally, we will make the proper column alias to the query and run this sub-select on
138
            // the query builder. Then, we will return the builder instance back to the developer
139
            // for further constraint chaining that needs to take place on the query as needed.
140
            $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 96. Are you sure it is defined here?
Loading history...
141
                preg_replace('/[^[:alnum:][:space:]_]/u', '', "$name $function $column")
142
            );
143
144
            if ($function === 'exists') {
145
                //FIXME: test for results from the subquery -> whereHas?
146
                $this->selectRaw(
0 ignored issues
show
Bug introduced by
It seems like selectRaw() 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

146
                $this->/** @scrutinizer ignore-call */ 
147
                       selectRaw(
Loading history...
147
                    sprintf('exists(%s) as %s', $query->toSql(), $this->getQuery()->grammar->wrap($alias)),
148
                    $query->getBindings()
149
                )->withCasts([$alias => 'bool']);
150
            } else {
151
                if ($function === null) {
152
                    $query->limit(1);
153
                }
154
155
                $this->getQuery()->exchangeTableAliases($query);
156
                $this->getQuery()->importBindings($query);
157
158
                $this->set($alias, new Expression(strtoupper($function) . '((' . $query->toSql() . '))'), 'postIterationVariables');
0 ignored issues
show
Bug introduced by
It seems like set() 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

158
                $this->/** @scrutinizer ignore-call */ 
159
                       set($alias, new Expression(strtoupper($function) . '((' . $query->toSql() . '))'), 'postIterationVariables');
Loading history...
159
                $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

159
                $this->/** @scrutinizer ignore-call */ 
160
                       addSelect($alias);
Loading history...
160
            }
161
        }
162
163
        return $this;
164
    }
165
}
166