Completed
Push — master ( c2ab20...6fd91f )
by Alex
14s queued 12s
created

associationStubFromRelation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 5
c 3
b 0
f 0
dl 0
loc 10
rs 10
cc 1
nc 1
nop 2
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
        return $stub;
38
    }
39
40
    private static function getHandlerMethod(Relation $relation):string
41
    {
42
        $methods = [];
43
        $methods[$relation instanceof BelongsTo] = 'BelongsTo'; //DONE
44
        $methods[$relation instanceof MorphTo] = 'MorphTo'; //DONE
45
        $methods[$relation instanceof BelongsToMany] = 'BelongsToMany'; //DONE
46
        $methods[$relation instanceof MorphToMany] = 'MorphToMany'; // DONE
47
        $methods[$relation instanceof HasOne] = 'HasOne';
48
        $methods[$relation instanceof HasMany] = 'HasMany';
49
        $methods[$relation instanceof HasManyThrough] = 'HasManyThrough'; //DONE
50
        $methods[$relation instanceof MorphMany] = 'MorphMany';
51
        $methods[$relation instanceof MorphOne] = 'MorphOne';
52
53
        return $methods[true];
54
    }
55
56
    /**
57
     * @param  string              $name
58
     * @param  Relation            $relation
59
     * @param  string              $cacheKey
60
     * @return AssociationStubMonomorphic
61
     */
62
    protected static function handleBelongsTo(string $name, Relation $relation, $cacheKey = 'BelongsTo'): AssociationStubMonomorphic
63
    {
64
        $stub = new AssociationStubMonomorphic();
65
        $keyChain = self::getKeyChain($relation, $cacheKey);
66
        $stub->setRelationName($name);
67
        $stub->setThroughFieldChain($keyChain);
68
        $stub->setKeyField($keyChain[0]);
69
        $stub->setForeignField($keyChain[1]);
70
        $stub->setTargType(get_class($relation->getRelated()));
71
        $stub->setMultiplicity(AssociationStubRelationType::ONE());
72
        return $stub;
73
    }
74
75
    /**
76
     * @param  string              $name
77
     * @param  Relation            $relation
78
     * @param  string              $cacheKey
79
     * @return AssociationStubPolymorphic
80
     */
81
    protected static function handleMorphTo(string $name, Relation $relation, $cacheKey = 'MorphTo'): AssociationStubPolymorphic
82
    {
83
        $stub = new AssociationStubPolymorphic();
84
        $keyChain = self::getKeyChain($relation, $cacheKey);
85
        $stub->setRelationName($name);
86
        $stub->setThroughFieldChain($keyChain);
87
        $stub->setKeyField($keyChain[2] ?: $relation->getRelated()->getKeyName());
88
        $stub->setForeignField($keyChain[2]);
89
        $stub->setMultiplicity(AssociationStubRelationType::ONE());
90
        $stub->setTargType(null);
91
        $stub->setMorphType($keyChain[1]);
92
        return $stub;
93
    }
94
95
96
    /**
97
     * @param  string              $name
98
     * @param  Relation            $relation
99
     * @param  string              $cacheKey
100
     * @return AssociationStubMonomorphic
101
     */
102
    protected static function handleBelongsToMany(string $name, Relation $relation, $cacheKey = 'BelongsToMany'): AssociationStubMonomorphic
103
    {
104
        $stub = new AssociationStubMonomorphic();
105
        $keyChain = self::getKeyChain($relation, $cacheKey);
106
        $stub->setRelationName($name);
107
        $stub->setThroughFieldChain($keyChain);
108
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
109
        $stub->setTargType(get_class($relation->getRelated()));
110
        $stub->setKeyField($keyChain[0]);
111
        $stub->setForeignField($keyChain[3]);
112
        return $stub;
113
    }
114
    /**
115
     * @param  string              $name
116
     * @param  Relation            $relation
117
     * @param  string              $cacheKey
118
     * @return AssociationStubMonomorphic
119
     */
120
    protected static function handleHasManyThrough(string $name, Relation $relation, $cacheKey = 'HasManyThrough'): AssociationStubMonomorphic
121
    {
122
        $keyChain = self::getKeyChain($relation, $cacheKey);
123
        $stub = new AssociationStubMonomorphic();
124
        $stub->setRelationName($name);
125
        $stub->setThroughFieldChain($keyChain);
126
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
127
        $stub->setTargType(get_class($relation->getRelated()));
128
        $stub->setKeyField($keyChain[0]);
129
        $stub->setForeignField($keyChain[3]);
130
        return $stub;
131
    }
132
133
    /**
134
     * @param  string              $name
135
     * @param  Relation            $relation
136
     * @param  string              $cacheKey
137
     * @return AssociationStubPolymorphic
138
     */
139
    protected static function handleMorphToMany(string $name, Relation $relation, $cacheKey = 'MorphToMany'): AssociationStubPolymorphic
140
    {
141
        //return self::handleBelongsToMany($name,$relation);
142
        //TODO: investigate if this could be treated as a BelongsToMany Or more importantly a Monomorphic as we know both sides
143
        $inverse = self::getKeyChain($relation, "inverse")[0];
144
        $stub = new AssociationStubPolymorphic();
145
        $keyChain = self::getKeyChain($relation, $cacheKey);
146
        $stub->setRelationName($name);
147
        $stub->setThroughFieldChain($keyChain);
148
        $stub->setKeyField($keyChain[3]);
149
        $stub->setForeignField($inverse ? null : $keyChain[1]);
150
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
151
        $stub->setMorphType($keyChain[2]);
152
        $stub->setTargType($inverse ? null : get_class($relation->getRelated()));
153
        return $stub;
154
    }
