Passed
Push — master ( f63467...cbae16 )
by Jonas
02:07
created

HasRelationships::newRelatedDeepThroughInstance()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 7
ccs 6
cts 6
cp 1
crap 3
rs 10
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\Eloquent\Traits\ConcatenatesRelationships;
9
use Staudenmeir\EloquentHasManyDeep\Eloquent\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 66
    public function hasManyDeep($related, array $through, array $foreignKeys = [], array $localKeys = [])
26
    {
27 66
        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 73
    protected function hasOneOrManyDeep($related, array $through, array $foreignKeys, array $localKeys)
54
    {
55 73
        $relatedSegments = preg_split('/\s+from\s+/i', $related);
56
57
        /** @var \Illuminate\Database\Eloquent\Model $relatedInstance */
58 73
        $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 73
        if (isset($relatedSegments[1])) {
61 1
            $relatedInstance->setTable($relatedSegments[1]);
62
        }
63
64 73
        $throughParents = $this->hasOneOrManyDeepThroughParents($through);
65
66 73
        $foreignKeys = $this->hasOneOrManyDeepForeignKeys($relatedInstance, $throughParents, $foreignKeys);
67
68 73
        $localKeys = $this->hasOneOrManyDeepLocalKeys($relatedInstance, $throughParents, $localKeys);
69
70 73
        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 73
    protected function hasOneOrManyDeepThroughParents(array $through)
80
    {
81 73
        return array_map(function ($class) {
82 73
            $segments = preg_split('/\s+(as|from)\s+/i', $class, -1, PREG_SPLIT_DELIM_CAPTURE);
83
84 73
            $instance = $this->newRelatedDeepThroughInstance($segments[0]);
85
86 73
            if (isset($segments[1])) {
87 7
                $instance->setTable(
88 7
                    $segments[1] === 'as'
89 5
                        ? $instance->getTable().' as '.$segments[2]
90 7
                        : $segments[2]
91
                );
92
            }
93
94 73
            return $instance;
95
        }, $through);
96
    }
97
98
    /**
99
     * Create a new model instance for a related "deep through" model.
100
     *
101
     * @param  string  $class
102
     * @return mixed
103
     */
104 73
    protected function newRelatedDeepThroughInstance($class)
105
    {
106 73
        return str_contains($class, '\\')
107 73
            ? (method_exists($this, 'newRelatedThroughInstance') // TODO[L10]
108 73
                ? $this->newRelatedThroughInstance($class)
109 73
                : new $class)
110 73
            : (new Pivot)->setTable($class);
111
    }
112
113
    /**
114
     * Prepare the foreign keys for a has-one-deep or has-many-deep relationship.
115
     *
116
     * @param \Illuminate\Database\Eloquent\Model $related
117
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
118
     * @param array $foreignKeys
119
     * @return array
120
     */
121 73
    protected function hasOneOrManyDeepForeignKeys(Model $related, array $throughParents, array $foreignKeys)
122
    {
123 73
        foreach (array_merge([$this], $throughParents) as $i => $instance) {
124
            /** @var \Illuminate\Database\Eloquent\Model $instance */
125 73
            if (!isset($foreignKeys[$i])) {
126 44
                if ($instance instanceof Pivot) {
127 8
                    $foreignKeys[$i] = ($throughParents[$i] ?? $related)->getKeyName();
128
                } else {
129 44
                    $foreignKeys[$i] = $instance->getForeignKey();
130
                }
131
            }
132
        }
133
134 73
        return $foreignKeys;
135
    }
136
137
    /**
138
     * Prepare the local keys for a has-one-deep or has-many-deep relationship.
139
     *
140
     * @param \Illuminate\Database\Eloquent\Model $related
141
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
142
     * @param array $localKeys
143
     * @return array
144
     */
145 73
    protected function hasOneOrManyDeepLocalKeys(Model $related, array $throughParents, array $localKeys)
146
    {
147 73
        foreach (array_merge([$this], $throughParents) as $i => $instance) {
148
            /** @var \Illuminate\Database\Eloquent\Model $instance */
149 73
            if (!isset($localKeys[$i])) {
150 47
                if ($instance instanceof Pivot) {
151 8
                    $localKeys[$i] = ($throughParents[$i] ?? $related)->getForeignKey();
152
                } else {
153 47
                    $localKeys[$i] = $instance->getKeyName();
154
                }
155
            }
156
        }
157
158 73
        return $localKeys;
159
    }
160
161
    /**
162
     * Instantiate a new HasManyDeep relationship.
163
     *
164
     * @param \Illuminate\Database\Eloquent\Builder $query
165
     * @param \Illuminate\Database\Eloquent\Model $farParent
166
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
167
     * @param array $foreignKeys
168
     * @param array $localKeys
169
     * @return \Staudenmeir\EloquentHasManyDeep\HasManyDeep
170
     */
171 66
    protected function newHasManyDeep(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
172
    {
173 66
        return new HasManyDeep($query, $farParent, $throughParents, $foreignKeys, $localKeys);
174
    }
175
176
    /**
177
     * Instantiate a new HasOneDeep relationship.
178
     *
179
     * @param \Illuminate\Database\Eloquent\Builder $query
180
     * @param \Illuminate\Database\Eloquent\Model $farParent
181
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
182
     * @param array $foreignKeys
183
     * @param array $localKeys
184
     * @return \Staudenmeir\EloquentHasManyDeep\HasOneDeep
185
     */
186 10
    protected function newHasOneDeep(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
187
    {
188 10
        return new HasOneDeep($query, $farParent, $throughParents, $foreignKeys, $localKeys);
189
    }
190
}
191