addEagerConstraintsWithCompositeKey()   A
last analyzed

Complexity

Conditions 4
Paths 1

Size

Total Lines 27
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 21
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 27
rs 9.584
1
<?php
2
3
namespace Staudenmeir\EloquentJsonRelations\Relations\Traits\CompositeKeys;
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
10
trait SupportsBelongsToJsonCompositeKeys
11
{
12
    /**
13
     * Determine whether the relationship has a composite key.
14
     *
15
     * @return bool
16
     */
17
    protected function hasCompositeKey(): bool
18
    {
19
        return is_array($this->foreignKey);
20
    }
21
22
    /**
23
     * Set the base constraints on the relation query for a composite key.
24
     *
25
     * @return void
26
     */
27
    protected function addConstraintsWithCompositeKey(): void
28
    {
29
        $columns = array_slice($this->ownerKey, 1);
30
31
        foreach ($columns as $column) {
32
            $this->query->where(
33
                $this->related->qualifyColumn($column),
34
                '=',
35
                $this->child->$column
36
            );
37
        }
38
    }
39
40
    /**
41
     * Set the constraints for an eager load of the relation for a composite key.
42
     *
43
     * @param array $models
44
     * @return void
45
     */
46
    protected function addEagerConstraintsWithCompositeKey(array $models): void
47
    {
48
        $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

48
        $keys = (new BaseCollection(/** @scrutinizer ignore-type */ $models))->map(
Loading history...
49
            function (Model $model) {
50
                return array_map(
51
                    fn (string $column) => $model[$column],
52
                    $this->foreignKey
53
                );
54
            }
55
        )->values()->unique(null, true)->all();
56
57
        $this->query->where(
58
            function (Builder $query) use ($keys) {
59
                foreach ($keys as $key) {
60
                    $query->orWhere(
61
                        function (Builder $query) use ($key) {
62
                            foreach ($this->ownerKey as $i => $column) {
63
                                if ($i === 0) {
64
                                    $query->whereIn(
65
                                        $this->related->qualifyColumn($column),
66
                                        $key[$i]
67
                                    );
68
                                } else {
69
                                    $query->where(
70
                                        $this->related->qualifyColumn($column),
71
                                        '=',
72
                                        $key[$i]
73
                                    );
74
                                }
75
                            }
76
                        }
77
                    );
78
                }
79
            }
80
        );
81
    }
82
83
    /**
84
     * Match the eagerly loaded results to their parents for a composite key.
85
     *
86
     * @param array $models
87
     * @param \Illuminate\Database\Eloquent\Collection $results
88
     * @param string $relation
89
     * @return array
90
     */
91
    protected function matchWithCompositeKey(array $models, Collection $results, string $relation): array
92
    {
93
        $dictionary = $this->buildDictionaryWithCompositeKey($results);
94
95
        foreach ($models as $model) {
96
            $matches = [];
97
98
            $additionalValues = array_map(
99
                fn (string $key) => $model->$key,
100
                array_slice($this->ownerKey, 1)
101
            );
102
103
            foreach ($this->getForeignKeys($model) as $id) {
0 ignored issues
show
Bug introduced by
It seems like getForeignKeys() 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
            foreach ($this->/** @scrutinizer ignore-call */ getForeignKeys($model) as $id) {
Loading history...
104
                $values = $additionalValues;
105
106
                array_unshift($values, $id);
107
108
                $key = implode("\0", $values);
109
110
                $matches = array_merge($matches, $dictionary[$key] ?? []);
111
            }
112
113
            $collection = $this->related->newCollection($matches);
114
115
            $model->setRelation($relation, $collection);
116
        }
117
118
        return $models;
119
    }
120
121
    /**
122
     * Build model dictionary keyed by the relation's composite foreign key.
123
     *
124
     * @param \Illuminate\Database\Eloquent\Collection $results
125
     * @return array
126
     */
127
    protected function buildDictionaryWithCompositeKey(Collection $results): array
128
    {
129
        $dictionary = [];
130
131
        foreach ($results as $result) {
132
            $values = array_map(
133
                fn (string $key) => $result->$key,
134
                $this->ownerKey
135
            );
136
137
            $values = implode("\0", $values);
138
139
            $dictionary[$values][] = $result;
140
        }
141
142
        return $dictionary;
143
    }
144
145
    /**
146
     * Add the constraints for a relationship query for a composite key.
147
     *
148
     * @param \Illuminate\Database\Eloquent\Builder $query
149
     * @return void
150
     */
151
    public function getRelationExistenceQueryWithCompositeKey(Builder $query): void
152
    {
153
        $columns = array_slice($this->foreignKey, 1, preserve_keys: true);
154
155
        foreach ($columns as $i => $column) {
156
            $query->whereColumn(
157
                $this->child->qualifyColumn($column),
158
                '=',
159
                $query->qualifyColumn($this->ownerKey[$i])
160
            );
161
        }
162
    }
163
}
164