Test Failed
Pull Request — master (#106)
by Alex
03:00
created

calculateRoundTripRelationsGenForwardReverse()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 34
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 28
nc 1
nop 6
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Models;
4
5
use Illuminate\Database\Eloquent\Model;
6
7
class MetadataRelationHolder
8
{
9
    protected $multConstraints = [ '0..1' => ['1'], '1' => ['0..1', '*'], '*' => ['1', '*']];
10
    protected $relations = [];
11
12
    public function __construct()
13
    {
14
    }
15
16
    /**
17
     * Add model's relationships to holder
18
     *
19
     * @param Model $model
20
     */
21
    public function addModel(Model $model)
22
    {
23
        if (!in_array('AlgoWeb\\PODataLaravel\\Models\\MetadataTrait', class_uses($model))) {
24
            $msg = 'Supplied model does not use MetadataTrait';
25
            throw new \InvalidArgumentException($msg);
26
        }
27
        $className = get_class($model);
28
        if (array_key_exists($className, $this->relations)) {
29
            $msg = $className.' already added';
30
            throw new \InvalidArgumentException($msg);
31
        }
32
        $this->relations[$className] = $model->getRelationships();
33
    }
34
35
    public function getRelationsByKey($className, $keyName)
36
    {
37
        $this->checkClassExists($className);
38
39
        $rels = $this->relations[$className];
40
        if (!array_key_exists($keyName, $rels)) {
41
            $msg = 'Key ' . $keyName . ' not registered on ' . $className;
42
            throw new \InvalidArgumentException($msg);
43
        }
44
45
        $result = [];
46
        $payload = $rels[$keyName];
47
        $principalType = $className;
48
        foreach ($payload as $dependentType => $targDeets) {
49
            if (!array_key_exists($dependentType, $this->relations)) {
50
                continue;
51
            }
52
            // if principal and ostensible dependent type are equal, drop through to specific handler
53
            // at moment, this is only for morphTo relations - morphedByMany doesn't cause this
54
            if ($principalType === $dependentType) {
55
                $morphToLines = $this->getMorphToRelations($principalType, $targDeets, $keyName);
56
                foreach ($morphToLines as $morph) {
57
                    if (!in_array($morph, $result)) {
58
                        $result[] = $morph;
59
                    }
60
                }
61
                continue;
62
            }
63
64
            $foreign = $this->relations[$dependentType];
65
66
            foreach ($targDeets as $principalProperty => $rawDeets) {
67
                $targKey = $rawDeets['local'];
68
                $principalMult = $rawDeets['multiplicity'];
69
                $principalProperty = $rawDeets['property'];
70
                if (!array_key_exists($targKey, $foreign)) {
71
                    continue;
72
                }
73
                $foreignDeets = $foreign[$targKey];
74
                foreach ($foreignDeets as $foreignType => $raw) {
75
                    if (!array_key_exists($foreignType, $this->relations)) {
76
                        continue;
77
                    }
78
                    $result = $this->generateRoundTripRelations(
79
                        $principalType,
80
                        $keyName,
81
                        $raw,
82
                        $principalMult,
83
                        $principalProperty,
84
                        $dependentType,
85
                        $result
86
                    );
87
                }
88
            }
89
        }
90
        return $result;
91
    }
92
93
    public function getRelationsByClass($className)
94
    {
95
        $this->checkClassExists($className);
96
97
        $rels = $this->relations[$className];
98
        $keys = array_keys($rels);
99
        $results = [];
100
        foreach ($keys as $key) {
101
            $lines = $this->getRelationsByKey($className, $key);
102
            foreach ($lines as $line) {
103
                if (!in_array($line, $results)) {
104
                    $results[] = $line;
105
                }
106
            }
107
        }
108
109
        return $results;
110
    }
111
112
    private function getMorphToRelations($principalType, $targDeets, $keyName)
113
    {
114
        $result = [];
115
        $deetKeys = array_keys($targDeets);
116
        $principalProperty = $deetKeys[0];
117
        $principalDeets = $targDeets[$principalProperty];
118
        $principalMult = $principalDeets['multiplicity'];
119
120
        foreach ($this->relations as $dependentType => $dependentDeets) {
121
            foreach ($dependentDeets as $targKey => $rawDeets) {
122
                foreach ($rawDeets as $targType => $interDeets) {
123
                    if ($targType != $principalType) {
124
                        continue;
125
                    }
126
                    $raw = $interDeets;
127
                    $result = $this->generateRoundTripRelations(
128
                        $principalType,
129
                        $keyName,
130
                        $raw,
131
                        $principalMult,
132
                        $principalProperty,
133
                        $dependentType,
134
                        $result
135
                    );
136
                }
137
            }
138
        }
139
        return $result;
140
    }
141
142
    /**
143
     * @param $principalType
144
     * @param $principalMult
145
     * @param $principalProperty
146
     * @param $dependentType
147
     * @param $dependentMult
148
     * @param $dependentProperty
149
     * @return array[]
150
     */
151
    private function calculateRoundTripRelationsGenForwardReverse(
152
        $principalType,
153
        $principalMult,
154
        $principalProperty,
155
        $dependentType,
156
        $dependentMult,
157
        $dependentProperty
158
    ) {
159
        assert(
160
            in_array($dependentMult, $this->multConstraints[$principalMult]),
161
            'Cannot pair multiplicities ' . $dependentMult . ' and ' . $principalMult
162
        );
163
        assert(
164
            in_array($principalMult, $this->multConstraints[$dependentMult]),
165
            'Cannot pair multiplicities ' . $principalMult . ' and ' . $dependentMult
166
        );
167
        $forward = [
168
            'principalType' => $principalType,
169
            'principalMult' => $dependentMult,
170
            'principalProp' => $principalProperty,
171
            'dependentType' => $dependentType,
172
            'dependentMult' => $principalMult,
173
            'dependentProp' => $dependentProperty
174
        ];
175
        $reverse = [
176
            'principalType' => $dependentType,
177
            'principalMult' => $principalMult,
178
            'principalProp' => $dependentProperty,
179
            'dependentType' => $principalType,
180
            'dependentMult' => $dependentMult,
181
            'dependentProp' => $principalProperty
182
        ];
183
        return [$forward, $reverse];
184
    }
185
186
    /**
187
     * @param $className
188
     */
189
    protected function checkClassExists($className)
190
    {
191
        if (!array_key_exists($className, $this->relations)) {
192
            $msg = $className . ' does not exist in holder';
193
            throw new \InvalidArgumentException($msg);
194
        }
195
    }
196
197
    /**
198
     * Reset stored relations
199
     */
200
    public function reset()
201
    {
202
        $this->relations = [];
203
    }
204
205
    /**
206
     * @param $principalType
207
     * @param $keyName
208
     * @param $raw
209
     * @param $principalMult
210
     * @param $principalProperty
211
     * @param $dependentType
212
     * @param $result
213
     * @return array
214
     */
215
    private function generateRoundTripRelations(
216
        $principalType,
217
        $keyName,
218
        $raw,
219
        $principalMult,
220
        $principalProperty,
221
        $dependentType,
222
        $result
223
    ) {
224
        foreach ($raw as $dependentProperty => $dependentPayload) {
225
            if ($keyName !== $dependentPayload['local']) {
226
                continue;
227
            }
228
            $dependentMult = $dependentPayload['multiplicity'];
229
            list($forward, $reverse) = $this->calculateRoundTripRelationsGenForwardReverse(
230
                $principalType,
231
                $principalMult,
232
                $principalProperty,
233
                $dependentType,
234
                $dependentMult,
235
                $dependentProperty
236
            );
237
            if (!in_array($forward, $result)) {
238
                // add forward relation
239
                $result[] = $forward;
240
            }
241
            if (!in_array($reverse, $result)) {
242
                // add reverse relation
243
                $result[] = $reverse;
244
            }
245
        }
246
        return $result;
247
    }
248
}
249