Passed
Pull Request — master (#221)
by Christopher
06:28 queued 01:03
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->setThroughField($keyChain[1]);
132
        $stub->setKeyField($keyChain[0]);
133
        $stub->setForeignField($keyChain[3]);
134
        return $stub;
135
    }
136
137
    /**
138
     * @param  string              $name
139
     * @param  Relation            $relation
140
     * @param  string              $cacheKey
141
     * @return AssociationStubPolymorphic
142
     */
143
    protected static function handleMorphToMany(string $name, Relation $relation, $cacheKey = 'MorphToMany'): AssociationStubBase
144
    {
145
        //return self::handleBelongsToMany($name,$relation);
146
        //TODO: investigate if this could be treated as a BelongsToMany Or more importantly a Monomorphic as we know both sides
147
        $inverse = self::getKeyChain($relation, "inverse")[0];
148
        $stub = new AssociationStubPolymorphic();
149
        $keyChain = self::getKeyChain($relation, $cacheKey);
150
        $stub->setRelationName($name);
151
        $stub->setThroughFieldChain($keyChain);
152
        $stub->setKeyField($keyChain[3]);
153
        $stub->setForeignField($inverse ? null : $keyChain[1]);
154
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
155
        $stub->setMorphType($keyChain[2]);
156
        $stub->setTargType($inverse ? null : get_class($relation->getRelated()));
157
        return $stub;
158
    }
159
160
    /**
161
     * @param string $name
162
     * @param Relation $relation
163
     * @param string $cacheKey
164
     * @return AssociationStubMonomorphic
165
     */
166
    protected static function handleHasOne(string $name, Relation $relation, $cacheKey = 'HasOneOrMany'): AssociationStubMonomorphic
167
    {
168
        $stub = new AssociationStubMonomorphic();
169
        $keyChain = self::getKeyChain($relation, $cacheKey);
170
        $stub->setRelationName($name);
171
        $stub->setThroughFieldChain($keyChain);
172
        $stub->setKeyField($keyChain[0]);
173
        $stub->setForeignField($keyChain[1]);
174
        $stub->setTargType(get_class($relation->getRelated()));
175
        $stub->setMultiplicity(AssociationStubRelationType::NULL_ONE());
176
        return $stub;
177
    }
178
179
    /**
180
     * @param string $name
181
     * @param Relation $relation
182
     * @param string $cacheKey
183
     * @return AssociationStubMonomorphic
184
     */
185
    protected static function handleHasMany(string $name, Relation $relation, $cacheKey = 'HasOneOrMany'): AssociationStubMonomorphic
186
    {
187
        $stub = new AssociationStubMonomorphic();
188
        $keyChain = self::getKeyChain($relation, $cacheKey);
189
        $stub->setRelationName($name);
190
        $stub->setThroughFieldChain($keyChain);
191
        $stub->setKeyField($keyChain[0]);
192
        $stub->setForeignField($keyChain[1]);
193
        $stub->setTargType(get_class($relation->getRelated()));
194
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
195
        return $stub;
196
    }
197
198
    /**
199
     * @param  string                     $name
200
     * @param  Relation                   $relation
201
     * @param  string                     $cacheKey
202
     * @return AssociationStubPolymorphic
203
     */
204
    protected static function handleMorphOne(string $name, Relation $relation, $cacheKey = 'MorphOneOrMany'):AssociationStubPolymorphic
205
    {
206
        $stub = new AssociationStubPolymorphic();
207
        $keyChain = self::getKeyChain($relation, $cacheKey);
208
        $stub->setRelationName($name);
209
        $stub->setThroughFieldChain($keyChain);
210
        $stub->setKeyField($keyChain[0]);
211
        $stub->setForeignField($keyChain[2]);
212
        $stub->setTargType(get_class($relation->getRelated()));
213
        $stub->setMorphType($keyChain[1]);
214
        $stub->setMultiplicity(AssociationStubRelationType::NULL_ONE());
215
        return $stub;
216
    }
217
218
219
    /**
220
     * @param  string                     $name
221
     * @param  Relation                  $relation
222
     * @param  string                     $cacheKey
223
     * @return AssociationStubPolymorphic
224
     */
225
    protected static function handleMorphMany(string $name, Relation $relation, $cacheKey = 'MorphOneOrMany'): AssociationStubPolymorphic
226
    {
227
        $stub = new AssociationStubPolymorphic();
228
        $keyChain = self::getKeyChain($relation, $cacheKey);
229
        $stub->setRelationName($name);
230
        $stub->setThroughFieldChain($keyChain);
231
        $stub->setKeyField($keyChain[0]);
232
        $stub->setMorphType($keyChain[1]);
233
        $stub->setForeignField($keyChain[2]);
234
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
235
        $stub->setTargType(get_class($relation->getRelated()));
236
        return $stub;
237
    }
238
239
    /**
240
     * @param Relation $relation
241
     * @param string $cacheKey
242
     * @return array
243
     */
244
    private static function getKeyChain(Relation $relation, string $cacheKey) : array
245
    {
246
        $fields = self::$fieldOrderCache[$cacheKey];
247
        $getter =  function () use ($fields) {
248
            $carry = [];
249
            foreach ($fields as $item) {
250
                $v = $this->{$item};
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $this seems to be never defined.
Loading history...
251
                if ($v == null && $item == 'ownerKey') {
252
                    $carry[] = null;
253
                    continue;
254
                }
255
                //TODO: investigate if this is needed can we use quailifed keys?
256
                $segments = explode('.', strval($this->{$item}));
257
                $carry[] = end($segments);
258
            }
259
            return $carry;
260
        };
261
        return call_user_func($getter->bindTo($relation, Relation::class));
262
    }
263
264
    private static $fieldOrderCache = [
265
        'BelongsTo' => ['foreignKey', 'ownerKey'],
266
        'BelongsToMany' => ['parentKey','foreignPivotKey','relatedPivotKey','relatedKey'],
267
        'HasOneOrMany' => ['localKey', 'foreignKey' ],
268
        'HasManyThrough' => ['localKey', 'firstKey', 'secondLocalKey', 'secondKey'],
269
        'MorphToMany' => ['parentKey','foreignPivotKey','morphType', 'relatedPivotKey','relatedKey'],
270
        'MorphTo' => ['foreignKey', 'morphType', 'ownerKey'],
271
        'MorphOneOrMany' => ['foreignKey', 'morphType', 'localKey' ],
272
        'inverse' => ['inverse'], // TODO: currently used to get inverse, should be removed when morephtomany is fixed
273
274
    ];
275
}
276