mgdobject::is_unique()   B
last analyzed

Complexity

Conditions 10
Paths 55

Size

Total Lines 54
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 10.0629

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 10
eloc 35
c 3
b 0
f 0
nc 55
nop 0
dl 0
loc 54
ccs 32
cts 35
cp 0.9143
crap 10.0629
rs 7.6666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
Bug Best Practice introduced by
The expression $identifier of type array<mixed,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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
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...
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
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

461
            $qb = $this->get_uniquefield_query(/** @scrutinizer ignore-type */ $parentclass, $field, $part, $parentfield, $up);
Loading history...
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
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

477
        $this->populate_from_entity(/** @scrutinizer ignore-type */ $entity);
Loading history...
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
The variable $stat seems to be defined by a foreach iteration on line 586. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
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
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...
598 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...
599 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...
600 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...
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
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...
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
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...
667 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...
668 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...
669 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...
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
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...
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
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...
812
    }
813
814 2
    public function unlock() : bool
815
    {
816 2
        return $this->manage_meta_property("lock", false);
817
    }
818
}
819