buildDictionaryForDeepRelationship()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
dl 0
loc 21
rs 9.6111
c 1
b 0
f 0
cc 5
nc 4
nop 1
1
<?php
2
3
namespace Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\ThirdParty\LaravelHasManyMerged;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Query\JoinClause;
8
use Korridor\LaravelHasManyMerged\HasManyMerged as Base;
9
use Staudenmeir\EloquentHasManyDeepContracts\Interfaces\ConcatenableRelation;
10
11
/**
12
 * @copyright Based on package by Constantin Graf (korridor): https://github.com/korridor/laravel-has-many-merged
13
 */
14
class HasManyMerged extends Base implements ConcatenableRelation
15
{
16
    /**
17
     * Append the relation's through parents, foreign and local keys to a deep relationship.
18
     *
19
     * @param \Illuminate\Database\Eloquent\Model[] $through
20
     * @param array $foreignKeys
21
     * @param array $localKeys
22
     * @param int $position
23
     * @return array
24
     */
25
    public function appendToDeepRelationship(array $through, array $foreignKeys, array $localKeys, int $position): array
26
    {
27
        if ($position === 0) {
28
            $foreignKeys[] = function (Builder $query, ?Builder $parentQuery = null) {
29
                if ($parentQuery) {
30
                    $this->getRelationExistenceQuery($this->query, $parentQuery);
31
                }
32
33
                $query->mergeConstraintsFrom($this->query);
34
            };
35
36
            $localKeys[] = $this->localKey;
37
        } else {
38
            $foreignKeys[] = function (Builder $query, JoinClause $join) {
39
                $join->on(
40
                    function (JoinClause $join) {
41
                        foreach ($this->foreignKeys as $foreignKey) {
42
                            $join->orOn($foreignKey, '=', $this->getQualifiedParentKeyName());
43
                        }
44
                    }
45
                );
46
            };
47
48
            $localKeys[] = null;
49
        }
50
51
        return [$through, $foreignKeys, $localKeys];
52
    }
53
54
    /**
55
     * Get the custom through key for an eager load of the relation.
56
     *
57
     * @param string $alias
58
     * @return array
59
     */
60
    public function getThroughKeyForDeepRelationships(string $alias): array
61
    {
62
        $columns = [];
63
64
        foreach ($this->foreignKeys as $i => $foreignKey) {
65
            $columns[] = "$foreignKey as $alias" . ($i > 0 ? "_$i" : '');
66
        }
67
68
        return $columns;
69
    }
70
71
    /**
72
     * Set the constraints for an eager load of the deep relation.
73
     *
74
     * @param \Illuminate\Database\Eloquent\Builder $query
75
     * @param array $models
76
     * @return void
77
     */
78
    public function addEagerConstraintsToDeepRelationship(Builder $query, array $models): void
79
    {
80
        $this->addEagerConstraints($models);
81
82
        $query->mergeConstraintsFrom($this->query);
83
    }
84
85
    /**
86
     * Match the eagerly loaded results for a deep relationship to their parents.
87
     *
88
     * @param array $models
89
     * @param \Illuminate\Database\Eloquent\Collection $results
90
     * @param string $relation
91
     * @return array
92
     */
93
    public function matchResultsForDeepRelationship(array $models, Collection $results, string $relation): array
94
    {
95
        $dictionary = $this->buildDictionaryForDeepRelationship($results);
96
97
        foreach ($models as $model) {
98
            if (isset($dictionary[$key = $model->getAttribute($this->localKey)])) {
99
                $model->setRelation(
100
                    $relation,
101
                    $this->getRelated()->newCollection($dictionary[$key])->unique($this->getRelated()->getKeyName())
102
                );
103
            }
104
        }
105
106
        return $models;
107
    }
108
109
    /**
110
     * Build the model dictionary for a deep relation.
111
     *
112
     * @param \Illuminate\Database\Eloquent\Collection $results
113
     * @return array
114
     */
115
    protected function buildDictionaryForDeepRelationship(Collection $results): array
116
    {
117
        $dictionary = [];
118
119
        $foreignKeyNames = array_map(
120
            fn ($i) => 'laravel_through_key' . ($i > 0 ? "_$i" : ''),
121
            range(0, count($this->foreignKeys) - 1)
122
        );
123
124
        foreach ($results as $result) {
125
            foreach ($foreignKeyNames as $foreignKeyName) {
126
                $foreignKeyValue = $result->{$foreignKeyName};
127
                if (!isset($dictionary[$foreignKeyValue])) {
128
                    $dictionary[$foreignKeyValue] = [];
129
                }
130
131
                $dictionary[$foreignKeyValue][] = $result;
132
            }
133
        }
134
135
        return $dictionary;
136
    }
137
138
    /**
139
     * Create a new instance of the relation from a base relation instance.
140
     *
141
     * @param \Korridor\LaravelHasManyMerged\HasManyMerged $relation
142
     * @return static
143
     */
144
    public static function fromBaseRelation(Base $relation): static
145
    {
146
        return new static(
147
            $relation->getQuery(),
148
            $relation->getParent(),
149
            $relation->getQualifiedForeignKeyNames(),
150
            (fn () => $this->localKey)->call($relation)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $this seems to be never defined.
Loading history...
151
        );
152
    }
153
}
154