Passed
Push — master ( 5a82e5...fe024b )
by Jonas
03:43
created

getRelationExistenceQueryForSelfRelation()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 27
ccs 13
cts 13
cp 1
rs 9.7998
cc 3
nc 4
nop 2
crap 3
1
<?php
2
3
namespace Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Graph;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9
use Illuminate\Database\Query\Expression;
10
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Graph\Traits\IsRecursiveRelation;
11
12
class Descendants extends BelongsToMany
13
{
14
    use IsRecursiveRelation {
15
        buildDictionary as baseBuildDictionary;
16
    }
17
18
    /**
19
     * Set the base constraints on the relation query.
20
     *
21
     * @return void
22
     */
23 123
    public function addConstraints()
24
    {
25 123
        if (static::$constraints) {
26 61
            $column = $this->andSelf ? $this->getQualifiedParentKeyName() : $this->getQualifiedForeignPivotKeyName();
27
28 61
            $constraint = function (Builder $query) use ($column) {
29 61
                $query->where(
30
                    $column,
31
                    '=',
32 61
                    $this->parent->{$this->parentKey}
33
                );
34
            };
35
36 61
            $this->addExpression($constraint);
37
        }
38
    }
39
40
    /**
41
     * Set the constraints for an eager load of the relation.
42
     *
43
     * @param array $models
44
     * @return void
45
     */
46 32
    public function addEagerConstraints(array $models)
47
    {
48 32
        $whereIn = $this->whereInMethod($this->parent, $this->parentKey);
49
50 32
        $column = $this->andSelf ? $this->getQualifiedParentKeyName() : $this->getQualifiedForeignPivotKeyName();
51
52 32
        $keys = $this->getKeys($models, $this->parentKey);
53
54 32
        $constraint = function (Builder $query) use ($models, $whereIn, $column, $keys) {
0 ignored issues
show
Unused Code introduced by
The import $models is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
55 32
            $query->$whereIn($column, $keys);
56
        };
57
58 32
        $this->addExpression($constraint, null, null, $this->andSelf ? 'unionAll' : 'union');
59
    }
60
61
    /**
62
     * Build model dictionary.
63
     *
64
     * @param \Illuminate\Database\Eloquent\Collection $results
65
     * @return array
66
     */
67 32
    protected function buildDictionary(Collection $results)
68
    {
69 32
        if ($this->andSelf) {
70 16
            return $this->baseBuildDictionary($results);
71
        }
72
73 16
        $dictionary = [];
74
75 16
        $depthName = $this->related->getDepthName();
76
77 16
        $firstLevelResults = $results->where($depthName, '=', 1)->groupBy($this->parentKey);
78
79 16
        foreach ($results as $result) {
80 16
            $keys = [];
81
82 16
            if ($result->$depthName > 1) {
83 16
                foreach ($firstLevelResults[$result->getFirstPathSegment()] as $model) {
84 16
                    $keys[] = $model->{$this->accessor}->{$this->foreignPivotKey};
85
                }
86
            } else {
87 16
                $keys[] = $result->{$this->accessor}->{$this->foreignPivotKey};
88
            }
89
90 16
            foreach ($keys as $key) {
91 16
                $dictionary[$key][] = $result;
92
            }
93
        }
94
95 16
        return $dictionary;
96
    }
97
98
    /**
99
     * Add the constraints for a relationship query.
100
     *
101
     * @param \Illuminate\Database\Eloquent\Builder $query
102
     * @param \Illuminate\Database\Eloquent\Builder $parentQuery
103
     * @param array|mixed $columns
104
     * @return \Illuminate\Database\Eloquent\Builder
105
     */
106 30
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
107
    {
108 30
        if ($query->getQuery()->from === $parentQuery->getQuery()->from) {
109 12
            return $this->getRelationExistenceQueryForSelfRelation($query, $columns);
110
        }
111
112 18
        $first = $this->andSelf
113 9
            ? $query->getQuery()->from . '.' . $this->parentKey
114 9
            : $this->getQualifiedForeignPivotKeyName();
115
116 18
        $constraint = function (Builder $query) use ($first) {
117 18
            $query->whereColumn(
118
                $first,
119
                '=',
120 18
                $this->getQualifiedParentKeyName()
121
            );
122
        };
123
124 18
        return $this->addExpression($constraint, $query->select($columns));
0 ignored issues
show
Bug introduced by
It seems like $query->select($columns) can also be of type Illuminate\Database\Query\Builder; however, parameter $query of Staudenmeir\LaravelAdjac...ndants::addExpression() does only seem to accept Illuminate\Database\Eloquent\Builder|null, maybe add an additional type check? ( Ignorable by Annotation )

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

124
        return $this->addExpression($constraint, /** @scrutinizer ignore-type */ $query->select($columns));
Loading history...
125
    }
126
127
    /**
128
     * Add the constraints for a relationship query on the same table.
129
     *
130
     * @param \Illuminate\Database\Eloquent\Builder $query
131
     * @param array|mixed $columns
132
     * @return \Illuminate\Database\Eloquent\Builder
133
     */
134 12
    public function getRelationExistenceQueryForSelfRelation(
135
        Builder $query,
136
        $columns = ['*']
137
    ): Builder {
138 12
        if ($columns instanceof Expression) {
139 9
            $columns = $this->replaceTableHash($query, $columns);
140
        }
141
142 12
        $table = $this->getRelationCountHash();
143
144 12
        $from = $query->getModel()->getTable() . ' as ' . $table;
145
146 12
        $query->getModel()->setTable($table);
147
148 12
        $first = $this->andSelf
149 6
            ? "$table.$this->parentKey"
150 6
            : "$this->table.$this->foreignPivotKey";
151
152 12
        $constraint = function (Builder $query) use ($first) {
153 12
            $query->whereColumn(
154
                $first,
155
                '=',
156 12
                $this->getQualifiedParentKeyName()
157
            );
158
        };
159
160 12
        return $this->addExpression($constraint, $query->select($columns), $from);
0 ignored issues
show
Bug introduced by
It seems like $query->select($columns) can also be of type Illuminate\Database\Query\Builder; however, parameter $query of Staudenmeir\LaravelAdjac...ndants::addExpression() does only seem to accept Illuminate\Database\Eloquent\Builder|null, maybe add an additional type check? ( Ignorable by Annotation )

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

160
        return $this->addExpression($constraint, /** @scrutinizer ignore-type */ $query->select($columns), $from);
Loading history...
161
    }
162
163
    /**
164
     * Add a recursive expression to the query.
165
     *
166
     * @param callable $constraint
167
     * @param \Illuminate\Database\Eloquent\Builder|null $query
168
     * @param string|null $from
169
     * @param string $union
170
     * @return \Illuminate\Database\Eloquent\Builder
171
     */
172 123
    protected function addExpression(
173
        callable $constraint,
174
        Builder $query = null,
175
        string $from = null,
176
        string $union = 'unionAll'
177
    ): Builder {
178 123
        $query = $query ?: $this->query;
179
180 123
        $initialDepth = $this->andSelf ? 0 : 1;
181
182 123
        return $query->withRelationshipExpression('desc', $constraint, $initialDepth, $from, null, $union);
183
    }
184
}
185