HasRelationships   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 19
eloc 42
dl 0
loc 176
rs 10
c 6
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A hasOneOrManyDeepLocalKeys() 0 14 4
A hasManyDeep() 0 3 1
A hasOneDeep() 0 3 1
A newRelatedDeepThroughInstance() 0 5 2
A newHasOneDeep() 0 3 1
A hasOneOrManyDeep() 0 18 2
A hasOneOrManyDeepForeignKeys() 0 14 4
A newHasManyDeep() 0 3 1
A hasOneOrManyDeepThroughParents() 0 17 3
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
    public function hasManyDeep($related, array $through, array $foreignKeys = [], array $localKeys = [])
26
    {
27
        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
    public function hasOneDeep($related, array $through, array $foreignKeys = [], array $localKeys = [])
40
    {
41
        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
    protected function hasOneOrManyDeep($related, array $through, array $foreignKeys, array $localKeys)
54
    {
55
        $relatedSegments = preg_split('/\s+from\s+/i', $related);
56
57
        /** @var \Illuminate\Database\Eloquent\Model $relatedInstance */
58
        $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
        if (isset($relatedSegments[1])) {
61
            $relatedInstance->setTable($relatedSegments[1]);
62
        }
63
64
        $throughParents = $this->hasOneOrManyDeepThroughParents($through);
65
66
        $foreignKeys = $this->hasOneOrManyDeepForeignKeys($relatedInstance, $throughParents, $foreignKeys);
67
68
        $localKeys = $this->hasOneOrManyDeepLocalKeys($relatedInstance, $throughParents, $localKeys);
69
70
        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
    protected function hasOneOrManyDeepThroughParents(array $through)
80
    {
81
        return array_map(function ($class) {
82
            $segments = preg_split('/\s+(as|from)\s+/i', $class, -1, PREG_SPLIT_DELIM_CAPTURE);
83
84
            $instance = $this->newRelatedDeepThroughInstance($segments[0]);
85
86
            if (isset($segments[1])) {
87
                $instance->setTable(
88
                    $segments[1] === 'as'
89
                        ? $instance->getTable().' as '.$segments[2]
90
                        : $segments[2]
91
                );
92
            }
93
94
            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 \Illuminate\Database\Eloquent\Model
103
     */
104
    protected function newRelatedDeepThroughInstance(string $class): Model
105
    {
106
        return str_contains($class, '\\')
107
            ? $this->newRelatedThroughInstance($class)
0 ignored issues
show
Bug introduced by
The method newRelatedThroughInstance() does not exist on Staudenmeir\EloquentHasManyDeep\HasRelationships. Did you maybe mean newRelatedDeepThroughInstance()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

107
            ? $this->/** @scrutinizer ignore-call */ newRelatedThroughInstance($class)

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
108
            : (new Pivot())->setTable($class);
109
    }
110
111
    /**
112
     * Prepare the foreign keys for a has-one-deep or has-many-deep relationship.
113
     *
114
     * @param \Illuminate\Database\Eloquent\Model $related
115
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
116
     * @param array $foreignKeys
117
     * @return array
118
     */
119
    protected function hasOneOrManyDeepForeignKeys(Model $related, array $throughParents, array $foreignKeys)
120
    {
121
        foreach (array_merge([$this], $throughParents) as $i => $instance) {
122
            /** @var \Illuminate\Database\Eloquent\Model $instance */
123
            if (!isset($foreignKeys[$i])) {
124
                if ($instance instanceof Pivot) {
125
                    $foreignKeys[$i] = ($throughParents[$i] ?? $related)->getKeyName();
126
                } else {
127
                    $foreignKeys[$i] = $instance->getForeignKey();
128
                }
129
            }
130
        }
131
132
        return $foreignKeys;
133
    }
134
135
    /**
136
     * Prepare the local keys for a has-one-deep or has-many-deep relationship.
137
     *
138
     * @param \Illuminate\Database\Eloquent\Model $related
139
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
140
     * @param array $localKeys
141
     * @return array
142
     */
143
    protected function hasOneOrManyDeepLocalKeys(Model $related, array $throughParents, array $localKeys)
144
    {
145
        foreach (array_merge([$this], $throughParents) as $i => $instance) {
146
            /** @var \Illuminate\Database\Eloquent\Model $instance */
147
            if (!isset($localKeys[$i])) {
148
                if ($instance instanceof Pivot) {
149
                    $localKeys[$i] = ($throughParents[$i] ?? $related)->getForeignKey();
150
                } else {
151
                    $localKeys[$i] = $instance->getKeyName();
152
                }
153
            }
154
        }
155
156
        return $localKeys;
157
    }
158
159
    /**
160
     * Instantiate a new HasManyDeep relationship.
161
     *
162
     * @param \Illuminate\Database\Eloquent\Builder $query
163
     * @param \Illuminate\Database\Eloquent\Model $farParent
164
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
165
     * @param array $foreignKeys
166
     * @param array $localKeys
167
     * @return \Staudenmeir\EloquentHasManyDeep\HasManyDeep
168
     */
169
    protected function newHasManyDeep(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
170
    {
171
        return new HasManyDeep($query, $farParent, $throughParents, $foreignKeys, $localKeys);
172
    }
173
174
    /**
175
     * Instantiate a new HasOneDeep relationship.
176
     *
177
     * @param \Illuminate\Database\Eloquent\Builder $query
178
     * @param \Illuminate\Database\Eloquent\Model $farParent
179
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
180
     * @param array $foreignKeys
181
     * @param array $localKeys
182
     * @return \Staudenmeir\EloquentHasManyDeep\HasOneDeep
183
     */
184
    protected function newHasOneDeep(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
185
    {
186
        return new HasOneDeep($query, $farParent, $throughParents, $foreignKeys, $localKeys);
187
    }
188
}
189