Completed
Pull Request — master (#219)
by Christopher
08:26 queued 03:44
created

MetadataRelationsTrait::addRelationsHook()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 10
c 2
b 0
f 0
dl 0
loc 22
rs 9.9332
cc 3
nc 4
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: alex
5
 * Date: 13/02/20
6
 * Time: 1:08 PM.
7
 */
8
namespace AlgoWeb\PODataLaravel\Models;
9
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
use Illuminate\Database\Eloquent\Relations\MorphMany;
13
use Illuminate\Database\Eloquent\Relations\MorphOne;
14
use Illuminate\Database\Eloquent\Relations\MorphToMany;
15
use Illuminate\Database\Eloquent\Relations\Relation;
16
use POData\Common\InvalidOperationException;
17
18
trait MetadataRelationsTrait
19
{
20
    use MetadataKeyMethodNamesTrait;
21
22
    protected static $relationHooks = [];
23
    protected static $relationCategories = [];
24
25
26
    /**
27
     * Get model's relationships.
28
     *
29
     * @throws InvalidOperationException
30
     * @throws \ReflectionException
31
     * @return array
32
     */
33
    public function getRelationships()
34
    {
35
        if (empty(static::$relationHooks)) {
36
            $hooks = [];
37
38
            $rels = $this->getRelationshipsFromMethods(true);
39
40
            $this->getRelationshipsUnknownPolyMorph($rels, $hooks);
41
42
            $this->getRelationshipsKnownPolyMorph($rels, $hooks);
43
44
            $this->getRelationshipsHasOne($rels, $hooks);
45
46
            $this->getRelationshipsHasMany($rels, $hooks);
47
48
            static::$relationHooks = $hooks;
49
        }
50
51
        return static::$relationHooks;
52
    }
53
54
    /**
55
     * Is this model the known side of at least one polymorphic relation?
56
     *
57
     * @throws InvalidOperationException
58
     * @throws \ReflectionException
59
     */
60
    public function isKnownPolymorphSide()
61
    {
62
        // isKnownPolymorph needs to be checking KnownPolymorphSide results - if you're checking UnknownPolymorphSide,
63
        // you're turned around
64
        $rels = $this->getRelationshipsFromMethods();
65
        return !empty($rels['KnownPolyMorphSide']);
66
    }
67
68
    /**
69
     * Is this model on the unknown side of at least one polymorphic relation?
70
     *
71
     * @throws InvalidOperationException
72
     * @throws \ReflectionException
73
     */
74
    public function isUnknownPolymorphSide()
75
    {
76
        // isUnknownPolymorph needs to be checking UnknownPolymorphSide results - if you're checking KnownPolymorphSide,
77
        // you're turned around
78
        $rels = $this->getRelationshipsFromMethods();
79
        return !empty($rels['UnknownPolyMorphSide']);
80
    }
81
82
    /**
83
     * @param bool $biDir
84
     *
85
     * @throws InvalidOperationException
86
     * @throws \ReflectionException
87
     * @return array
88
     */
89
    protected function getRelationshipsFromMethods(bool $biDir = false)
90
    {
91
        $biDirVal = intval($biDir);
92
        $isCached = isset(static::$relationCategories[$biDirVal]) && !empty(static::$relationCategories[$biDirVal]);
93
        if (!$isCached) {
94
            /** @var Model $model */
95
            $model = $this;
96
            $relationships = [
97
                'HasOne' => [],
98
                'UnknownPolyMorphSide' => [],
99
                'HasMany' => [],
100
                'KnownPolyMorphSide' => []
101
            ];
102
            $methods = $this->getModelClassMethods($model);
103
            foreach ($methods as $method) {
104
                //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
105
                $reflection = new \ReflectionMethod($model, $method);
106
                $fileName = $reflection->getFileName();
107
108
                $file = new \SplFileObject($fileName);
109
                $file->seek($reflection->getStartLine()-1);
110
                $code = '';
111
                while ($file->key() < $reflection->getEndLine()) {
112
                    $code .= $file->current();
113
                    $file->next();
114
                }
115
116
                $code = trim(preg_replace('/\s\s+/', '', $code));
117
                if (false === stripos($code, 'function')) {
118
                    $msg = 'Function definition must have keyword \'function\'';
119
                    throw new InvalidOperationException($msg);
120
                }
121
                $begin = strpos($code, 'function(');
122
                $code = substr($code, /* @scrutinizer ignore-type */$begin, strrpos($code, '}')-$begin+1);
123
                $lastCode = $code[strlen(/* @scrutinizer ignore-type */$code)-1];
124
                if ('}' != $lastCode) {
125
                    $msg = 'Final character of function definition must be closing brace';
126
                    throw new InvalidOperationException($msg);
127
                }
128
                foreach (static::$relTypes as $relation) {
129
                    $search = '$this->' . $relation . '(';
130
                    $found = stripos(/* @scrutinizer ignore-type */$code, $search);
131
                    if (!$found) {
132
                        continue;
133
                    }
134
                    //Resolve the relation's model to a Relation object.
135
                    $relationObj = $model->$method();
136
                    if (!($relationObj instanceof Relation)) {
137
                        continue;
138
                    }
139
                    $relObject = $relationObj->getRelated();
140
                    $relatedModel = '\\' . get_class($relObject);
141
                    if (!in_array(MetadataTrait::class, class_uses($relatedModel))) {
142
                        continue;
143
                    }
144
                    $targObject = $biDir ? $relationObj : $relatedModel;
145
                    if (in_array($relation, static::$manyRelTypes)) {
146
                        //Collection or array of models (because Collection is Arrayable)
147
                        $relationships['HasMany'][$method] = $targObject;
148
                    } elseif ('morphTo' === $relation) {
149
                        // Model isn't specified because relation is polymorphic
150
                        $relationships['UnknownPolyMorphSide'][$method] =
151
                            $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
152
                    } else {
153
                        //Single model is returned
154
                        $relationships['HasOne'][$method] = $targObject;
155
                    }
156
                    if (in_array($relation, ['morphMany', 'morphOne', 'morphToMany'])) {
157
                        $relationships['KnownPolyMorphSide'][$method] = $targObject;
158
                    }
159
                    if (in_array($relation, ['morphedByMany'])) {
160
                        $relationships['UnknownPolyMorphSide'][$method] = $targObject;
161
                    }
162
                }
163
            }
164
            static::$relationCategories[$biDirVal] = $relationships;
165
        }
166
        return static::$relationCategories[$biDirVal];
167
    }
168
169
    /**
170
     * @param  array                     $rels
171
     * @param  array                     $hooks
172
     * @throws InvalidOperationException
173
     */
174
    protected function getRelationshipsHasMany(array $rels, array &$hooks)
175
    {
176
        /**
177
         * @var string   $property
178
         * @var Relation $relation
179
         */
180
        foreach ($rels['HasMany'] as $property => $relation) {
181
            if ($relation instanceof MorphMany || $relation instanceof MorphToMany) {
182
                continue;
183
            }
184
            $mult = '*';
185
            $targ = get_class($relation->getRelated());
186
            $keyName = $this->polyglotFkKey($relation);
187
            $localName = $this->polyglotRkKey($relation);
188
            $thruName = $this->polyglotThroughKey($relation);
189
190
            $first = $keyName;
191
            $last = $localName;
192
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ, null, $thruName);
193
        }
194
    }
195
196
    /**
197
     * @param  array                     $rels
198
     * @param  array                     $hooks
199
     * @throws InvalidOperationException
200
     */
201
    protected function getRelationshipsHasOne(array $rels, array &$hooks)
202
    {
203
        /**
204
         * @var string   $property
205
         * @var Relation $foo
206
         */
207
        foreach ($rels['HasOne'] as $property => $foo) {
208
            if ($foo instanceof MorphOne) {
209
                continue;
210
            }
211
            $isBelong = $foo instanceof BelongsTo;
212
            $mult = $isBelong ? '1' : '0..1';
213
            $targ = get_class($foo->getRelated());
214
215
            $keyName = $this->polyglotFkKey($foo);
216
            $localName = $this->polyglotRkKey($foo);
217
            $first = $isBelong ? $localName : $keyName;
218
            $last = $isBelong ? $keyName : $localName;
219
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
220
        }
221
    }
222
223
    /**
224
     * @param  array                     $rels
225
     * @param  array                     $hooks
226
     * @throws InvalidOperationException
227
     */
228
    protected function getRelationshipsKnownPolyMorph(array $rels, array &$hooks)
229
    {
230
        /**
231
         * @var string   $property
232
         * @var Relation $foo
233
         */
234
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
235
            $isMany = $foo instanceof MorphToMany;
236
            $targ = get_class($foo->getRelated());
237
            $mult = $isMany ? '*' : ($foo instanceof MorphMany ? '*' : '1');
238
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
239
240
            $keyName = $this->polyglotFkKey($foo);
241
            $localName = $this->polyglotRkKey($foo);
242
            $first = $isMany ? $keyName : $localName;
243
            $last = $isMany ? $localName : $keyName;
244
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ, 'unknown');
245
        }
