Test Failed
Pull Request — master (#106)
by Alex
07:07
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 View Code Duplication
        if (!array_key_exists($className, $this->relations)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
38
            $msg = $className . ' does not exist in holder';
39
            throw new \InvalidArgumentException($msg);
40
        }
41
42
        $rels = $this->relations[$className];
43
        if (!array_key_exists($keyName, $rels)) {
44
            $msg = 'Key ' . $keyName . ' not registered on ' . $className;
45
            throw new \InvalidArgumentException($msg);
46
        }
47
48
        $result = [];
49
        $payload = $rels[$keyName];
50
        $principalType = $className;
51
        foreach ($payload as $dependentType => $targDeets) {
52
            if (!array_key_exists($dependentType, $this->relations)) {
53
                continue;
54
            }
55
            // if principal and ostensible dependent type are equal, drop through to specific handler
56
            // at moment, this is only for morphTo relations - morphedByMany doesn't cause this
57
            if ($principalType === $dependentType) {
58
                $morphToLines = $this->getMorphToRelations($principalType, $targDeets, $keyName);
59
                foreach ($morphToLines as $morph) {
60
                    if (!in_array($morph, $result)) {
61
                        $result[] = $morph;
62
                    }
63
                }
64
                continue;
65
            }
66
67
            $foreign = $this->relations[$dependentType];
68
69
            foreach ($targDeets as $principalProperty => $rawDeets) {
70
                $targKey = $rawDeets['local'];
71
                $principalMult = $rawDeets['multiplicity'];
72
                $principalProperty = $rawDeets['property'];
73
                if (!array_key_exists($targKey, $foreign)) {
74
                    continue;
75
                }
76
                $foreignDeets = $foreign[$targKey];
77
                foreach ($foreignDeets as $foreignType => $raw) {
78
                    if (!array_key_exists($foreignType, $this->relations)) {
79
                        continue;
80
                    }
81 View Code Duplication
                    foreach ($raw as $dependentProperty => $dependentPayload) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
82
                        if ($keyName == $dependentPayload['local']) {
83
                            $dependentMult = $dependentPayload['multiplicity'];
84
                            // generate forward and reverse relations
85
                            list($forward, $reverse) = $this->calculateRoundTripRelationsGenForwardReverse(
86
                                $principalType,
87
                                $principalMult,
88
                                $principalProperty,
89
                                $dependentType,
90
                                $dependentMult,
91
                                $dependentProperty
92
                            );
93
                            if (!in_array($forward, $result)) {
94
                                // add forward relation
95
                                $result[] = $forward;
96
                            }
97
                            if (!in_array($reverse, $result)) {
98
                                // add reverse relation
99
                                $result[] = $reverse;
100
                            }
101
                        }
102
                    }
103
                }
104
            }
105
        }
106
        return $result;
107
    }
108
109
    public function getRelationsByClass($className)
110
    {
111 View Code Duplication
        if (!array_key_exists($className, $this->relations)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
112
            $msg = $className . ' does not exist in holder';
113
            throw new \InvalidArgumentException($msg);
114
        }
115
116
        $rels = $this->relations[$className];
117
        $keys = array_keys($rels);
118
        $results = [];
119
        foreach ($keys as $key) {
120
            $lines = $this->getRelationsByKey($className, $key);
121
            foreach ($lines as $line) {
122
                if (!in_array($line, $results)) {
123
                    $results[] = $line;
124
                }
125
            }
126
        }
127
128
        return $results;
129
    }
130
131
    private function getMorphToRelations($principalType, $targDeets, $keyName)
132
    {
133
        $result = [];
134
        $deetKeys = array_keys($targDeets);
135
        $principalProperty = $deetKeys[0];
136
        $principalDeets = $targDeets[$principalProperty];
137
        $principalMult = $principalDeets['multiplicity'];
138
139
        foreach ($this->relations as $dependentType => $dependentDeets) {
140
            foreach ($dependentDeets as $targKey => $rawDeets) {
141
                foreach ($rawDeets as $targType => $interDeets) {
142
                    if ($targType != $principalType) {
143
                        continue;
144
                    }
145 View Code Duplication
                    foreach ($interDeets as $dependentProperty => $finalDeets) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
146
                        if ($keyName !== $finalDeets['local']) {
147
                            continue;
148
                        }
149
                        $dependentMult = $finalDeets['multiplicity'];
150
                        list($forward, $reverse) = $this->calculateRoundTripRelationsGenForwardReverse(
151
                            $principalType,
152
                            $principalMult,
153
                            $principalProperty,
154
                            $dependentType,
155
                            $dependentMult,
156
                            $dependentProperty
157
                        );
158
                        if (!in_array($forward, $result)) {
159
                            // add forward relation
160
                            $result[] = $forward;
161
                        }
162
                        if (!in_array($reverse, $result)) {
163
                            // add reverse relation
164
                            $result[] = $reverse;
165
                        }
166
                    }
167
                }
168
            }
169
        }
170
        return $result;
171
    }
172
173
    /**
174
     * @param $principalType
175
     * @param $principalMult
176
     * @param $principalProperty
177
     * @param $dependentType
178
     * @param $dependentMult
179
     * @param $dependentProperty
180
     * @return array[]
181
     */
182
    private function calculateRoundTripRelationsGenForwardReverse(
183
        $principalType,
184
        $principalMult,
185
        $principalProperty,
186
        $dependentType,
187
        $dependentMult,
188
        $dependentProperty
189
    ) {
190
        assert(
191
            in_array($dependentMult, $this->multConstraints[$principalMult]),
192
            'Cannot pair multiplicities ' . $dependentMult . ' and ' . $principalMult
193
        );
194
        assert(
195
            in_array($principalMult, $this->multConstraints[$dependentMult]),
196
            'Cannot pair multiplicities ' . $principalMult . ' and ' . $dependentMult
197
        );
198
        $forward = [
199
            'principalType' => $principalType,
200
            'principalMult' => $dependentMult,
201
            'principalProp' => $principalProperty,
202
            'dependentType' => $dependentType,
203
            'dependentMult' => $principalMult,
204
            'dependentProp' => $dependentProperty
205
        ];
206
        $reverse = [
207
            'principalType' => $dependentType,
208
            'principalMult' => $principalMult,
209
            'principalProp' => $dependentProperty,
210
            'dependentType' => $principalType,
211
            'dependentMult' => $dependentMult,
212
            'dependentProp' => $principalProperty
213
        ];
214
        return [$forward, $reverse];
215
    }
216
}
217