1 | <?php |
||||
2 | /** |
||||
3 | * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/ |
||||
4 | * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/ |
||||
5 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public License |
||||
6 | */ |
||||
7 | namespace midgard\portable\api; |
||||
8 | |||||
9 | use midgard\portable\storage\connection; |
||||
10 | use midgard\portable\storage\objectmanager; |
||||
11 | use midgard\portable\storage\collection; |
||||
12 | use midgard\portable\storage\interfaces\metadata as metadata_interface; |
||||
13 | use midgard\portable\mgdschema\translator; |
||||
14 | use midgard\portable\api\error\exception; |
||||
15 | use Doctrine\ORM\Query; |
||||
16 | use midgard_connection; |
||||
17 | use Doctrine\ORM\Proxy\Proxy; |
||||
18 | use Doctrine\ORM\QueryBuilder; |
||||
19 | use Doctrine\ORM\EntityNotFoundException; |
||||
20 | |||||
21 | /** |
||||
22 | * @property metadata $metadata |
||||
23 | */ |
||||
24 | abstract class mgdobject extends dbobject |
||||
25 | { |
||||
26 | protected $metadata; // compat with mgd behavior: If the schema has no metadata, the property is present anyway |
||||
27 | |||||
28 | public $action = ''; // <== does this need to do anything? |
||||
29 | |||||
30 | private array $collections = []; |
||||
31 | |||||
32 | /** |
||||
33 | * @param mixed $id ID or GUID |
||||
34 | */ |
||||
35 | 137 | public function __construct($id = null) |
|||
36 | { |
||||
37 | 137 | if ($id !== null) { |
|||
38 | 50 | if (is_int($id)) { |
|||
39 | 41 | $this->get_by_id($id); |
|||
40 | 14 | } elseif (is_string($id)) { |
|||
41 | 14 | $this->get_by_guid($id); |
|||
42 | } |
||||
43 | } |
||||
44 | } |
||||
45 | |||||
46 | 16 | private function get_collection(string $classname) : collection |
|||
47 | { |
||||
48 | 16 | if (!isset($this->collections[$classname])) { |
|||
49 | 16 | $this->collections[$classname] = new collection($classname); |
|||
50 | } |
||||
51 | 16 | return $this->collections[$classname]; |
|||
52 | } |
||||
53 | |||||
54 | 1 | public function __debugInfo() |
|||
55 | { |
||||
56 | 1 | $ret = parent::__debugInfo(); |
|||
57 | 1 | if (property_exists($this, 'metadata')) { |
|||
58 | 1 | $metadata = new \stdClass; |
|||
59 | 1 | foreach ($this->cm->getFieldNames() as $name) { |
|||
60 | 1 | if (str_contains($name, 'metadata_')) { |
|||
61 | 1 | $fieldname = str_replace('metadata_', '', $name); |
|||
62 | 1 | $metadata->$fieldname = $this->__get($name); |
|||
63 | } |
||||
64 | } |
||||
65 | 1 | $ret['metadata'] = $metadata; |
|||
66 | } |
||||
67 | |||||
68 | 1 | return $ret; |
|||
69 | } |
||||
70 | |||||
71 | 113 | public function __set($field, $value) |
|||
72 | { |
||||
73 | 113 | if ($field == 'guid') { |
|||
74 | 6 | return; |
|||
75 | } |
||||
76 | 113 | parent::__set($field, $value); |
|||
77 | } |
||||
78 | |||||
79 | 130 | public function __get($field) |
|||
80 | { |
||||
81 | 130 | if ( $field === 'metadata' |
|||
82 | 130 | && $this->metadata === null |
|||
83 | 130 | && $this instanceof metadata_interface) { |
|||
84 | 104 | $this->metadata = new metadata($this); |
|||
85 | } |
||||
86 | |||||
87 | 130 | return parent::__get($field); |
|||
88 | } |
||||
89 | |||||
90 | 1 | public function __call($method, $args) |
|||
91 | { |
||||
92 | 1 | if ($method === 'list') { |
|||
93 | 1 | return $this->_list(); |
|||
94 | } |
||||
95 | throw new \BadMethodCallException("Unknown method " . $method . " on " . get_class($this)); |
||||
96 | } |
||||
97 | |||||
98 | 4 | protected function load_parent(array $candidates) : ?dbobject |
|||
99 | { |
||||
100 | 4 | foreach ($candidates as $candidate) { |
|||
101 | 4 | if ( is_string($this->$candidate) |
|||
102 | 4 | && mgd_is_guid($this->$candidate)) { |
|||
103 | return \midgard_object_class::get_object_by_guid($this->$candidate); |
||||
104 | } |
||||
105 | 4 | if ($this->$candidate !== null) { |
|||
106 | //Proxies become stale if the object itself is detached, so we have to re-fetch |
||||
107 | 4 | if ( $this->$candidate instanceof Proxy |
|||
108 | 4 | && $this->$candidate->__isInitialized()) { |
|||
109 | try { |
||||
110 | 1 | $this->$candidate->get_by_id($this->$candidate->id); |
|||
111 | } catch (exception $e) { |
||||
112 | connection::log()->error('Failed to refresh parent from proxy: ' . $e->getMessage()); |
||||
113 | return null; |
||||
114 | } |
||||
115 | } |
||||
116 | 4 | return $this->$candidate; |
|||
117 | } |
||||
118 | } |
||||
119 | 1 | return null; |
|||
120 | } |
||||
121 | |||||
122 | 43 | public function get_by_id(int $id) : bool |
|||
123 | { |
||||
124 | 43 | $entity = connection::get_em()->find(get_class($this), $id); |
|||
125 | |||||
126 | 43 | if ($entity === null) { |
|||
127 | 3 | throw exception::not_exists(); |
|||
128 | } |
||||
129 | // Normally, the proxy should be automatically loaded when accessing e.g. $entity->guid, |
||||
130 | // because that triggers the proxy's magic __get method, which initializes. But since mgdobject |
||||
131 | // is the entity's (grand)parent class, PHP rules dictate that we have access to |
||||
132 | // its parents' protected variables. Hence, we need to load explicitly. |
||||
133 | 42 | if ($entity instanceof Proxy && !$entity->__isInitialized()) { |
|||
134 | try { |
||||
135 | 7 | $entity->__load(); |
|||
136 | 1 | } catch (EntityNotFoundException) { |
|||
137 | 1 | throw exception::object_purged(); |
|||
138 | } |
||||
139 | } |
||||
140 | 42 | if ($entity instanceof metadata_interface && $entity->{metadata_interface::DELETED_FIELD}) { |
|||
141 | // This can happen when the "deleted" entity is still in EM's identity map |
||||
142 | throw exception::object_deleted(); |
||||
143 | } |
||||
144 | |||||
145 | 42 | $this->populate_from_entity($entity); |
|||
146 | |||||
147 | 42 | connection::get_em()->detach($entity); |
|||
148 | 42 | exception::ok(); |
|||
149 | 42 | return true; |
|||
150 | } |
||||
151 | |||||
152 | 16 | public function get_by_guid(string $guid) : bool |
|||
153 | { |
||||
154 | 16 | if (!mgd_is_guid($guid)) { |
|||
155 | 1 | throw new \InvalidArgumentException("'$guid' is not a valid guid"); |
|||
156 | } |
||||
157 | 15 | $entity = connection::get_em()->getRepository(get_class($this))->findOneBy(['guid' => $guid]); |
|||
158 | 15 | if ($entity === null) { |
|||
159 | throw exception::not_exists(); |
||||
160 | } |
||||
161 | 15 | $this->populate_from_entity($entity); |
|||
162 | |||||
163 | 15 | connection::get_em()->detach($entity); |
|||
164 | 15 | exception::ok(); |
|||
165 | 15 | return true; |
|||
166 | } |
||||
167 | |||||
168 | 110 | public function create() : bool |
|||
169 | { |
||||
170 | 110 | $this->initialize(); |
|||
171 | |||||
172 | 110 | if ($this->cm->getIdentifierValues($this)) { |
|||
173 | 2 | exception::duplicate(); |
|||
174 | 2 | return false; |
|||
175 | } |
||||
176 | 110 | if ( !$this->is_unique() |
|||
177 | 110 | || !$this->check_parent()) { |
|||
178 | 2 | return false; |
|||
179 | } |
||||
180 | 110 | if (!$this->check_fields()) { |
|||
181 | 1 | return false; |
|||
182 | } |
||||
183 | try { |
||||
184 | 109 | $om = new objectmanager(connection::get_em()); |
|||
185 | 109 | $om->create($this); |
|||
186 | 1 | } catch (\Exception $e) { |
|||
187 | 1 | exception::internal($e); |
|||
188 | 1 | return false; |
|||
189 | } |
||||
190 | 109 | return (bool) $this->cm->getIdentifierValues($this); |
|||
191 | } |
||||
192 | |||||
193 | 16 | public function update() : bool |
|||
194 | { |
||||
195 | 16 | $this->initialize(); |
|||
196 | |||||
197 | 16 | if (!$this->cm->getIdentifierValues($this)) { |
|||
198 | 1 | midgard_connection::get_instance()->set_error(MGD_ERR_INTERNAL); |
|||
199 | 1 | return false; |
|||
200 | } |
||||
201 | 15 | if (!$this->check_fields()) { |
|||
202 | 2 | return false; |
|||
203 | } |
||||
204 | try { |
||||
205 | 13 | $om = new objectmanager(connection::get_em()); |
|||
206 | 13 | $om->update($this); |
|||
207 | } catch (\Exception $e) { |
||||
208 | exception::internal($e); |
||||
209 | return false; |
||||
210 | } |
||||
211 | |||||
212 | 13 | return true; |
|||
213 | } |
||||
214 | |||||
215 | /** |
||||
216 | * @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension, |
||||
217 | * so we might consider ignoring it, too |
||||
218 | */ |
||||
219 | 29 | public function delete(bool $check_dependencies = true) : bool |
|||
220 | { |
||||
221 | 29 | $this->initialize(); |
|||
222 | |||||
223 | 29 | if (!$this->cm->getIdentifierValues($this)) { |
|||
224 | 1 | exception::invalid_property_value(); |
|||
225 | 1 | return false; |
|||
226 | } |
||||
227 | 28 | if ( $check_dependencies |
|||
228 | 28 | && $this->has_dependents()) { |
|||
229 | 4 | exception::has_dependants(); |
|||
230 | 4 | return false; |
|||
231 | } |
||||
232 | 28 | if (!($this instanceof metadata_interface)) { |
|||
233 | 1 | exception::invalid_property_value(); |
|||
234 | 1 | return false; |
|||
235 | } |
||||
236 | 27 | if ($this->{metadata_interface::DELETED_FIELD}) { |
|||
237 | 1 | return true; |
|||
238 | } |
||||
239 | |||||
240 | try { |
||||
241 | 27 | $om = new objectmanager(connection::get_em()); |
|||
242 | 27 | $om->delete($this); |
|||
243 | } catch (\Exception $e) { |
||||
244 | exception::internal($e); |
||||
245 | return false; |
||||
246 | } |
||||
247 | |||||
248 | 27 | return true; |
|||
249 | } |
||||
250 | |||||
251 | 110 | private function is_unique() : bool |
|||
252 | { |
||||
253 | 110 | if (empty($this->cm->midgard['unique_fields'])) { |
|||
254 | 104 | return true; |
|||
255 | } |
||||
256 | |||||
257 | 8 | $qb = connection::get_em()->createQueryBuilder(); |
|||
258 | 8 | $qb->from(get_class($this), 'c'); |
|||
259 | 8 | $conditions = $qb->expr()->andX(); |
|||
260 | 8 | $parameters = []; |
|||
261 | 8 | if ($identifier = $this->cm->getIdentifierValues($this)) { |
|||
262 | $parameters = array_merge($parameters, $identifier); |
||||
263 | $field = key($identifier); |
||||
264 | $conditions->add($qb->expr()->neq('c.' . $field, ':' . $field)); |
||||
265 | } |
||||
266 | 8 | $found = false; |
|||
267 | 8 | foreach ($this->cm->midgard['unique_fields'] as $field) { |
|||
268 | 8 | if (empty($this->$field)) { |
|||
269 | //empty names automatically pass according to Midgard logic |
||||
270 | 2 | continue; |
|||
271 | } |
||||
272 | 7 | $conditions->add($qb->expr()->eq('c.' . $field, ':' . $field)); |
|||
273 | 7 | $parameters[$field] = $this->$field; |
|||
274 | 7 | $found = true; |
|||
275 | } |
||||
276 | |||||
277 | 8 | if (!$found) { |
|||
278 | 2 | return true; |
|||
279 | } |
||||
280 | |||||
281 | 7 | foreach (['upfield', 'parentfield'] as $candidate) { |
|||
282 | 7 | if (!empty($this->cm->midgard[$candidate])) { |
|||
283 | // TODO: This needs to be changed so that value is always numeric, since this is how midgard does it |
||||
284 | 6 | if ($this->{$this->cm->midgard[$candidate]} === null) { |
|||
285 | 6 | $conditions->add($qb->expr()->isNull('c.' . $this->cm->midgard[$candidate])); |
|||
286 | } else { |
||||
287 | 6 | $conditions->add($qb->expr()->eq('c.' . $this->cm->midgard[$candidate], ':' . $this->cm->midgard[$candidate])); |
|||
288 | 6 | $parameters[$this->cm->midgard[$candidate]] = $this->{$this->cm->midgard[$candidate]}; |
|||
289 | } |
||||
290 | 6 | break; |
|||
291 | } |
||||
292 | } |
||||
293 | |||||
294 | 7 | $qb->where($conditions) |
|||
295 | 7 | ->setParameters($parameters); |
|||
296 | |||||
297 | 7 | $qb->select("count(c)"); |
|||
298 | 7 | $count = (int) $qb->getQuery()->getSingleScalarResult(); |
|||
299 | |||||
300 | 7 | if ($count !== 0) { |
|||
301 | 1 | exception::object_name_exists(); |
|||
302 | 1 | return false; |
|||
303 | } |
||||
304 | 7 | return true; |
|||
305 | } |
||||
306 | |||||
307 | 110 | private function check_parent() : bool |
|||
308 | { |
||||
309 | 110 | if ( empty($this->cm->midgard['parentfield']) |
|||
310 | 110 | || empty($this->cm->midgard['parent'])) { |
|||
311 | 110 | return true; |
|||
312 | } |
||||
313 | |||||
314 | 8 | if (empty($this->{$this->cm->midgard['parentfield']})) { |
|||
315 | 1 | exception::object_no_parent(); |
|||
316 | 1 | return false; |
|||
317 | } |
||||
318 | 8 | return true; |
|||
319 | } |
||||
320 | |||||
321 | 110 | private function check_fields() : bool |
|||
322 | { |
||||
323 | 110 | foreach ($this->cm->fieldMappings as $name => $field) { |
|||
324 | 110 | if ( $field['midgard:midgard_type'] == translator::TYPE_GUID |
|||
325 | 110 | && !empty($this->$name) |
|||
326 | 110 | && !mgd_is_guid($this->$name)) { |
|||
327 | 2 | exception::invalid_property_value("'" . $name . "' property's value is not a guid."); |
|||
328 | 2 | return false; |
|||
329 | } |
||||
330 | } |
||||
331 | 109 | return $this->check_upfield(); |
|||
332 | } |
||||
333 | |||||
334 | 109 | private function check_upfield() : bool |
|||
335 | { |
||||
336 | 109 | $identifier = $this->cm->getIdentifierValues($this); |
|||
337 | 109 | if ( $identifier |
|||
0 ignored issues
–
show
|
|||||
338 | 109 | && !empty($this->cm->midgard['upfield']) |
|||
339 | 109 | && $this->__get($this->cm->midgard['upfield']) === reset($identifier) |
|||
340 | 109 | && $this->cm->getAssociationMapping($this->cm->midgard['upfield'])['targetEntity'] === $this->cm->getName()) { |
|||
341 | 1 | exception::tree_is_circular(); |
|||
342 | 1 | return false; |
|||
343 | } |
||||
344 | // @todo this should be recursive |
||||
345 | 109 | return true; |
|||
346 | } |
||||
347 | |||||
348 | public function is_in_parent_tree($root_id, $id) : bool |
||||
349 | { |
||||
350 | return false; |
||||
351 | } |
||||
352 | |||||
353 | public function is_in_tree($root_id, $id) : bool |
||||
354 | { |
||||
355 | return false; |
||||
356 | } |
||||
357 | |||||
358 | 35 | public function has_dependents() : bool |
|||
359 | { |
||||
360 | 35 | $this->initialize(); |
|||
361 | |||||
362 | 35 | $stat = false; |
|||
363 | |||||
364 | 35 | if (!empty($this->cm->midgard['upfield'])) { |
|||
365 | 30 | $identifier = $this->cm->getIdentifierValues($this); |
|||
366 | 30 | $qb = connection::get_em()->createQueryBuilder(); |
|||
367 | 30 | $qb->from(get_class($this), 'c') |
|||
368 | 30 | ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0') |
|||
369 | 30 | ->setParameter(0, (int) reset($identifier)) |
|||
370 | 30 | ->select("COUNT(c)"); |
|||
371 | 30 | $results = (int) $qb->getQuery()->getSingleScalarResult(); |
|||
372 | 30 | $stat = $results > 0; |
|||
373 | } |
||||
374 | |||||
375 | 35 | if ( !$stat |
|||
376 | 35 | && !empty($this->cm->midgard['childtypes'])) { |
|||
377 | 28 | foreach ($this->cm->midgard['childtypes'] as $typename => $parentfield) { |
|||
378 | 28 | $identifier = $this->cm->getIdentifierValues($this); |
|||
379 | 28 | $qb = connection::get_em()->createQueryBuilder(); |
|||
380 | 28 | $qb->from(connection::get_fqcn($typename), 'c') |
|||
381 | 28 | ->where('c.' . $parentfield . ' = ?0') |
|||
382 | 28 | ->setParameter(0, (int) reset($identifier)) |
|||
383 | 28 | ->select("COUNT(c)"); |
|||
384 | |||||
385 | 28 | $results = (int) $qb->getQuery()->getSingleScalarResult(); |
|||
386 | 28 | $stat = $results > 0; |
|||
387 | 28 | if ($stat) { |
|||
388 | 3 | break; |
|||
389 | } |
||||
390 | } |
||||
391 | } |
||||
392 | |||||
393 | 35 | return $stat; |
|||
394 | } |
||||
395 | |||||
396 | public function get_parent() |
||||
397 | { |
||||
398 | return null; |
||||
399 | } |
||||
400 | |||||
401 | /** |
||||
402 | * This function is called list() in Midgard, but that doesn't work in plain PHP |
||||
403 | */ |
||||
404 | 1 | private function _list() : array |
|||
405 | { |
||||
406 | 1 | $this->initialize(); |
|||
407 | |||||
408 | 1 | if (!empty($this->cm->midgard['upfield'])) { |
|||
409 | 1 | $identifier = $this->cm->getIdentifierValues($this); |
|||
410 | 1 | $qb = connection::get_em()->createQueryBuilder(); |
|||
411 | 1 | $qb->from(get_class($this), 'c') |
|||
412 | 1 | ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0') |
|||
413 | 1 | ->setParameter(0, (int) reset($identifier)) |
|||
414 | 1 | ->select("c"); |
|||
415 | 1 | return $qb->getQuery()->getResult(); |
|||
416 | } |
||||
417 | |||||
418 | return []; |
||||
419 | } |
||||
420 | |||||
421 | /** |
||||
422 | * This should return child objects, but only if they are of a different type |
||||
423 | * For all other input, an empty array is returned |
||||
424 | * (not implemented yet) |
||||
425 | */ |
||||
426 | public function list_children(string $classname) : array |
||||
427 | { |
||||
428 | return []; |
||||
429 | } |
||||
430 | |||||
431 | 1 | public function get_by_path(string $path) : bool |
|||
432 | { |
||||
433 | 1 | $parts = explode('/', trim($path, '/')); |
|||
434 | 1 | if (empty($parts)) { |
|||
435 | return false; |
||||
436 | } |
||||
437 | 1 | $this->initialize(); |
|||
438 | |||||
439 | 1 | if (count($this->cm->midgard['unique_fields']) != 1) { |
|||
440 | return false; |
||||
441 | } |
||||
442 | |||||
443 | 1 | $field = $this->cm->midgard['unique_fields'][0]; |
|||
444 | |||||
445 | 1 | if (!empty($this->cm->midgard['parent'])) { |
|||
446 | 1 | $parent_cm = connection::get_em()->getClassMetadata(connection::get_fqcn($this->cm->midgard['parent'])); |
|||
447 | 1 | $parentclass = $this->cm->fullyQualifiedClassName($this->cm->midgard['parent']); |
|||
448 | 1 | $parentfield = $parent_cm->midgard['upfield']; |
|||
0 ignored issues
–
show
|
|||||
449 | 1 | $upfield = $this->cm->midgard['parentfield']; |
|||
450 | 1 | } elseif (!empty($this->cm->midgard['upfield'])) { |
|||
451 | 1 | $parentclass = get_class($this); |
|||
452 | 1 | $upfield = $this->cm->midgard['upfield']; |
|||
453 | 1 | $parentfield = $upfield; |
|||
454 | } else { |
||||
455 | return false; |
||||
456 | } |
||||
457 | |||||
458 | 1 | $name = array_pop($parts); |
|||
459 | 1 | $up = 0; |
|||
460 | 1 | foreach ($parts as $part) { |
|||
461 | 1 | $qb = $this->get_uniquefield_query($parentclass, $field, $part, $parentfield, $up); |
|||
0 ignored issues
–
show
It seems like
$parentclass can also be of type null ; however, parameter $classname of midgard\portable\api\mgd...get_uniquefield_query() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
462 | 1 | $qb->select("c.id"); |
|||
463 | 1 | $up = (int) $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR); |
|||
464 | 1 | if ($up === 0) { |
|||
465 | 1 | return $this->reset(); |
|||
466 | } |
||||
467 | } |
||||
468 | |||||
469 | 1 | $qb = $this->get_uniquefield_query(get_class($this), $field, $name, $upfield, $up); |
|||
470 | 1 | $qb->select("c"); |
|||
471 | |||||
472 | 1 | $entity = $qb->getQuery()->getOneOrNullResult(); |
|||
473 | |||||
474 | 1 | if ($entity === null) { |
|||
475 | 1 | return $this->reset(); |
|||
476 | } |
||||
477 | 1 | $this->populate_from_entity($entity); |
|||
0 ignored issues
–
show
It seems like
$entity can also be of type integer ; however, parameter $entity of midgard\portable\api\dbo...:populate_from_entity() does only seem to accept midgard\portable\api\dbobject , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
478 | |||||
479 | 1 | return true; |
|||
480 | } |
||||
481 | |||||
482 | 1 | private function reset() : bool |
|||
483 | { |
||||
484 | 1 | exception::not_exists(); |
|||
485 | 1 | foreach ($this->cm->getIdentifierFieldNames() as $field) { |
|||
486 | 1 | $this->$field = 0; |
|||
487 | } |
||||
488 | 1 | $this->set_guid(''); |
|||
489 | 1 | return false; |
|||
490 | } |
||||
491 | |||||
492 | 1 | protected function get_uniquefield_query(string $classname, string $field, string $part, string $upfield, int $up) : QueryBuilder |
|||
493 | { |
||||
494 | 1 | $qb = connection::get_em()->createQueryBuilder(); |
|||
495 | 1 | $qb->from($classname, 'c'); |
|||
496 | 1 | $conditions = $qb->expr()->andX(); |
|||
497 | 1 | $conditions->add($qb->expr()->eq('c.' . $field, ':' . $field)); |
|||
498 | 1 | $parameters = [ |
|||
499 | 1 | $field => $part |
|||
500 | 1 | ]; |
|||
501 | |||||
502 | 1 | if (empty($up)) { |
|||
503 | // If the database was created by Midgard, it might contain 0 instead of NULL, so... |
||||
504 | 1 | $empty_conditions = $qb->expr()->orX() |
|||
505 | 1 | ->add($qb->expr()->isNull('c.' . $upfield)) |
|||
506 | 1 | ->add($qb->expr()->eq('c.' . $upfield, '0')); |
|||
507 | 1 | $conditions->add($empty_conditions); |
|||
508 | } else { |
||||
509 | 1 | $conditions->add($qb->expr()->eq('c.' . $upfield, ':' . $upfield)); |
|||
510 | 1 | $parameters[$upfield] = $up; |
|||
511 | } |
||||
512 | |||||
513 | 1 | $qb->where($conditions) |
|||
514 | 1 | ->setParameters($parameters); |
|||
515 | |||||
516 | 1 | return $qb; |
|||
517 | } |
||||
518 | |||||
519 | /** |
||||
520 | * @return boolean |
||||
521 | */ |
||||
522 | public function parent() |
||||
523 | { |
||||
524 | return false; |
||||
525 | } |
||||
526 | |||||
527 | 1 | public function has_parameters() : bool |
|||
528 | { |
||||
529 | 1 | return !$this->get_collection('midgard_parameter')->is_empty($this->guid); |
|||
530 | } |
||||
531 | |||||
532 | 4 | public function list_parameters(?string $domain = null) : array |
|||
533 | { |
||||
534 | 4 | $constraints = []; |
|||
535 | 4 | if ($domain) { |
|||
536 | 1 | $constraints = ["domain" => $domain]; |
|||
537 | } |
||||
538 | |||||
539 | 4 | return $this->get_collection('midgard_parameter')->find($this->guid, $constraints); |
|||
540 | } |
||||
541 | |||||
542 | 3 | public function find_parameters(array $constraints = []) : array |
|||
543 | { |
||||
544 | 3 | return $this->get_collection('midgard_parameter')->find($this->guid, $constraints); |
|||
545 | } |
||||
546 | |||||
547 | 1 | public function delete_parameters(array $constraints = []) : int |
|||
548 | { |
||||
549 | 1 | return $this->get_collection('midgard_parameter')->delete($this->guid, $constraints); |
|||
550 | } |
||||
551 | |||||
552 | 1 | public function purge_parameters(array $constraints = []) : int |
|||
553 | { |
||||
554 | 1 | return $this->get_collection('midgard_parameter')->purge($this->guid, $constraints); |
|||
555 | } |
||||
556 | |||||
557 | 2 | public function get_parameter(string $domain, string $name) |
|||
558 | { |
||||
559 | 2 | if (!$this->guid) { |
|||
560 | 1 | return false; |
|||
561 | } |
||||
562 | 2 | $qb = connection::get_em()->createQueryBuilder(); |
|||
563 | 2 | $qb |
|||
564 | 2 | ->select('c.value') |
|||
565 | 2 | ->from(connection::get_fqcn('midgard_parameter'), 'c') |
|||
566 | 2 | ->where('c.domain = :domain AND c.name = :name AND c.parentguid = :parentguid') |
|||
567 | 2 | ->setParameters(['domain' => $domain, 'name' => $name, 'parentguid' => $this->guid]); |
|||
568 | |||||
569 | 2 | return $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR); |
|||
570 | } |
||||
571 | |||||
572 | 11 | public function set_parameter(string $domain, string $name, $value) : bool |
|||
573 | { |
||||
574 | 11 | $constraints = [ |
|||
575 | 11 | 'domain' => $domain, |
|||
576 | 11 | 'name' => $name, |
|||
577 | 11 | ]; |
|||
578 | 11 | $params = $this->get_collection('midgard_parameter')->find($this->guid, $constraints); |
|||
579 | |||||
580 | // check value |
||||
581 | 11 | if (in_array($value, [false, null, ''], true)) { |
|||
582 | 2 | if (empty($params)) { |
|||
583 | 1 | exception::not_exists(); |
|||
584 | 1 | return false; |
|||
585 | } |
||||
586 | 2 | foreach ($params as $param) { |
|||
587 | 2 | $stat = $param->delete(); |
|||
588 | } |
||||
589 | 2 | return $stat; |
|||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
590 | } |
||||
591 | |||||
592 | 11 | $om = new objectmanager(connection::get_em()); |
|||
593 | try { |
||||
594 | // create new |
||||
595 | 11 | if (empty($params)) { |
|||
596 | 11 | $parameter = $om->new_instance(connection::get_fqcn('midgard_parameter')); |
|||
597 | 11 | $parameter->parentguid = $this->guid; |
|||
0 ignored issues
–
show
The property
parentguid does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
598 | 11 | $parameter->domain = $domain; |
|||
0 ignored issues
–
show
The property
domain does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
599 | 11 | $parameter->name = $name; |
|||
0 ignored issues
–
show
The property
name does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
600 | 11 | $parameter->value = $value; |
|||
0 ignored issues
–
show
The property
value does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
601 | 11 | $om->create($parameter); |
|||
602 | } |
||||
603 | // use existing |
||||
604 | else { |
||||
605 | 1 | $parameter = $params[0]; |
|||
606 | 1 | $parameter->value = $value; |
|||
0 ignored issues
–
show
|
|||||
607 | 1 | $om->update($parameter); |
|||
608 | } |
||||
609 | 11 | return true; |
|||
610 | } catch (\Exception $e) { |
||||
611 | exception::internal($e); |
||||
612 | return false; |
||||
613 | } |
||||
614 | } |
||||
615 | |||||
616 | /** |
||||
617 | * The signature is a little different from original, because Doctrine doesn't support func_get_args() in proxies |
||||
618 | */ |
||||
619 | 2 | public function parameter(string $domain, string $name, $value = '__UNINITIALIZED__') |
|||
620 | { |
||||
621 | 2 | if ($value === '__UNINITIALIZED__') { |
|||
622 | 1 | return $this->get_parameter($domain, $name); |
|||
623 | } |
||||
624 | 2 | return $this->set_parameter($domain, $name, $value); |
|||
625 | } |
||||
626 | |||||
627 | 1 | public function has_attachments() : bool |
|||
628 | { |
||||
629 | 1 | return !$this->get_collection('midgard_attachment')->is_empty($this->guid); |
|||
630 | } |
||||
631 | |||||
632 | 2 | public function list_attachments() : array |
|||
633 | { |
||||
634 | 2 | return $this->get_collection('midgard_attachment')->find($this->guid, []); |
|||
635 | } |
||||
636 | |||||
637 | public function find_attachments(array $constraints = []) : array |
||||
638 | { |
||||
639 | return $this->get_collection('midgard_attachment')->find($this->guid, $constraints); |
||||
640 | } |
||||
641 | |||||
642 | public function delete_attachments(array $constraints = []) : int |
||||
643 | { |
||||
644 | return $this->get_collection('midgard_attachment')->delete($this->guid, $constraints); |
||||
645 | } |
||||
646 | |||||
647 | /** |
||||
648 | * @return boolean False if one or more attachments couldn't be deleted |
||||
649 | * @todo Implement delete_blob & return value |
||||
650 | */ |
||||
651 | public function purge_attachments(array $constraints = [], bool $delete_blob = true) |
||||
652 | { |
||||
653 | return $this->get_collection('midgard_attachment')->purge($this->guid, $constraints); |
||||
654 | } |
||||
655 | |||||
656 | 3 | public function create_attachment(string $name, string $title = '', string $mimetype = '') : ?attachment |
|||
657 | { |
||||
658 | 3 | $existing = $this->get_collection('midgard_attachment')->find($this->guid, ['name' => $name]); |
|||
659 | 3 | if (!empty($existing)) { |
|||
660 | 1 | exception::object_name_exists(); |
|||
661 | 1 | return null; |
|||
662 | } |
||||
663 | 3 | $om = new objectmanager(connection::get_em()); |
|||
664 | 3 | $att = $om->new_instance(connection::get_fqcn('midgard_attachment')); |
|||
665 | |||||
666 | 3 | $att->parentguid = $this->guid; |
|||
0 ignored issues
–
show
The property
parentguid does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
667 | 3 | $att->title = $title; |
|||
0 ignored issues
–
show
The property
title does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
668 | 3 | $att->name = $name; |
|||
0 ignored issues
–
show
The property
name does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
669 | 3 | $att->mimetype = $mimetype; |
|||
0 ignored issues
–
show
The property
mimetype does not exist on midgard\portable\api\dbobject . Since you implemented __set , consider adding a @property annotation.
![]() |
|||||
670 | try { |
||||
671 | 3 | $om->create($att); |
|||
672 | 3 | return $att; |
|||
673 | } catch (\Exception $e) { |
||||
674 | exception::internal($e); |
||||
675 | return null; |
||||
676 | } |
||||
677 | } |
||||
678 | |||||
679 | /** |
||||
680 | * @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension, |
||||
681 | * so we might consider ignoring it, too |
||||
682 | */ |
||||
683 | 17 | public function purge(bool $check_dependencies = true) : bool |
|||
684 | { |
||||
685 | 17 | if (!$this->cm->getIdentifierValues($this)) { |
|||
686 | // This usually means that the object has been purged already |
||||
687 | exception::not_exists(); |
||||
688 | return false; |
||||
689 | } |
||||
690 | 17 | if ( $check_dependencies |
|||
691 | 17 | && $this->has_dependents()) { |
|||
692 | 2 | exception::has_dependants(); |
|||
693 | 2 | return false; |
|||
694 | } |
||||
695 | |||||
696 | try { |
||||
697 | 17 | $om = new objectmanager(connection::get_em()); |
|||
698 | 17 | $om->purge($this); |
|||
699 | 2 | } catch (\Doctrine\ORM\EntityNotFoundException) { |
|||
700 | 2 | exception::not_exists(); |
|||
701 | 2 | return false; |
|||
702 | } catch (\Exception $e) { |
||||
703 | exception::internal($e); |
||||
704 | return false; |
||||
705 | } |
||||
706 | |||||
707 | 17 | return true; |
|||
708 | } |
||||
709 | |||||
710 | 2 | public static function undelete(string $guid) : bool |
|||
711 | { |
||||
712 | 2 | return \midgard_object_class::undelete($guid); |
|||
713 | } |
||||
714 | |||||
715 | 1 | public static function new_query_builder() : \midgard_query_builder |
|||
716 | { |
||||
717 | 1 | return new \midgard_query_builder(static::class); |
|||
718 | } |
||||
719 | |||||
720 | 1 | public static function new_collector(string $field, $value) : \midgard_collector |
|||
721 | { |
||||
722 | 1 | return new \midgard_collector(static::class, $field, $value); |
|||
723 | } |
||||
724 | |||||
725 | public static function new_reflection_property() : \midgard_reflection_property |
||||
726 | { |
||||
727 | return new \midgard_reflection_property(static::class); |
||||
728 | } |
||||
729 | |||||
730 | 110 | public function set_guid(string $guid) |
|||
731 | { |
||||
732 | 110 | parent::__set('guid', $guid); |
|||
733 | } |
||||
734 | |||||
735 | /** |
||||
736 | * Helper for managing the isapproved and islocked metadata properties |
||||
737 | */ |
||||
738 | 8 | private function manage_meta_property(string $action, bool $value) : bool |
|||
739 | { |
||||
740 | 8 | if (!($this instanceof metadata_interface)) { |
|||
741 | 4 | exception::no_metadata(); |
|||
742 | 4 | return false; |
|||
743 | } |
||||
744 | 4 | $user = connection::get_user(); |
|||
745 | 4 | if ($user === null) { |
|||
746 | 4 | exception::access_denied(); |
|||
747 | 4 | return false; |
|||
748 | } |
||||
749 | 4 | if ($action == 'lock') { |
|||
750 | 2 | $flag = 'islocked'; |
|||
751 | 2 | } elseif ($action == 'approve') { |
|||
752 | 2 | $flag = 'isapproved'; |
|||
753 | } else { |
||||
754 | throw new exception('Unsupported action ' . $action); |
||||
755 | } |
||||
756 | // same val |
||||
757 | 4 | if ($this->__get('metadata')->$flag === $value) { |
|||
758 | 3 | return false; |
|||
759 | } |
||||
760 | 4 | if ($value === false) { |
|||
761 | 2 | $action = 'un' . $action; |
|||
762 | } |
||||
763 | |||||
764 | 4 | if ($this->cm->getIdentifierValues($this)) { |
|||
765 | try { |
||||
766 | 4 | $om = new objectmanager(connection::get_em()); |
|||
767 | 4 | $om->{$action}($this); |
|||
768 | } catch (\Exception $e) { |
||||
769 | exception::internal($e); |
||||
770 | return false; |
||||
771 | } |
||||
772 | } |
||||
773 | |||||
774 | 4 | return true; |
|||
775 | } |
||||
776 | |||||
777 | 3 | public function approve() : bool |
|||
778 | { |
||||
779 | 3 | return $this->manage_meta_property("approve", true); |
|||
780 | } |
||||
781 | |||||
782 | 2 | public function is_approved() : bool |
|||
783 | { |
||||
784 | 2 | if (!($this instanceof metadata_interface)) { |
|||
785 | exception::no_metadata(); |
||||
786 | return false; |
||||
787 | } |
||||
788 | 2 | return $this->metadata_isapproved; |
|||
0 ignored issues
–
show
The property
metadata_isapproved does not exist on midgard\portable\api\mgdobject . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
789 | } |
||||
790 | |||||
791 | 2 | public function unapprove() : bool |
|||
792 | { |
||||
793 | 2 | return $this->manage_meta_property("approve", false); |
|||
794 | } |
||||
795 | |||||
796 | 3 | public function lock() : bool |
|||
797 | { |
||||
798 | 3 | if ($this->is_locked()) { |
|||
799 | 1 | exception::object_is_locked(); |
|||
800 | 1 | return false; |
|||
801 | } |
||||
802 | 3 | return $this->manage_meta_property("lock", true); |
|||
803 | } |
||||
804 | |||||
805 | 3 | public function is_locked() : bool |
|||
806 | { |
||||
807 | 3 | if (!($this instanceof metadata_interface)) { |
|||
808 | 1 | exception::no_metadata(); |
|||
809 | 1 | return false; |
|||
810 | } |
||||
811 | 2 | return $this->metadata_islocked; |
|||
0 ignored issues
–
show
The property
metadata_islocked does not exist on midgard\portable\api\mgdobject . Since you implemented __get , consider adding a @property annotation.
![]() |
|||||
812 | } |
||||
813 | |||||
814 | 2 | public function unlock() : bool |
|||
815 | { |
||||
816 | 2 | return $this->manage_meta_property("lock", false); |
|||
817 | } |
||||
818 | } |
||||
819 |
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.