Siblings   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 17
eloc 63
c 3
b 0
f 0
dl 0
loc 184
ccs 76
cts 76
cp 1
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A addEagerConstraints() 0 10 2
A getResults() 0 3 1
A getRelationExistenceQueryForSelfRelation() 0 28 2
A addConstraints() 0 15 4
A match() 0 21 4
A __construct() 0 5 1
A getRelationExistenceQuery() 0 26 3
1
<?php
2
3
namespace Staudenmeir\LaravelAdjacencyList\Eloquent\Relations;
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\HasMany;
9
10
/**
11
 * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
12
 * @extends HasMany<TRelatedModel>
13
 */
14
class Siblings extends HasMany
15
{
16
    /**
17
     * Whether to include the parent model.
18
     *
19
     * @var bool
20
     */
21
    protected $andSelf;
22
23
    /**
24
     * Create a new siblings relationship instance.
25
     *
26
     * @param \Illuminate\Database\Eloquent\Builder $query
27
     * @param \Illuminate\Database\Eloquent\Model $parent
28
     * @param string $foreignKey
29
     * @param string $localKey
30
     * @param bool $andSelf
31
     * @return void
32
     */
33 76
    public function __construct(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
34
    {
35 76
        $this->andSelf = $andSelf;
36
37 76
        parent::__construct($query, $parent, $foreignKey, $localKey);
38
    }
39
40
    /**
41
     * Set the base constraints on the relation query.
42
     *
43
     * @return void
44
     */
45 76
    public function addConstraints()
46
    {
47 76
        if (static::$constraints) {
48 30
            $this->query->where($this->foreignKey, '=', $this->getParentKey());
49
50 30
            if (!$this->andSelf) {
51 18
                $this->query->where(
52 18
                    $this->related->getQualifiedLocalKeyName(),
53 18
                    '<>',
54 18
                    $this->parent->{$this->parent->getLocalKeyName()}
55 18
                );
56
            }
57
58 30
            if (!array_key_exists($this->localKey, $this->parent->getAttributes())) {
59 6
                $this->query->whereNotNull($this->foreignKey);
60
            }
61
        }
62
    }
63
64
    /**
65
     * Set the constraints for an eager load of the relation.
66
     *
67
     * @param array $models
68
     * @return void
69
     */
70 24
    public function addEagerConstraints(array $models)
71
    {
72 24
        $keys = $this->getKeys($models, $this->localKey);
73
74 24
        $this->query->where(
75 24
            function (Builder $query) use ($keys) {
76 24
                $query->whereIn($this->foreignKey, $keys);
77
78 24
                if (in_array(null, $keys, true)) {
79 24
                    $query->orWhereNull($this->foreignKey);
80
                }
81 24
            }
82 24
        );
83
    }
84
85
    /**
86
     * Match the eagerly loaded results to their parents.
87
     *
88
     * @param array $models
89
     * @param \Illuminate\Database\Eloquent\Collection $results
90
     * @param string $relation
91
     * @return array
92
     */
93 24
    public function match(array $models, Collection $results, $relation)
94
    {
95 24
        $dictionary = $this->buildDictionary($results);
96
97 24
        foreach ($models as $model) {
98 24
            $key = $model->{$this->localKey};
99
100 24
            if (isset($dictionary[$key])) {
101 24
                $value = $this->related->newCollection($dictionary[$key]);
102
103 24
                if (!$this->andSelf) {
104 12
                    $value = $value->reject(function (Model $result) use ($model) {
105 12
                        return $result->{$result->getLocalKeyName()} == $model->{$model->getLocalKeyName()};
106 12
                    })->values();
107
                }
108
109 24
                $model->setRelation($relation, $value);
110
            }
111
        }
112
113 24
        return $models;
114
    }
115
116
    /**
117
     * Get the results of the relationship.
118
     *
119
     * @phpstan-return \Traversable<int, TRelatedModel>
120
     */
121 12
    public function getResults()
122
    {
123 12
        return $this->query->get();
124
    }
125
126
    /**
127
     * Add the constraints for a relationship query.
128
     *
129
     * @param \Illuminate\Database\Eloquent\Builder $query
130
     * @param \Illuminate\Database\Eloquent\Builder $parentQuery
131
     * @param array|mixed $columns
132
     * @return \Illuminate\Database\Eloquent\Builder
133
     */
134 22
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
135
    {
136 22
        if ($query->getQuery()->from === $parentQuery->getQuery()->from) {
137 12
            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
138
        }
139
140 10
        $first = $this->foreignKey;
141 10
        $second = $parentQuery->qualifyColumn($this->localKey);
142
143 10
        $query->select($columns)
144 10
            ->where(function (Builder $query) use ($first, $second) {
145 10
                $query->whereColumn($first, '=', $second)
146 10
                    ->orWhere(function (Builder $query) use ($first, $second) {
147 10
                        $query->whereNull($first)->whereNull($second);
148 10
                    });
149 10
            });
150
151 10
        if (!$this->andSelf) {
152 5
            $query->whereColumn(
153 5
                $this->related->getQualifiedLocalKeyName(),
154 5
                '<>',
155 5
                $this->parent->getQualifiedLocalKeyName()
156 5
            );
157
        }
158
159 10
        return $query;
160
    }
161
162
    /**
163
     * Add the constraints for a relationship query on the same table.
164
     *
165
     * @param \Illuminate\Database\Eloquent\Builder $query
166
     * @param \Illuminate\Database\Eloquent\Builder $parentQuery
167
     * @param array|mixed $columns
168
     * @return \Illuminate\Database\Eloquent\Builder
169
     */
170 12
    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
171
    {
172 12
        $table = $this->getRelationCountHash();
173
174 12
        $query->from($query->getModel()->getTable().' as '.$table);
175
176 12
        $query->getModel()->setTable($table);
177
178 12
        $first = $table.'.'.$this->getForeignKeyName();
179 12
        $second = $this->getQualifiedParentKeyName();
180
181 12
        $query->select($columns)
182 12
            ->where(function (Builder $query) use ($first, $second) {
183 12
                $query->whereColumn($first, '=', $second)
184 12
                    ->orWhere(function (Builder $query) use ($first, $second) {
185 12
                        $query->whereNull($first)->whereNull($second);
186 12
                    });
187 12
            });
188
189 12
        if (!$this->andSelf) {
190 6
            $query->whereColumn(
191 6
                $table.'.'.$this->related->getLocalKeyName(),
192 6
                '<>',
193 6
                $this->parent->getQualifiedLocalKeyName()
194 6
            );
195
        }
196
197 12
        return $query;
198
    }
199
}
200