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)) { |
|
|
|
|
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) { |
|
|
|
|
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)) { |
|
|
|
|
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) { |
|
|
|
|
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
|
|
|
|
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.