Passed
Push — master ( 5af9c3...926d18 )
by Jonas
11:53
created

HasRelationships::hasManyDeepFromRelations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Staudenmeir\EloquentHasManyDeep;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Relations\Pivot;
8
use Staudenmeir\EloquentHasManyDeep\Traits\ConcatenatesRelationships;
9
use Staudenmeir\EloquentHasManyDeep\Traits\ReversesRelationships;
10
11
trait HasRelationships
12
{
13
    use ConcatenatesRelationships;
14
    use ReversesRelationships;
15
16
    /**
17
     * Define a has-many-deep relationship.
18
     *
19
     * @param string $related
20
     * @param array $through
21
     * @param array $foreignKeys
22
     * @param array $localKeys
23
     * @return \Staudenmeir\EloquentHasManyDeep\HasManyDeep
24
     */
25 53
    public function hasManyDeep($related, array $through, array $foreignKeys = [], array $localKeys = [])
26
    {
27 53
        return $this->newHasManyDeep(...$this->hasOneOrManyDeep($related, $through, $foreignKeys, $localKeys));
28
    }
29
30
    /**
31
     * Define a has-one-deep relationship.
32
     *
33
     * @param string $related
34
     * @param array $through
35
     * @param array $foreignKeys
36
     * @param array $localKeys
37
     * @return \Staudenmeir\EloquentHasManyDeep\HasOneDeep
38
     */
39 10
    public function hasOneDeep($related, array $through, array $foreignKeys = [], array $localKeys = [])
40
    {
41 10
        return $this->newHasOneDeep(...$this->hasOneOrManyDeep($related, $through, $foreignKeys, $localKeys));
42
    }
43
44
    /**
45
     * Prepare a has-one-deep or has-many-deep relationship.
46
     *
47
     * @param string $related
48
     * @param array $through
49
     * @param array $foreignKeys
50
     * @param array $localKeys
51
     * @return array
52
     */
53 60
    protected function hasOneOrManyDeep($related, array $through, array $foreignKeys, array $localKeys)
54
    {
55 60
        $relatedSegments = preg_split('/\s+from\s+/i', $related);
56
57
        /** @var \Illuminate\Database\Eloquent\Model $relatedInstance */
58 60
        $relatedInstance = $this->newRelatedInstance($relatedSegments[0]);
0 ignored issues
show
Bug introduced by
It seems like newRelatedInstance() 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

58
        /** @scrutinizer ignore-call */ 
59
        $relatedInstance = $this->newRelatedInstance($relatedSegments[0]);
Loading history...
59
60 60
        if (isset($relatedSegments[1])) {
61 1
            $relatedInstance->setTable($relatedSegments[1]);
62
        }
63
64 60
        $throughParents = $this->hasOneOrManyDeepThroughParents($through);
65
66 60
        $foreignKeys = $this->hasOneOrManyDeepForeignKeys($relatedInstance, $throughParents, $foreignKeys);
67
68 60
        $localKeys = $this->hasOneOrManyDeepLocalKeys($relatedInstance, $throughParents, $localKeys);
69
70 60
        return [$relatedInstance->newQuery(), $this, $throughParents, $foreignKeys, $localKeys];
71
    }
72
73
    /**
74
     * Prepare the through parents for a has-one-deep or has-many-deep relationship.
75
     *
76
     * @param array $through
77
     * @return array
78
     */
79 60
    protected function hasOneOrManyDeepThroughParents(array $through)
80
    {
81 60
        return array_map(function ($class) {
82 60
            $segments = preg_split('/\s+(as|from)\s+/i', $class, -1, PREG_SPLIT_DELIM_CAPTURE);
83
84 60
            $instance = str_contains($segments[0], '\\')
85 60
                ? (method_exists($this, 'newRelatedThroughInstance') // TODO[L10]
86 60
                    ? $this->newRelatedThroughInstance($segments[0])
87 60
                    : new $segments[0]())
88 13
                : (new Pivot())->setTable($segments[0]);
89
90 60
            if (isset($segments[1])) {
91 7
                $instance->setTable(
92 7
                    $segments[1] === 'as'
93 5
                        ? $instance->getTable().' as '.$segments[2]
94 7
                        : $segments[2]
95
                );
96
            }
97
98 60
            return $instance;
99
        }, $through);
100
    }
101
102
    /**
103
     * Prepare the foreign keys for a has-one-deep or has-many-deep relationship.
104
     *
105
     * @param \Illuminate\Database\Eloquent\Model $related
106
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
107
     * @param array $foreignKeys
108
     * @return array
109
     */
110 60
    protected function hasOneOrManyDeepForeignKeys(Model $related, array $throughParents, array $foreignKeys)
111
    {
112 60
        foreach (array_merge([$this], $throughParents) as $i => $instance) {
113
            /** @var \Illuminate\Database\Eloquent\Model $instance */
114 60
            if (!isset($foreignKeys[$i])) {
115 40
                if ($instance instanceof Pivot) {
116 8
                    $foreignKeys[$i] = ($throughParents[$i] ?? $related)->getKeyName();
117
                } else {
118 40
                    $foreignKeys[$i] = $instance->getForeignKey();
119
                }
120
            }
121
        }
122
123 60
        return $foreignKeys;
124
    }
125
126
    /**
127
     * Prepare the local keys for a has-one-deep or has-many-deep relationship.
128
     *
129
     * @param \Illuminate\Database\Eloquent\Model $related
130
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
131
     * @param array $localKeys
132
     * @return array
133
     */
134 60
    protected function hasOneOrManyDeepLocalKeys(Model $related, array $throughParents, array $localKeys)
135
    {
136 60
        foreach (array_merge([$this], $throughParents) as $i => $instance) {
137
            /** @var \Illuminate\Database\Eloquent\Model $instance */
138 60
            if (!isset($localKeys[$i])) {
139 43
                if ($instance instanceof Pivot) {
140 8
                    $localKeys[$i] = ($throughParents[$i] ?? $related)->getForeignKey();
141
                } else {
142 43
                    $localKeys[$i] = $instance->getKeyName();
143
                }
144
            }
145
        }
146
147 60
        return $localKeys;
148
    }
149
150
    /**
151
     * Instantiate a new HasManyDeep relationship.
152
     *
153
     * @param \Illuminate\Database\Eloquent\Builder $query
154
     * @param \Illuminate\Database\Eloquent\Model $farParent
155
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
156
     * @param array $foreignKeys
157
     * @param array $localKeys
158
     * @return \Staudenmeir\EloquentHasManyDeep\HasManyDeep
159
     */
160 53
    protected function newHasManyDeep(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
161
    {
162 53
        return new HasManyDeep($query, $farParent, $throughParents, $foreignKeys, $localKeys);
163
    }
164
165
    /**
166
     * Instantiate a new HasOneDeep relationship.
167
     *
168
     * @param \Illuminate\Database\Eloquent\Builder $query
169
     * @param \Illuminate\Database\Eloquent\Model $farParent
170
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
171
     * @param array $foreignKeys
172
     * @param array $localKeys
173
     * @return \Staudenmeir\EloquentHasManyDeep\HasOneDeep
174
     */
175 10
    protected function newHasOneDeep(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
176
    {
177 10
        return new HasOneDeep($query, $farParent, $throughParents, $foreignKeys, $localKeys);
178
    }
179
}
180