Passed
Branch master (4b377c)
by Andreas
03:22
created

mgdobject   F

Complexity

Total Complexity 154

Size/Duplication

Total Lines 796
Duplicated Lines 0 %

Test Coverage

Coverage 86.24%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 407
dl 0
loc 796
ccs 376
cts 436
cp 0.8624
rs 2
c 4
b 0
f 0
wmc 154

52 Methods

Rating   Name   Duplication   Size   Complexity  
A is_in_parent_tree() 0 3 1
A check_fields() 0 13 5
A check_upfield() 0 11 5
B has_dependents() 0 34 6
A check_parent() 0 14 4
A get_parent() 0 3 1
A is_in_tree() 0 3 1
A _list() 0 14 2
A __debugInfo() 0 15 4
A __call() 0 6 2
A __set() 0 6 2
A __construct() 0 7 4
A get_collection() 0 6 2
A list_parameters() 0 8 2
A new_query_builder() 0 3 1
A set_guid() 0 3 1
A list_children() 0 3 1
A update() 0 19 4
A get_by_guid() 0 14 3
A unlock() 0 3 1
A approve() 0 3 1
A new_reflection_property() 0 3 1
A has_attachments() 0 3 1
A undelete() 0 3 1
A purge_parameters() 0 3 1
B manage_meta_property() 0 38 9
A find_parameters() 0 3 1
A is_locked() 0 7 2
A unapprove() 0 3 1
A get_parameter() 0 13 2
B is_unique() 0 55 10
A is_approved() 0 7 2
A has_parameters() 0 3 1
A delete_attachments() 0 3 1
A list_attachments() 0 3 1
A create_attachment() 0 21 3
A lock() 0 7 2
A new_collector() 0 3 1
B delete() 0 29 7
A purge() 0 26 6
A parent() 0 3 1
B get_by_path() 0 55 8
A parameter() 0 6 2
A purge_attachments() 0 3 1
A find_attachments() 0 3 1
A get_uniquefield_query() 0 25 2
B load_parent() 0 22 8
A create() 0 24 6
A delete_parameters() 0 3 1
A __get() 0 9 4
B set_parameter() 0 42 6
B get_by_id() 0 31 8

How to fix   Complexity   

Complex Class

Complex classes like mgdobject often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use mgdobject, and based on these observations, apply Extract Interface, too.

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
20
/**
21
 * @property metadata $metadata
22
 */