246
    }
247
248
    /**
249
     * @param  array                     $rels
250
     * @param  array                     $hooks
251
     * @throws InvalidOperationException
252
     */
253
    protected function getRelationshipsUnknownPolyMorph(array $rels, array &$hooks)
254
    {
255
        /**
256
         * @var string   $property
257
         * @var Relation $foo
258
         */
259
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
260
            $isMany = $foo instanceof MorphToMany;
261
            $targ = get_class($foo->getRelated());
262
            $mult = $isMany ? '*' : '1';
263
            
264
            $keyName = $this->polyglotFkKey($foo);
265
            $localName = $this->polyglotRkKey($foo);
266
267
            $first = $keyName;
268
            $last = (isset($localName) && '' != $localName) ? $localName : $foo->getRelated()->getKeyName();
269
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ, 'known');
270
        }
271
    }
272
273
    /**
274
     * @param             $hooks
275
     * @param             $foreignField
276
     * @param             $RelationName
277
     * @param             $localKey
278
     * @param             $mult
279
     * @param string|null $relatedObject
280
     * @param mixed|null  $type
281
     * @param mixed|null  $through
282
     */
283
    protected function addRelationsHook(
284
        array &$hooks,
285
        $foreignField,
286
        $RelationName,
287
        $localKey,
288
        $mult,
289
        $relatedObject,
290
        $type = null,
291
        $through = null
292
    ) {
293
        if (!isset($hooks[$foreignField])) {
294
            $hooks[$foreignField] = [];
295
        }
296
        if (!isset($hooks[$foreignField][$relatedObject])) {
297
            $hooks[$foreignField][$relatedObject] = [];
298
        }
299
        $hooks[$foreignField][$relatedObject][$RelationName] = [
300
            'property' => $RelationName,
301
            'local' => $localKey,
302
            'through' => $through,
303
            'multiplicity' => $mult,
304
            'type' => $type
305
        ];
306
    }
307
}
308