SupportsCompositeKeys   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 17
eloc 56
dl 0
loc 158
rs 10
c 1
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A shouldSelectWithCompositeKey() 0 8 1
A matchWithCompositeKey() 0 24 4
A addEagerConstraintsWithCompositeKey() 0 21 3
A addConstraintsWithCompositeKey() 0 9 2
A hasLeadingCompositeKey() 0 3 1
A buildDictionaryWithCompositeKey() 0 19 4
A getRelationExistenceQueryWithCompositeKey() 0 9 2
1
<?php
2
3
namespace Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Collection as BaseCollection;
9
use Staudenmeir\EloquentHasManyDeep\Eloquent\CompositeKey;
10
11
trait SupportsCompositeKeys
12
{
13
    /**
14
     * Determine whether the relationship starts with a composite key.
15
     *
16
     * @return bool
17
     */
18
    protected function hasLeadingCompositeKey(): bool
19
    {
20
        return $this->localKeys[0] instanceof CompositeKey;
21
    }
22
23
    /**
24
     * Set the base constraints on the relation query for a leading composite key.
25
     *
26
     * @return void
27
     */
28
    protected function addConstraintsWithCompositeKey(): void
29
    {
30
        $columns = array_slice($this->foreignKeys[0]->columns, 1, null, true);
31
32
        foreach ($columns as $i => $column) {
33
            $this->query->where(
34
                $this->throughParent->qualifyColumn($column),
35
                '=',
36
                $this->farParent[$this->localKeys[0]->columns[$i]]
37
            );
38
        }
39
    }
40
41
    /**
42
     * Set the constraints for an eager load of the relation for a leading composite key.
43
     *
44
     * @param array $models
45
     * @return void
46
     */
47
    protected function addEagerConstraintsWithCompositeKey(array $models): void
48
    {
49
        $keys = (new BaseCollection($models))->map(
0 ignored issues
show
Bug introduced by
$models of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::__construct(). ( Ignorable by Annotation )

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

49
        $keys = (new BaseCollection(/** @scrutinizer ignore-type */ $models))->map(
Loading history...
50
            function (Model $model) {
51
                return array_map(
52
                    fn (string $column) => $model[$column],
53
                    $this->localKeys[0]->columns
54
                );
55
            }
56
        )->values()->unique(null, true)->all();
57
58
        $this->query->where(
59
            function (Builder $query) use ($keys) {
60
                foreach ($keys as $key) {
61
                    $query->orWhere(
62
                        function (Builder $query) use ($key) {
63
                            foreach ($this->foreignKeys[0]->columns as $i => $column) {
64
                                $query->where(
65
                                    $this->throughParent->qualifyColumn($column),
66
                                    '=',
67
                                    $key[$i]
68
                                );
69
                            }
70
                        }
71
                    );
72
                }
73
            }
74
        );
75
    }
76
77
    /**
78
     * Match the eagerly loaded results to their parents for a leading composite key.
79
     *
80
     * @param array $models
81
     * @param \Illuminate\Database\Eloquent\Collection $results
82
     * @param string $relation
83
     * @return array
84
     */
85
    protected function matchWithCompositeKey(array $models, Collection $results, string $relation): array
86
    {
87
        $dictionary = $this->buildDictionaryWithCompositeKey($results);
88
89
        foreach ($models as $model) {
90
            $values = [];
91
92
            foreach ($this->localKeys[0]->columns as $column) {
93
                $values[] = $this->getDictionaryKey(
0 ignored issues
show
Bug introduced by
It seems like getDictionaryKey() 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

93
                /** @scrutinizer ignore-call */ 
94
                $values[] = $this->getDictionaryKey(
Loading history...
94
                    $model->getAttribute($column)
95
                );
96
            }
97
98
            $key = implode("\0", $values);
99
100
            if (isset($dictionary[$key])) {
101
                $model->setRelation(
102
                    $relation,
103
                    $this->related->newCollection($dictionary[$key])
104
                );
105
            }
106
        }
107
108
        return $models;
109
    }
110
111
    /**
112
     * Build model dictionary keyed by the relation's composite foreign key.
113
     *
114
     * @param \Illuminate\Database\Eloquent\Collection $results
115
     * @return array
116
     */
117
    protected function buildDictionaryWithCompositeKey(Collection $results): array
118
    {
119
        $dictionary = [];
120
121
        foreach ($results as $result) {
122
            $values = [];
123
124
            foreach ($this->foreignKeys[0]->columns as $i => $column) {
125
                $alias = 'laravel_through_key' . ($i > 0 ? "_$i" : '');
126
127
                $values[] = $result->$alias;
128
            }
129
130
            $values = implode("\0", $values);
131
132
            $dictionary[$values][] = $result;
133
        }
134
135
        return $dictionary;
136
    }
137
138
    /**
139
     * Get the columns to select for a leading composite key.
140
     *
141
     * @return array
142
     */
143
    protected function shouldSelectWithCompositeKey(): array
144
    {
145
        $columns = array_slice($this->foreignKeys[0]->columns, 1, null, true);
146
147
        return array_map(
148
            fn ($column, $i) => $this->throughParent->qualifyColumn($column) . " as laravel_through_key_$i",
149
            $columns,
150
            array_keys($columns)
151
        );
152
    }
153
154
    /**
155
     * Add the constraints for a relationship query for a leading composite key.
156
     *
157
     * @param \Illuminate\Database\Eloquent\Builder $query
158
     * @return void
159
     */
160
    public function getRelationExistenceQueryWithCompositeKey(Builder $query): void
161
    {
162
        $columns = array_slice($this->localKeys[0]->columns, 1, null, true);
163
164
        foreach ($columns as $i => $column) {
165
            $query->whereColumn(
166
                $this->farParent->qualifyColumn($column),
167
                '=',
168
                $this->throughParent->qualifyColumn($this->foreignKeys[0]->columns[$i])
169
            );
170
        }
171
    }
172
}
173