Passed
Pull Request — master (#221)
by Alex
05:59
created

AssociationStubFactory::handleMorphOne()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
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 12
rs 9.9332
cc 1
nc 1
nop 3
1
<?php
2
declare(strict_types=1);
3
4
namespace AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations;
5
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9
use Illuminate\Database\Eloquent\Relations\HasMany;
10
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
11
use Illuminate\Database\Eloquent\Relations\HasOne;
12
use Illuminate\Database\Eloquent\Relations\MorphMany;
13
use Illuminate\Database\Eloquent\Relations\MorphOne;
14
use Illuminate\Database\Eloquent\Relations\MorphTo;
15
use Illuminate\Database\Eloquent\Relations\MorphToMany;
16
use Illuminate\Database\Eloquent\Relations\Relation;
17
use POData\Common\InvalidOperationException;
18
19
abstract class AssociationStubFactory
20
{
21
    /**
22
     * @param Model $parent
23
     * @param string $name
24
     * @param Relation $relation
25
     * @return AssociationStubBase
26
     * @throws InvalidOperationException
27
     */
28
    public static function associationStubFromRelation(Model $parent, string $name): AssociationStubBase
29
    {
30
        $relation = $parent->{$name}();
31
        $handler = self::getHandlerMethod($relation);
32
        /**
33
         * @var AssociationStubBase $stub
34
         */
35
        $stub = self::{'handle' . $handler}($name, $relation);
36
        $stub->setBaseType(get_class($parent));
37
        if (!$stub->isOk()) {
38
            throw new InvalidOperationException('Generated stub not consistent');
39
        }
40
        return $stub;
41
    }
42
43
    private static function getHandlerMethod(Relation $relation):string
44
    {
45
        $methods = [];
46
        $methods[$relation instanceof BelongsTo] = 'BelongsTo'; //DONE
47
        $methods[$relation instanceof MorphTo] = 'MorphTo'; //DONE
48
        $methods[$relation instanceof BelongsToMany] = 'BelongsToMany'; //DONE
49
        $methods[$relation instanceof MorphToMany] = 'MorphToMany'; // DONE
50
        $methods[$relation instanceof HasOne] = 'HasOne';
51
        $methods[$relation instanceof HasMany] = 'HasMany';
52
        $methods[$relation instanceof HasManyThrough] = 'HasManyThrough'; //DONE
53
        $methods[$relation instanceof MorphMany] = 'MorphMany';
54
        $methods[$relation instanceof MorphOne] = 'MorphOne';
55
56
        return $methods[true];
57
    }
58
59
    /**
60
     * @param  string              $name
61
     * @param  Relation            $relation
62
     * @param  string              $cacheKey
63
     * @return AssociationStubMonomorphic
64
     */
65
    protected static function handleBelongsTo(string $name, Relation $relation, $cacheKey = 'BelongsTo'): AssociationStubMonomorphic
66
    {
67
        $stub = new AssociationStubMonomorphic();
68
        $keyChain = self::getKeyChain($relation, $cacheKey);
69
        $stub->setRelationName($name);
70
        $stub->setThroughFieldChain($keyChain);
71
        $stub->setKeyField($keyChain[0]);
72
        $stub->setForeignField($keyChain[1]);
73
        $stub->setTargType(get_class($relation->getRelated()));
74
        $stub->setMultiplicity(AssociationStubRelationType::ONE());
75
        return $stub;
76
    }
77
78
    /**
79
     * @param  string              $name
80
     * @param  Relation            $relation
81
     * @param  string              $cacheKey
82
     * @return AssociationStubPolymorphic
83
     */
84
    protected static function handleMorphTo(string $name, Relation $relation, $cacheKey = 'MorphTo'): AssociationStubPolymorphic
85
    {
86
        $stub = new AssociationStubPolymorphic();
87
        $keyChain = self::getKeyChain($relation, $cacheKey);
88
        $stub->setRelationName($name);
89
        $stub->setThroughFieldChain($keyChain);
90
        $stub->setKeyField($keyChain[2] ?: $relation->getRelated()->getKeyName());
91
        $stub->setForeignField($keyChain[2]);
92
        $stub->setMultiplicity(AssociationStubRelationType::ONE());
93
        $stub->setTargType(null);
94
        $stub->setMorphType($keyChain[1]);
95
        return $stub;
96
    }
97
98
99
    /**
100
     * @param  string              $name
101
     * @param  Relation            $relation
102
     * @param  string              $cacheKey
103
     * @return AssociationStubMonomorphic
104
     */
105
    protected static function handleBelongsToMany(string $name, Relation $relation, $cacheKey = 'BelongsToMany'): AssociationStubMonomorphic
106
    {
107
        $stub = new AssociationStubMonomorphic();
108
        $keyChain = self::getKeyChain($relation, $cacheKey);
109
        $stub->setRelationName($name);
110
        $stub->setThroughFieldChain($keyChain);
111
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
112
        $stub->setTargType(get_class($relation->getRelated()));
113
        $stub->setKeyField($keyChain[0]);
114
        $stub->setForeignField($keyChain[3]);
115
        return $stub;
116
    }
117
    /**
118
     * @param  string              $name
119
     * @param  Relation            $relation
120
     * @param  string              $cacheKey
121
     * @return AssociationStubMonomorphic
122
     */
123
    protected static function handleHasManyThrough(string $name, Relation $relation, $cacheKey = 'HasManyThrough'): AssociationStubMonomorphic
124
    {
125
        $keyChain = self::getKeyChain($relation, $cacheKey);
126
        $stub = new AssociationStubMonomorphic();
127
        $stub->setRelationName($name);
128
        $stub->setThroughFieldChain($keyChain);
129
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
130
        $stub->setTargType(get_class($relation->getRelated()));
131
        $stub->setKeyField($keyChain[0]);
132
        $stub->setForeignField($keyChain[3]);
133
        return $stub;
134
    }
135
136
    /**
137
     * @param  string              $name
138
     * @param  Relation            $relation
139
     * @param  string              $cacheKey
140
     * @return AssociationStubPolymorphic
141
     */
142
    protected static function handleMorphToMany(string $name, Relation $relation, $cacheKey = 'MorphToMany'): AssociationStubBase
143
    {
144
        //return self::handleBelongsToMany($name,$relation);
145
        //TODO: investigate if this could be treated as a BelongsToMany Or more importantly a Monomorphic as we know both sides
146
        $inverse = self::getKeyChain($relation, "inverse")[0];
147
        $stub = new AssociationStubPolymorphic();
148
        $keyChain = self::getKeyChain($relation, $cacheKey);
149
        $stub->setRelationName($name);
150
        $stub->setThroughFieldChain($keyChain);
151
        $stub->setKeyField($keyChain[3]);
152
        $stub->setForeignField($inverse ? null : $keyChain[1]);
153
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
154
        $stub->setMorphType($keyChain[2]);
155
        $stub->setTargType($inverse ? null : get_class($relation->getRelated()));
156
        return $stub;
157
    }
158
159
    /**
160
     * @param string $name
161
     * @param Relation $relation
162
     * @param string $cacheKey
163
     * @return AssociationStubMonomorphic
164
     */
165
    protected static function handleHasOne(string $name, Relation $relation, $cacheKey = 'HasOneOrMany'): AssociationStubMonomorphic
166
    {
167
        $stub = new AssociationStubMonomorphic();
168
        $keyChain = self::getKeyChain($relation, $cacheKey);
169
        $stub->setRelationName($name);
170
        $stub->setThroughFieldChain($keyChain);
171
        $stub->setKeyField($keyChain[0]);
172
        $stub->setForeignField($keyChain[1]);
173
        $stub->setTargType(get_class($relation->getRelated()));
174
        $stub->setMultiplicity(AssociationStubRelationType::NULL_ONE());
175
        return $stub;
176
    }
177
178
    /**
179
     * @param string $name
180
     * @param Relation $relation
181
     * @param string $cacheKey
182
     * @return AssociationStubMonomorphic
183
     */
184
    protected static function handleHasMany(string $name, Relation $relation, $cacheKey = 'HasOneOrMany'): AssociationStubMonomorphic
185
    {
186
        $stub = new AssociationStubMonomorphic();
187
        $keyChain = self::getKeyChain($relation, $cacheKey);
188
        $stub->setRelationName($name);
189
        $stub->setThroughFieldChain($keyChain);
190
        $stub->setKeyField($keyChain[0]);
191
        $stub->setForeignField($keyChain[1]);
192
        $stub->setTargType(get_class($relation->getRelated()));
193
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
194
        return $stub;
195
    }
196
197
    /**
198
     * @param  string                     $name
199
     * @param  Relation                   $relation
200
     * @param  string                     $cacheKey
201
     * @return AssociationStubPolymorphic
202
     */
203
    protected static function handleMorphOne(string $name, Relation $relation, $cacheKey = 'MorphOneOrMany'):AssociationStubPolymorphic
204
    {
205
        $stub = new AssociationStubPolymorphic();
206
        $keyChain = self::getKeyChain($relation, $cacheKey);
207
        $stub->setRelationName($name);
208
        $stub->setThroughFieldChain($keyChain);
209
        $stub->setKeyField($keyChain[0]);
210
        $stub->setForeignField($keyChain[2]);
211
        $stub->setTargType(get_class($relation->getRelated()));
212
        $stub->setMorphType($keyChain[1]);
213
        $stub->setMultiplicity(AssociationStubRelationType::NULL_ONE());
214
        return $stub;
215
    }
216
217
    /**
218
     * @param  string                     $name
219
     * @param  Relation                   $relation
220
     * @param  string                     $cacheKey
221
     * @return AssociationStubPolymorphic
222
     */
223
    protected static function handleMorphMany(string $name, Relation $relation, $cacheKey = 'MorphOneOrMany'): AssociationStubPolymorphic
224
    {
225
        $stub = new AssociationStubPolymorphic();
226
        $keyChain = self::getKeyChain($relation, $cacheKey);
227
        $stub->setRelationName($name);
228
        $stub->setThroughFieldChain($keyChain);
229
        $stub->setKeyField($keyChain[0]);
230
        $stub->setMorphType($keyChain[1]);
231
        $stub->setForeignField($keyChain[2]);
232
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
233
        $stub->setTargType(get_class($relation->getRelated()));
234
        return $stub;
235
    }
236
237
    /**
238
     * @param Relation $relation
239
     * @param string $cacheKey
240
     * @return array
241
     */
242
    private static function getKeyChain(Relation $relation, string $cacheKey) : array
243
    {
244
        $fields = self::$fieldOrderCache[$cacheKey];
245
        $getter = function () use ($fields) {
246
            $carry = [];
247
            foreach ($fields as $item) {
248
                $v = $this->{$item};
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $this seems to be never defined.
Loading history...
249
                if ($v == null && $item == 'ownerKey') {
250
                    $carry[] = null;
251
                    continue;
252
                }
253
                //TODO: investigate if this is needed can we use quailifed keys?
254
                $segments = explode('.', strval($this->{$item}));
255
                $carry[] = end($segments);
256
            }
257
            return $carry;
258
        };
259
        return call_user_func($getter->bindTo($relation, Relation::class));
260
    }
261
262
    private static $fieldOrderCache = [
263
        'BelongsTo' => ['foreignKey', 'ownerKey'],
264
        'BelongsToMany' => ['parentKey','foreignPivotKey','relatedPivotKey','relatedKey'],
265
        'HasOneOrMany' => ['localKey', 'foreignKey' ],
266
        'HasManyThrough' => ['localKey', 'firstKey', 'secondLocalKey', 'secondKey'],
267
        'MorphToMany' => ['parentKey','foreignPivotKey','morphType', 'relatedPivotKey','relatedKey'],
268
        'MorphTo' => ['foreignKey', 'morphType', 'ownerKey'],
269
        'MorphOneOrMany' => ['foreignKey', 'morphType', 'localKey'],
270
        'inverse' => ['inverse'], // TODO: currently used to get inverse, should be removed when morphtomany is fixed
271
    ];
272
}
273