Completed
Push — master ( d58822...10e381 )
by Jonas
15:56
created

addEagerConstraintsWithCompositeKey()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 16
nc 1
nop 1
dl 0
loc 21
ccs 16
cts 16
cp 1
crap 3
rs 9.7333
c 1
b 0
f 0
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 Staudenmeir\EloquentHasManyDeep\Eloquent\CompositeKey;
9
10
trait SupportsCompositeKeys
11
{
12
    /**
13
     * Determine whether the relationship starts with a composite key.
14
     *
15
     * @return bool
16
     */
17 73
    protected function hasLeadingCompositeKey(): bool
18
    {
19 73
        return $this->localKeys[0] instanceof CompositeKey;
20
    }
21
22
    /**
23
     * Set the base constraints on the relation query for a leading composite key.
24
     *
25
     * @return void
26
     */
27 5
    protected function addConstraintsWithCompositeKey(): void
28
    {
29 5
        $columns = array_slice($this->foreignKeys[0]->columns, 1, null, true);
30
31 5
        foreach ($columns as $i => $column) {
32 5
            $this->query->where(
33 5
                $this->throughParent->qualifyColumn($column),
34
                '=',
35 5
                $this->farParent[$this->localKeys[0]->columns[$i]]
36
            );
37
        }
38
    }
39
40
    /**
41
     * Set the constraints for an eager load of the relation for a leading composite key.
42
     *
43
     * @param array $models
44
     * @return void
45
     */
46 2
    protected function addEagerConstraintsWithCompositeKey(array $models): void
47
    {
48 2
        $keys = collect($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 $value of collect(). ( 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 = collect(/** @scrutinizer ignore-type */ $models)->map(
Loading history...
49 2
            function (Model $model) {
50 2
                return array_map(
51 2
                    fn (string $column) => $model[$column],
52 2
                    $this->localKeys[0]->columns
53
                );
54
            }
55 2
        )->values()->unique(null, true)->all();
56
57 2
        $this->query->where(
58 2
            function (Builder $query) use ($models, $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...
59 2
                foreach ($keys as $key) {
60 2
                    $query->orWhere(
61 2
                        function (Builder $query) use ($key) {
62 2
                            foreach ($this->foreignKeys[0]->columns as $i => $column) {
63 2
                                $query->where(
64 2
                                    $this->throughParent->qualifyColumn($column),
65
                                    '=',
66 2
                                    $key[$i]
67
                                );
68
                            }
69
                        }
70
                    );
71
                }
72
            }
73
        );
74
    }
75
76
    /**
77
     * Match the eagerly loaded results to their parents for a leading composite key.
78
     *
79
     * @param array $models
80
     * @param \Illuminate\Database\Eloquent\Collection $results
81
     * @param string $relation
82
     * @return array
83
     */
84 2
    protected function matchWithCompositeKey(array $models, Collection $results, string $relation): array
85
    {
86 2
        $dictionary = $this->buildDictionaryWithCompositeKey($results);
87
88 2
        foreach ($models as $model) {
89 2
            $values = [];
90
91 2
            foreach ($this->localKeys[0]->columns as $column) {
92 2
                $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

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