1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Mado\QueryBundle\Component\Meta; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* @since Class available since Release 2.1.0 |
7
|
|
|
*/ |
8
|
|
|
class JsonPathFinder |
9
|
|
|
{ |
10
|
|
|
const INDEX_ENTITY_PARENT = 0; |
11
|
|
|
|
12
|
|
|
const INDEX_FK_RELATION_NAME = 1; |
13
|
|
|
|
14
|
|
|
const INDEX_ENTITY_FIRST_CHILD = 2; |
15
|
|
|
|
16
|
|
|
private $map; |
17
|
|
|
|
18
|
|
|
private $entity; |
19
|
|
|
|
20
|
|
|
private $entitiesPath = []; |
21
|
|
|
|
22
|
|
|
private $wrongPath = []; |
23
|
|
|
|
24
|
|
|
private $mapper; |
25
|
|
|
|
26
|
|
|
private $appendRootEntityToSubject; |
27
|
|
|
|
28
|
|
|
private $incrementSubject; |
29
|
|
|
|
30
|
|
|
private static $indeToDescriptionMap = [ |
31
|
|
|
self::INDEX_ENTITY_PARENT => 'parent', |
32
|
|
|
self::INDEX_FK_RELATION_NAME => 'relation', |
33
|
|
|
self::INDEX_ENTITY_FIRST_CHILD => 'first child', |
34
|
|
|
]; |
35
|
|
|
|
36
|
|
|
public function __construct( |
37
|
|
|
DataMapper $mapper |
38
|
|
|
) { |
39
|
|
|
$this->mapper = $mapper; |
40
|
|
|
|
41
|
|
|
$this->appendRootEntityToSubject = function($subject, $rootEntity) { |
42
|
|
|
$subject[] = $rootEntity; |
43
|
|
|
return $subject; |
44
|
|
|
}; |
45
|
|
|
|
46
|
|
|
$this->incrementSubject = function($subject) { |
47
|
|
|
return ++$subject; |
48
|
|
|
}; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
public function setEntity(string $entity) |
52
|
|
|
{ |
53
|
|
|
$this->entity = $entity; |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
public function getFirstParentOf(string $innerEntity) |
57
|
|
|
{ |
58
|
|
|
$this->getMap(); |
59
|
|
|
|
60
|
|
|
return $this->keep( |
61
|
|
|
self::INDEX_ENTITY_PARENT, |
62
|
|
|
$innerEntity |
63
|
|
|
); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
public function getFirstChildOf(string $innerEntity) |
67
|
|
|
{ |
68
|
|
|
return $this->keep( |
69
|
|
|
self::INDEX_ENTITY_FIRST_CHILD, |
70
|
|
|
$innerEntity |
71
|
|
|
); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
public function getSourceRelation(string $innerEntity) |
75
|
|
|
{ |
76
|
|
|
return $this->keep( |
77
|
|
|
self::INDEX_FK_RELATION_NAME, |
78
|
|
|
$innerEntity |
79
|
|
|
); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
public function clearMap(string $innerEntity) |
83
|
|
|
{ |
84
|
|
|
if (in_array($this->entity, $this->listOfParentsOf($innerEntity))) { |
85
|
|
|
foreach ($this->map as $rootEntity => $meta) { |
86
|
|
|
if ($this->entity != $rootEntity) { |
87
|
|
|
unset($this->map[$rootEntity]); |
88
|
|
|
} |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
public function getPathTo(string $innerEntity = '') |
94
|
|
|
{ |
95
|
|
|
$this->entitiesPath[] = $innerEntity; |
96
|
|
|
|
97
|
|
|
$path = $this->getSourceRelation($innerEntity); |
98
|
|
|
|
99
|
|
|
if ($this->numberOfRelationsToEntity($innerEntity) != 1) { |
100
|
|
|
$this->clearMap($innerEntity); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
if ($this->entity != $this->getFirstParentOf($innerEntity)) { |
104
|
|
|
if (!($relation = $this->getFirstParentOf($innerEntity))) { |
105
|
|
|
throw new Exceptions\UnreachablePathException(var_export([ |
106
|
|
|
'innerEntity' => $innerEntity, |
107
|
|
|
'relation' => $relation, |
108
|
|
|
], true)); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
return $this->getPathTo($relation) . '.' . $path; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $path; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
public function setQueryStartEntity(string $startEntity) |
118
|
|
|
{ |
119
|
|
|
$this->setEntity($startEntity); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
public function getPathToEntity(string $entityToReach) |
123
|
|
|
{ |
124
|
|
|
foreach ($this->getMap() as $rootEntity => $meta) { |
125
|
|
|
if (in_array($rootEntity, $this->wrongPath)) { |
126
|
|
|
unset($this->map[$rootEntity]); |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
return '_embedded.' . $this->getPathTo($entityToReach); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
public function keep($val, $innerEntity) |
134
|
|
|
{ |
135
|
|
|
foreach ($this->getMap() as $rootEntity => $meta) { |
136
|
|
|
foreach ($meta['relations'] as $name => $entity) { |
137
|
|
|
if (self::INDEX_ENTITY_FIRST_CHILD == $val) { |
138
|
|
|
return $entity; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
if ($entity == $innerEntity) { |
142
|
|
|
$return = [ |
143
|
|
|
self::INDEX_ENTITY_PARENT => $rootEntity, |
144
|
|
|
self::INDEX_FK_RELATION_NAME => $name, |
145
|
|
|
][$val]; |
146
|
|
|
|
147
|
|
|
return $return; |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
throw new Exceptions\UnespectedValueException(var_export([ |
153
|
|
|
'val' => self::$indeToDescriptionMap[$val], |
154
|
|
|
'innerEntity' => $innerEntity, |
155
|
|
|
'map' => $this->getMap(), |
156
|
|
|
], true)); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
public function numberOfRelationsToEntity(string $entityToReach) |
160
|
|
|
{ |
161
|
|
|
return $this->mapTargetRelations( |
162
|
|
|
$this->incrementSubject, |
163
|
|
|
$subject = 0, |
164
|
|
|
$entityToReach |
165
|
|
|
); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
public function listOfParentsOf(string $entityToReach) |
169
|
|
|
{ |
170
|
|
|
return $this->mapTargetRelations( |
171
|
|
|
$this->appendRootEntityToSubject, |
172
|
|
|
$subject = [], |
173
|
|
|
$entityToReach |
174
|
|
|
); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
public function getEntitiesPath() |
178
|
|
|
{ |
179
|
|
|
if (!$this->entitiesPath) { |
|
|
|
|
180
|
|
|
throw new Exceptions\UndefinedPathException( |
181
|
|
|
'Any path was requested' |
182
|
|
|
); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
return $this->entitiesPath; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
public function removeStep($parentToSkip) |
189
|
|
|
{ |
190
|
|
|
$this->wrongPath[] = $parentToSkip; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
public function getHashKeyForDestination(string $destination) |
194
|
|
|
{ |
195
|
|
|
return md5($this->entity . $destination); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
private function getMap() |
199
|
|
|
{ |
200
|
|
|
if (!$this->map) { |
201
|
|
|
$this->map = $this->mapper->getMap(); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return $this->map; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
public function addEntity(array $parents, $rootEntity) : array |
208
|
|
|
{ |
209
|
|
|
$parents[] = $rootEntity; |
210
|
|
|
|
211
|
|
|
return $parents; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
public function mapTargetRelations( |
215
|
|
|
callable $action, |
216
|
|
|
$subject, |
217
|
|
|
string $entityToReach |
218
|
|
|
) { |
219
|
|
|
foreach ($this->getMap() as $rootEntity => $meta) { |
220
|
|
|
foreach ($meta['relations'] as $name => $relationEntity) { |
221
|
|
|
if ($relationEntity == $entityToReach) { |
222
|
|
|
$subject = $action($subject, $rootEntity); |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
return $subject; |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.