155
156
    /**
157
     * @param string $name
158
     * @param Relation $relation
159
     * @param string $cacheKey
160
     * @return AssociationStubMonomorphic
161
     */
162
    protected static function handleHasOne(string $name, Relation $relation, $cacheKey = 'HasOneOrMany'): AssociationStubMonomorphic
163
    {
164
        $stub = new AssociationStubMonomorphic();
165
        $keyChain = self::getKeyChain($relation, $cacheKey);
166
        $stub->setRelationName($name);
167
        $stub->setThroughFieldChain($keyChain);
168
        $stub->setKeyField($keyChain[0]);
169
        $stub->setForeignField($keyChain[1]);
170
        $stub->setTargType(get_class($relation->getRelated()));
171
        $stub->setMultiplicity(AssociationStubRelationType::NULL_ONE());
172
        return $stub;
173
    }
174
175
    /**
176
     * @param string $name
177
     * @param Relation $relation
178
     * @param string $cacheKey
179
     * @return AssociationStubMonomorphic
180
     */
181
    protected static function handleHasMany(string $name, Relation $relation, $cacheKey = 'HasOneOrMany'): AssociationStubMonomorphic
182
    {
183
        $stub = new AssociationStubMonomorphic();
184
        $keyChain = self::getKeyChain($relation, $cacheKey);
185
        $stub->setRelationName($name);
186
        $stub->setThroughFieldChain($keyChain);
187
        $stub->setKeyField($keyChain[0]);
188
        $stub->setForeignField($keyChain[1]);
189
        $stub->setTargType(get_class($relation->getRelated()));
190
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
191
        return $stub;
192
    }
193
194
    /**
195
     * @param  string                     $name
196
     * @param  Relation                   $relation
197
     * @param  string                     $cacheKey
198
     * @return AssociationStubPolymorphic
199
     */
200
    protected static function handleMorphOne(string $name, Relation $relation, $cacheKey = 'MorphOneOrMany'):AssociationStubPolymorphic
201
    {
202
        $stub = new AssociationStubPolymorphic();
203
        $keyChain = self::getKeyChain($relation, $cacheKey);
204
        $stub->setRelationName($name);
205
        $stub->setThroughFieldChain($keyChain);
206
        $stub->setKeyField($keyChain[0]);
207
        $stub->setForeignField($keyChain[2]);
208
        $stub->setTargType(get_class($relation->getRelated()));
209
        $stub->setMorphType($keyChain[1]);
210
        $stub->setMultiplicity(AssociationStubRelationType::NULL_ONE());
211
        return $stub;
212
    }
213
214
    /**
215
     * @param  string                     $name
216
     * @param  Relation                   $relation
217
     * @param  string                     $cacheKey
218
     * @return AssociationStubPolymorphic
219
     */
220
    protected static function handleMorphMany(string $name, Relation $relation, $cacheKey = 'MorphOneOrMany'): AssociationStubPolymorphic
221
    {
222
        $stub = new AssociationStubPolymorphic();
223
        $keyChain = self::getKeyChain($relation, $cacheKey);
224
        $stub->setRelationName($name);
225
        $stub->setThroughFieldChain($keyChain);
226
        $stub->setKeyField($keyChain[0]);
227
        $stub->setMorphType($keyChain[1]);
228
        $stub->setForeignField($keyChain[2]);
229
        $stub->setMultiplicity(AssociationStubRelationType::MANY());
230
        $stub->setTargType(get_class($relation->getRelated()));
231
        return $stub;
232
    }
233
234
    /**
235
     * @param Relation $relation
236
     * @param string $cacheKey
237
     * @return array
238
     */
239
    private static function getKeyChain(Relation $relation, string $cacheKey) : array
240
    {
241
        $fields = self::$fieldOrderCache[$cacheKey];
242
        $getter = function () use ($fields) {
243
            $carry = [];
244
            foreach ($fields as $item) {
245
                $v = $this->{$item};
246
                if (null == $v && 'ownerKey' == $item) {
247
                    $carry[] = null;
248
                    continue;
249
                }
250
                //TODO: investigate if this is needed can we use quailifed keys?
251
                $segments = explode('.', strval($this->{$item}));
252
                $carry[] = end($segments);
253
            }
254
            return $carry;
255
        };
256
        return call_user_func($getter->bindTo($relation, Relation::class));
257
    }
258
259
    private static $fieldOrderCache = [
260
        'BelongsTo' => ['foreignKey', 'ownerKey'],
261
        'BelongsToMany' => ['parentKey','foreignPivotKey','relatedPivotKey','relatedKey'],
262
        'HasOneOrMany' => ['localKey', 'foreignKey' ],
263
        'HasManyThrough' => ['localKey', 'firstKey', 'secondLocalKey', 'secondKey'],
264
        'MorphToMany' => ['parentKey','foreignPivotKey','morphType', 'relatedPivotKey','relatedKey'],
265
        'MorphTo' => ['foreignKey', 'morphType', 'ownerKey'],
266
        'MorphOneOrMany' => ['foreignKey', 'morphType', 'localKey'],
267
        'inverse' => ['inverse'], // TODO: currently used to get inverse, should be removed when morphtomany is fixed
268
    ];
269
}
270