23
abstract class mgdobject extends dbobject
24
{
25
    protected $metadata; // compat with mgd behavior: If the schema has no metadata, the property is present anyway
26
27
    public $action = ''; // <== does this need to do anything?
28
29
    private $collections = [];
30
31
    /**
32
     * @param mixed $id ID or GUID
33
     */
34 132
    public function __construct($id = null)
35
    {
36 132
        if ($id !== null) {
37 49
            if (is_int($id)) {
38 40
                $this->get_by_id($id);
39 14
            } elseif (is_string($id)) {
40 14
                $this->get_by_guid($id);
41
            }
42
        }
43
    }
44
45 16
    private function get_collection(string $classname) : collection
46
    {
47 16
        if (!isset($this->collections[$classname])) {
48 16
            $this->collections[$classname] = new collection($classname);
49
        }
50 16
        return $this->collections[$classname];
51
    }
52
53 1
    public function __debugInfo()
54
    {
55 1
        $ret = parent::__debugInfo();
56 1
        if (property_exists($this, 'metadata')) {
57 1
            $metadata = new \stdClass;
58 1
            foreach ($this->cm->getFieldNames() as $name) {
59 1
                if (strpos($name, 'metadata_') !== false) {
60 1
                    $fieldname = str_replace('metadata_', '', $name);
61 1
                    $metadata->$fieldname = $this->__get($name);
62
                }
63
            }
64 1
            $ret['metadata'] = $metadata;
65
        }
66
67 1
        return $ret;
68
    }
69
70 109
    public function __set($field, $value)
71
    {
72 109
        if ($field == 'guid') {
73 6
            return;
74
        }
75 109
        parent::__set($field, $value);
76
    }
77
78 125
    public function __get($field)
79
    {
80 125
        if (   $field === 'metadata'
81 125
            && $this->metadata === null
82
            && $this instanceof metadata_interface) {
83 101
            $this->metadata = new metadata($this);
84
        }
85
86 125
        return parent::__get($field);
87
    }
88
89 1
    public function __call($method, $args)
90
    {
91 1
        if ($method === 'list') {
92 1
            return $this->_list();
93
        }
94
        throw new \BadMethodCallException("Unknown method " . $method . " on " . get_class($this));
95
    }
96
97 4
    protected function load_parent(array $candidates) : ?dbobject
98
    {
99 4
        foreach ($candidates as $candidate) {
100 4
            if (   is_string($this->$candidate)
101 4
                && mgd_is_guid($this->$candidate)) {
102
                return \midgard_object_class::get_object_by_guid($this->$candidate);
103
            }
104 4
            if ($this->$candidate !== null) {
105
                //Proxies become stale if the object itself is detached, so we have to re-fetch
106 4
                if (   $this->$candidate instanceof Proxy
107 4
                    && $this->$candidate->__isInitialized()) {
108
                    try {
109 1
                        $this->$candidate->get_by_id($this->$candidate->id);
110
                    } catch (exception $e) {
111
                        connection::log()->error('Failed to refresh parent from proxy: ' . $e->getMessage());
112
                        return null;
113
                    }
114
                }
115 4
                return $this->$candidate;
116
            }
117
        }
118 1
        return null;
119
    }
120
121 42
    public function get_by_id(int $id) : bool
122
    {
123 42
        $entity = connection::get_em()->find(get_class($this), $id);
124
125 42
        if ($entity === null) {
126 2
            throw exception::not_exists();
127
        }
128
        // According to Doctrine documentation, proxies should be transparent, but in practice,
129
        // there will be problems if we don't force-load
130 41
        if (   $entity instanceof Proxy
131 41
            && !$entity->__isInitialized()) {
132
            try {
133 6
                $entity->__load();
134
            } catch (\Doctrine\ORM\EntityNotFoundException $e) {
135
                throw exception::object_purged();
136
            }
137
        }
138 41
        if ($entity instanceof metadata_interface && $entity->{metadata_interface::DELETED_FIELD}) {
139
            // This can happen when the "deleted" entity is still in EM's identity map
140
            throw exception::object_deleted();
141
        }
142 41
        if (empty($entity->guid)) {
143
            // This can happen when a reference proxy to a purged entity is still in EM's identity map
144
            throw exception::object_purged();
145
        }
146
147 41
        $this->populate_from_entity($entity);
148
149 41
        connection::get_em()->detach($entity);
150 41
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
151 41
        return true;
152
    }
153
154 16
    public function get_by_guid(string $guid) : bool
155
    {
156 16
        if (!mgd_is_guid($guid)) {
157 1
            throw new \InvalidArgumentException("'$guid' is not a valid guid");
158
        }
159 15
        $entity = connection::get_em()->getRepository(get_class($this))->findOneBy(['guid' => $guid]);
160 15
        if ($entity === null) {
161
            throw exception::not_exists();
162
        }
163 15
        $this->populate_from_entity($entity);
164
165 15
        connection::get_em()->detach($entity);
166 15
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
167 15
        return true;
168
    }
169
170 107
    public function create() : bool
171
    {
172 107
        if (!empty($this->id)) {
173 2
            exception::duplicate();
174 2
            return false;
175
        }
176 107
        if (   !$this->is_unique()
177 107
            || !$this->check_parent()) {
178 2
            return false;
179
        }
180 107
        if (!$this->check_fields()) {
181 1
            return false;
182
        }
183
        try {
184 106
            $om = new objectmanager(connection::get_em());
185 106
            $om->create($this);
186
        } catch (\Exception $e) {
187
            exception::internal($e);
188
            return false;
189
        }
190
191 106
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
192
193 106
        return $this->id != 0;
194
    }
195
196 16
    public function update() : bool
197
    {
198 16
        if (empty($this->id)) {
199 1
            midgard_connection::get_instance()->set_error(MGD_ERR_INTERNAL);
200 1
            return false;
201
        }
202 15
        if (!$this->check_fields()) {
203 2
            return false;
204
        }
205
        try {
206 13
            $om = new objectmanager(connection::get_em());
207 13
            $om->update($this);
208
        } catch (\Exception $e) {
209
            exception::internal($e);
210
            return false;
211
        }
212 13
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
213
214 13
        return true;
215
    }
216
217
    /**
218
     * @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension,
219
     * so we might consider ignoring it, too
220
     */
221 28
    public function delete(bool $check_dependencies = true) : bool
222
    {
223 28
        if (empty($this->id)) {
224 1
            midgard_connection::get_instance()->set_error(MGD_ERR_INVALID_PROPERTY_VALUE);
225 1
            return false;
226
        }
227 27
        if (   $check_dependencies
228 27
            && $this->has_dependents()) {
229 4
            exception::has_dependants();
230 4
            return false;
231
        }
232 27
        if (!($this instanceof metadata_interface)) {
233 1
            exception::invalid_property_value();
234 1
            return false;
235
        }
236 26
        if ($this->{metadata_interface::DELETED_FIELD}) {
237 1
            return true;
238
        }
239
240
        try {
241 26
            $om = new objectmanager(connection::get_em());
242 26
            $om->delete($this);
243
        } catch (\Exception $e) {
244
            exception::internal($e);
245
            return false;
246
        }
247
248 26
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
249 26
        return true;
250
    }
251
252 107
    private function is_unique() : bool
253
    {
254 107
        $this->initialize();
255
256 107
        if (empty($this->cm->midgard['unique_fields'])) {
257 101
            return true;
258
        }
259
260 8
        $qb = connection::get_em()->createQueryBuilder();
261 8
        $qb->from(get_class($this), 'c');
262 8
        $conditions = $qb->expr()->andX();
263 8
        $parameters = [];
264 8
        if ($this->id) {
265
            $parameters['id'] = $this->id;
266
            $conditions->add($qb->expr()->neq('c.id', ':id'));
267
        }
268 8
        $found = false;
269 8
        foreach ($this->cm->midgard['unique_fields'] as $field) {
270 8
            if (empty($this->$field)) {
271
                //empty names automatically pass according to Midgard logic
272 2
                continue;
273
            }
274 7
            $conditions->add($qb->expr()->eq('c.' . $field, ':' . $field));
275 7
            $parameters[$field] = $this->$field;
276 7
            $found = true;
277
        }
278
279 8
        if (!$found) {
280 2
            return true;
281
        }
282
283 7
        foreach (['upfield', 'parentfield'] as $candidate) {
284 7
            if (!empty($this->cm->midgard[$candidate])) {
285
                // TODO: This needs to be changed so that value is always numeric, since this is how midgard does it
286 6
                if ($this->{$this->cm->midgard[$candidate]} === null) {
287 6
                    $conditions->add($qb->expr()->isNull('c.' . $this->cm->midgard[$candidate]));
288
                } else {
289 6
                    $conditions->add($qb->expr()->eq('c.' . $this->cm->midgard[$candidate], ':' . $this->cm->midgard[$candidate]));
290 6
                    $parameters[$this->cm->midgard[$candidate]] = $this->{$this->cm->midgard[$candidate]};
291
                }
292 6
                break;
293
            }
294
        }
295
296 7
        $qb->where($conditions)
297 7
            ->setParameters($parameters);
298
299 7
        $qb->select("count(c)");
300 7
        $count = (int) $qb->getQuery()->getSingleScalarResult();
301
302 7
        if ($count !== 0) {
303 1
            exception::object_name_exists();
304 1
            return false;
305
        }
306 7
        return true;
307
    }
308
309 107
    private function check_parent() : bool
310
    {
311 107
        $this->initialize();
312
313 107
        if (   empty($this->cm->midgard['parentfield'])
314 107
            || empty($this->cm->midgard['parent'])) {
315 107
            return true;
316
        }
317
318 8
        if (empty($this->{$this->cm->midgard['parentfield']})) {
319 1
            exception::object_no_parent();
320 1
            return false;
321
        }
322 8
        return true;
323
    }
324
325 107
    private function check_fields() : bool
326
    {
327 107
        $this->initialize();
328
329 107
        foreach ($this->cm->fieldMappings as $name => $field) {
330 107
            if (   $field['midgard:midgard_type'] == translator::TYPE_GUID
331 107
                && !empty($this->$name)
332 107
                && !mgd_is_guid($this->$name)) {
333 2
                exception::invalid_property_value("'" . $name . "' property's value is not a guid.");
334 2
                return false;
335
            }
336
        }
337 106
        return $this->check_upfield();
338
    }
339
340 106
    private function check_upfield() : bool
341
    {
342 106
        if (   !empty($this->id)
343 106
            && !empty($this->cm->midgard['upfield'])
344 106
            && $this->__get($this->cm->midgard['upfield']) === $this->id
345 106
            && $this->cm->getAssociationMapping($this->cm->midgard['upfield'])['targetEntity'] === $this->cm->getName()) {
346 1
            exception::tree_is_circular();
347 1
            return false;
348
        }
349
        // @todo this should be recursive
350 106
        return true;
351
    }
352
353
    public function is_in_parent_tree($root_id, $id) : bool
354
    {
355
        return false;
356
    }
357
358
    public function is_in_tree($root_id, $id) : bool
359
    {
360
        return false;
361
    }
362
363 34
    public function has_dependents() : bool
364
    {
365 34
        $this->initialize();
366
367 34
        $stat = false;
368
369 34
        if (!empty($this->cm->midgard['upfield'])) {
370 29
            $qb = connection::get_em()->createQueryBuilder();
371 29
            $qb->from(get_class($this), 'c')
372 29
                ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0')
373 29
                ->setParameter(0, $this->id)
374 29
                ->select("COUNT(c)");
375 29
            $results = (int) $qb->getQuery()->getSingleScalarResult();
376 29
            $stat = $results > 0;
377
        }
378
379 34
        if (   !$stat
380 34
            && !empty($this->cm->midgard['childtypes'])) {
381 27
            foreach ($this->cm->midgard['childtypes'] as $typename => $parentfield) {
382 27
                $qb = connection::get_em()->createQueryBuilder();
383 27
                $qb->from(connection::get_fqcn($typename), 'c')
384 27
                    ->where('c.' . $parentfield . ' = ?0')
385 27
                    ->setParameter(0, $this->id)
386 27
                    ->select("COUNT(c)");
387
388 27
                $results = (int) $qb->getQuery()->getSingleScalarResult();
389 27
                $stat = $results > 0;
390 27
                if ($stat) {
391 3
                    break;
392
                }
393
            }
394
        }
395
396 34
        return $stat;
397
    }
398
399
    public function get_parent()
400
    {
401
        return null;
402
    }
403
404
    /**
405
     * This function is called list() in Midgard, but that doesn't work in plain PHP
406
     */
407 1
    private function _list() : array
408
    {
409 1
        $this->initialize();
410
411 1
        if (!empty($this->cm->midgard['upfield'])) {
412 1
            $qb = connection::get_em()->createQueryBuilder();
413 1
            $qb->from(get_class($this), 'c')
414 1
                ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0')
415 1
                ->setParameter(0, $this->id)
416 1
                ->select("c");
417 1
            return $qb->getQuery()->getResult();
418
        }
419
420
        return [];
421
    }
422
423
    /**
424
     * This should return child objects, but only if they are of a different type
425
     * For all other input, an empty array is returned
426
     * (not implemented yet)
427
     */
428
    public function list_children(string $classname) : array
429
    {
430
        return [];
431
    }
432
433 1
    public function get_by_path(string $path) : bool
434
    {
435 1
        $parts = explode('/', trim($path, '/'));
436 1
        if (empty($parts)) {
437
            return false;
438
        }
439 1
        $this->initialize();
440
441 1
        if (count($this->cm->midgard['unique_fields']) != 1) {
442
            return false;
443
        }
444
445 1
        $field = $this->cm->midgard['unique_fields'][0];
446
447 1
        if (!empty($this->cm->midgard['parent'])) {
448 1
            $parent_cm = connection::get_em()->getClassMetadata(connection::get_fqcn($this->cm->midgard['parent']));
449 1
            $parentclass = $this->cm->fullyQualifiedClassName($this->cm->midgard['parent']);
450 1
            $parentfield = $parent_cm->midgard['upfield'];
0 ignored issues
show
Bug introduced by
Accessing midgard on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
451 1
            $upfield = $this->cm->midgard['parentfield'];
452 1
        } elseif (!empty($this->cm->midgard['upfield'])) {
453 1
            $parentclass = get_class($this);
454 1
            $upfield = $this->cm->midgard['upfield'];
455 1
            $parentfield = $upfield;
456
        } else {
457
            return false;
458
        }
459
460 1
        $name = array_pop($parts);
461 1
        $up = 0;
462 1
        foreach ($parts as $part) {
463 1
            $qb = $this->get_uniquefield_query($parentclass, $field, $part, $parentfield, $up);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

463
            $qb = $this->get_uniquefield_query(/** @scrutinizer ignore-type */ $parentclass, $field, $part, $parentfield, $up);
Loading history...
464 1
            $qb->select("c.id");
465 1
            $up = (int) $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
466 1
            if ($up === 0) {
467 1
                exception::not_exists();
468 1
                $this->id = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
469 1
                $this->set_guid('');
470 1
                return false;
471
            }
472
        }
473
474 1
        $qb = $this->get_uniquefield_query(get_class($this), $field, $name, $upfield, $up);
475 1
        $qb->select("c");
476
477 1
        $entity = $qb->getQuery()->getOneOrNullResult();
478
479 1
        if ($entity === null) {
480 1
            exception::not_exists();
481 1
            $this->id = 0;
482 1
            $this->set_guid('');
483 1
            return false;
484
        }
485 1
        $this->populate_from_entity($entity);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

485
        $this->populate_from_entity(/** @scrutinizer ignore-type */ $entity);
Loading history...
486
487 1
        return true;
488
    }
489
490 1
    protected function get_uniquefield_query(string $classname, string $field, string $part, string $upfield, int $up) : QueryBuilder
491
    {
492 1
        $qb = connection::get_em()->createQueryBuilder();
493 1
        $qb->from($classname, 'c');
494 1
        $conditions = $qb->expr()->andX();
495 1
        $conditions->add($qb->expr()->eq('c.' . $field, ':' . $field));
496 1
        $parameters = [
497
            $field => $part
498
        ];
499
500 1
        if (empty($up)) {
501
            // If the database was created by Midgard, it might contain 0 instead of NULL, so...
502 1
            $empty_conditions = $qb->expr()->orX()
503 1
                ->add($qb->expr()->isNull('c.' . $upfield))
504 1
                ->add($qb->expr()->eq('c.' . $upfield, '0'));
505 1
            $conditions->add($empty_conditions);
506
        } else {
507 1
            $conditions->add($qb->expr()->eq('c.' . $upfield, ':' . $upfield));
508 1
            $parameters[$upfield] = $up;
509
        }
510
511 1
        $qb->where($conditions)
512 1
            ->setParameters($parameters);
513
514 1
        return $qb;
515
    }
516
517
    /**
518
     * @return boolean
519
     */
520
    public function parent()
521
    {
522
        return false;
523
    }
524
525 1
    public function has_parameters() : bool
526
    {
527 1
        return !$this->get_collection('midgard_parameter')->is_empty($this->guid);
528
    }
529
530 4
    public function list_parameters(string $domain = null) : array
531
    {
532 4
        $constraints = [];
533 4
        if ($domain) {
534 1
            $constraints = ["domain" => $domain];
535
        }
536
537 4
        return $this->get_collection('midgard_parameter')->find($this->guid, $constraints);
538
    }
539
540 3
    public function find_parameters(array $constraints = []) : array
541
    {
542 3
        return $this->get_collection('midgard_parameter')->find($this->guid, $constraints);
543
    }
544
545 1
    public function delete_parameters(array $constraints = []) : int
546
    {
547 1
        return $this->get_collection('midgard_parameter')->delete($this->guid, $constraints);
548
    }
549
550 1
    public function purge_parameters(array $constraints = []) : int
551
    {
552 1
        return $this->get_collection('midgard_parameter')->purge($this->guid, $constraints);
553
    }
554
555 2
    public function get_parameter(string $domain, string $name)
556
    {
557 2
        if (!$this->guid) {
558 1
            return false;
559
        }
560 2
        $qb = connection::get_em()->createQueryBuilder();
561
        $qb
562 2
            ->select('c.value')
563 2
            ->from(connection::get_fqcn('midgard_parameter'), 'c')
564 2
            ->where('c.domain = :domain AND c.name = :name AND c.parentguid = :parentguid')
565 2
            ->setParameters(['domain' => $domain, 'name' => $name, 'parentguid' => $this->guid]);
566
567 2
        return $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
568
    }
569
570 11
    public function set_parameter(string $domain, string $name, $value) : bool
571
    {
572 11
        $constraints = [
573
            'domain' => $domain,
574
            'name' => $name,
575
        ];
576 11
        $params = $this->get_collection('midgard_parameter')->find($this->guid, $constraints);
577
578
        // check value
579 11
        if (in_array($value, [false, null, ''], true)) {
580 2
            if (empty($params)) {
581 1
                exception::not_exists();
582 1
                return false;
583
            }
584 2
            foreach ($params as $param) {
585 2
                $stat = $param->delete();
586
            }
587 2
            return $stat;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $stat seems to be defined by a foreach iteration on line 584. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
588
        }
589
590 11
        $om = new objectmanager(connection::get_em());
591
        try {
592
            // create new
593 11
            if (empty($params)) {
594 11
                $parameter = $om->new_instance(connection::get_fqcn('midgard_parameter'));
595 11
                $parameter->parentguid = $this->guid;
0 ignored issues
show
Bug Best Practice introduced by
The property parentguid does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
596 11
                $parameter->domain = $domain;
0 ignored issues
show
Bug Best Practice introduced by
The property domain does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
597 11
                $parameter->name = $name;
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
598 11
                $parameter->value = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property value does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
599 11
                $om->create($parameter);
600
            }
601
            // use existing
602
            else {
603 1
                $parameter = $params[0];
604 1
                $parameter->value = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property value does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
605 1
                $om->update($parameter);
606
            }
607 11
            midgard_connection::get_instance()->set_error(MGD_ERR_OK);
608 11
            return true;
609
        } catch (\Exception $e) {
610
            exception::internal($e);
611
            return false;
612
        }
613
    }
614
615
    /**
616
     * The signature is a little different from original, because Doctrine doesn't support func_get_args() in proxies
617
     */
618 2
    public function parameter(string $domain, string $name, $value = '__UNINITIALIZED__')
619
    {
620 2
        if ($value === '__UNINITIALIZED__') {
621 1
            return $this->get_parameter($domain, $name);
622
        }
623 2
        return $this->set_parameter($domain, $name, $value);
624
    }
625
626 1
    public function has_attachments() : bool
627
    {
628 1
        return !$this->get_collection('midgard_attachment')->is_empty($this->guid);
629
    }
630
631 2
    public function list_attachments() : array
632
    {
633 2
        return $this->get_collection('midgard_attachment')->find($this->guid, []);
634
    }
635
636
    public function find_attachments(array $constraints = []) : array
637
    {
638
        return $this->get_collection('midgard_attachment')->find($this->guid, $constraints);
639
    }
640
641
    public function delete_attachments(array $constraints = []) : int
642
    {
643
        return $this->get_collection('midgard_attachment')->delete($this->guid, $constraints);
644
    }
645
646
    /**
647
     * @return boolean False if one or more attachments couldn't be deleted
648
     * @todo Implement delete_blob & return value
649
     */
650
    public function purge_attachments(array $constraints = [], bool $delete_blob = true)
651
    {
652
        return $this->get_collection('midgard_attachment')->purge($this->guid, $constraints);
653
    }
654
655 3
    public function create_attachment(string $name, string $title = '', string $mimetype = '') : ?attachment
656
    {
657 3
        $existing = $this->get_collection('midgard_attachment')->find($this->guid, ['name' => $name]);
658 3
        if (!empty($existing)) {
659 1
            exception::object_name_exists();
660 1
            return null;
661
        }
662 3
        $om = new objectmanager(connection::get_em());
663 3
        $att = $om->new_instance(connection::get_fqcn('midgard_attachment'));
664
665 3
        $att->parentguid = $this->guid;
0 ignored issues
show
Bug Best Practice introduced by
The property parentguid does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
666 3
        $att->title = $title;
0 ignored issues
show
Bug Best Practice introduced by
The property title does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
667 3
        $att->name = $name;
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
668 3
        $att->mimetype = $mimetype;
0 ignored issues
show
Bug Best Practice introduced by
The property mimetype does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
669
        try {
670 3
            $om->create($att);
671 3
            midgard_connection::get_instance()->set_error(MGD_ERR_OK);
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 16
    public function purge(bool $check_dependencies = true) : bool
684
    {
685 16
        if (empty($this->id)) {
686
            // This usually means that the object has been purged already
687
            exception::not_exists();
688
            return false;
689
        }
690 16
        if (   $check_dependencies
691 16
            && $this->has_dependents()) {
692 2
            exception::has_dependants();
693 2
            return false;
694
        }
695
696
        try {
697 16
            $om = new objectmanager(connection::get_em());
698 16
            $om->purge($this);
699 2
        } catch (\Doctrine\ORM\EntityNotFoundException $e) {
700 2
            exception::not_exists();
701 2
            return false;
702
        } catch (\Exception $e) {
703
            exception::internal($e);
704
            return false;
705
        }
706 16
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
707
708 16
        return true;
709
    }
710
711 2
    public static function undelete(string $guid) : bool
712
    {
713 2
        return \midgard_object_class::undelete($guid);
714
    }
715
716 1
    public static function new_query_builder() : \midgard_query_builder
717
    {
718 1
        return new \midgard_query_builder(get_called_class());
719
    }
720
721 1
    public static function new_collector(string $field, $value) : \midgard_collector
722
    {
723 1
        return new \midgard_collector(get_called_class(), $field, $value);
724
    }
725
726
    public static function new_reflection_property() : \midgard_reflection_property
727
    {
728
        return new \midgard_reflection_property(get_called_class());
729
    }
730
731 107
    public function set_guid(string $guid)
732
    {
733 107
        parent::__set('guid', $guid);
734
    }
735
736
    /**
737
     * Helper for managing the isapproved and islocked metadata properties
738
     */
739 8
    private function manage_meta_property(string $action, bool $value) : bool
740
    {
741 8
        if (!($this instanceof metadata_interface)) {
742 4
            exception::no_metadata();
743 4
            return false;
744
        }
745 4
        $user = connection::get_user();
746 4
        if ($user === null) {
747 4
            exception::access_denied();
748 4
            return false;
749
        }
750 4
        if ($action == 'lock') {
751 2
            $flag = 'islocked';
752 2
        } elseif ($action == 'approve') {
753 2
            $flag = 'isapproved';
754
        } else {
755
            throw new exception('Unsupported action ' . $action);
756
        }
757
        // same val
758 4
        if ($this->__get('metadata')->$flag === $value) {
759 3
            return false;
760
        }
761 4
        if ($value === false) {
762 2
            $action = 'un' . $action;
763
        }
764
765 4
        if ($this->id) {
766
            try {
767 4
                $om = new objectmanager(connection::get_em());
768 4
                $om->{$action}($this);
769
            } catch (\Exception $e) {
770
                exception::internal($e);
771
                return false;
772
            }
773
        }
774 4
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
775
776 4
        return true;
777
    }
778
779 3
    public function approve() : bool
780
    {
781 3
        return $this->manage_meta_property("approve", true);
782
    }
783
784 2
    public function is_approved() : bool
785
    {
786 2
        if (!($this instanceof metadata_interface)) {
787
            exception::no_metadata();
788
            return false;
789
        }
790 2
        return $this->metadata_isapproved;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_isapproved does not exist on midgard\portable\api\mgdobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
791
    }
792
793 2
    public function unapprove() : bool
794
    {
795 2
        return $this->manage_meta_property("approve", false);
796
    }
797
798 3
    public function lock() : bool
799
    {
800 3
        if ($this->is_locked()) {
801 1
            exception::object_is_locked();
802 1
            return false;
803
        }
804 3
        return $this->manage_meta_property("lock", true);
805
    }
806
807 3
    public function is_locked() : bool
808
    {
809 3
        if (!($this instanceof metadata_interface)) {
810 1
            exception::no_metadata();
811 1
            return false;
812
        }
813 2
        return $this->metadata_islocked;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_islocked does not exist on midgard\portable\api\mgdobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
814
    }
815
816 2
    public function unlock() : bool
817
    {
818 2
        return $this->manage_meta_property("lock", false);
819
    }
820
}
821