Passed
Push — master ( 26f97e...049cd9 )
by Andreas
08:43
created

mgdobject::get_by_path()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 55
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 8.0291

Importance

Changes 0
Metric Value
cc 8
eloc 39
c 0
b 0
f 0
nc 13
nop 1
dl 0
loc 55
ccs 36
cts 39
cp 0.9231
crap 8.0291
rs 8.0515

How to fix   Long Method   

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
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 133
    public function __construct($id = null)
35
    {
36 133
        if ($id !== null) {
37 50
            if (is_int($id)) {
38 41
                $this->get_by_id($id);
39 14
            } elseif (is_string($id)) {
40 14
                $this->get_by_guid($id);
41
            }
42
        }
43 131
    }
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 110
    public function __set($field, $value)
71
    {
72 110
        if ($field == 'guid') {
73 6
            return;
74
        }
75 110
        parent::__set($field, $value);
76 110
    }
77
78 126
    public function __get($field)
79
    {
80 126
        if (   $field === 'metadata'
81 126
            && $this->metadata === null
82 126
            && $this instanceof metadata_interface) {
83 102
            $this->metadata = new metadata($this);
84
        }
85
86 126
        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 43
    public function get_by_id(int $id) : bool
122
    {
123 43
        $entity = connection::get_em()->find(get_class($this), $id);
124
125 43
        if ($entity === null) {
126 3
            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 42
        if (   $entity instanceof Proxy
131 42
            && !$entity->__isInitialized()) {
132
            try {
133 7
                $entity->__load();
134 1
            } catch (\Doctrine\ORM\EntityNotFoundException $e) {
135 1
                throw exception::object_purged();
136
            }
137
        }
138 42
        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 42
        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 42
        $this->populate_from_entity($entity);
148
149 42
        connection::get_em()->detach($entity);
150 42
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
151 42
        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 108
    public function create() : bool
171
    {
172 108
        if (!empty($this->id)) {
173 2
            exception::duplicate();
174 2
            return false;
175
        }
176 108
        if (   !$this->is_unique()
177 108
            || !$this->check_parent()) {
178 2
            return false;
179
        }
180 108
        if (!$this->check_fields()) {
181 1
            return false;
182
        }
183
        try {
184 107
            $om = new objectmanager(connection::get_em());
185 107
            $om->create($this);
186
        } catch (\Exception $e) {
187
            exception::internal($e);
188
            return false;
189
        }
190
191 107
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
192
193 107
        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 29
    public function delete(bool $check_dependencies = true) : bool
222
    {
223 29
        if (empty($this->id)) {
224 1
            midgard_connection::get_instance()->set_error(MGD_ERR_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
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
249 27
        return true;
250
    }
251
252 108
    private function is_unique() : bool
253
    {
254 108
        $this->initialize();
255
256 108
        if (empty($this->cm->midgard['unique_fields'])) {
257 102
            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 108
    private function check_parent() : bool
310
    {
311 108
        $this->initialize();
312
313 108
        if (   empty($this->cm->midgard['parentfield'])
314 108
            || empty($this->cm->midgard['parent'])) {
315 108
            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 108
    private function check_fields() : bool
326
    {
327 108
        $this->initialize();
328
329 108
        foreach ($this->cm->fieldMappings as $name => $field) {
330 108
            if (   $field['midgard:midgard_type'] == translator::TYPE_GUID
331 108
                && !empty($this->$name)
332 108
                && !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 107
        return $this->check_upfield();
338
    }
339
340 107
    private function check_upfield() : bool
341
    {
342 107
        if (   !empty($this->id)
343 107
            && !empty($this->cm->midgard['upfield'])
344 107
            && $this->__get($this->cm->midgard['upfield']) === $this->id
345 107
            && $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 107
        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 35
    public function has_dependents() : bool
364
    {
365 35
        $this->initialize();
366
367 35
        $stat = false;
368
369 35
        if (!empty($this->cm->midgard['upfield'])) {
370 30
            $qb = connection::get_em()->createQueryBuilder();
371 30
            $qb->from(get_class($this), 'c')
372 30
                ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0')
373 30
                ->setParameter(0, $this->id)
374 30
                ->select("COUNT(c)");
375 30
            $results = (int) $qb->getQuery()->getSingleScalarResult();
376 30
            $stat = $results > 0;
377
        }
378
379 35
        if (   !$stat
380 35
            && !empty($this->cm->midgard['childtypes'])) {
381 28
            foreach ($this->cm->midgard['childtypes'] as $typename => $parentfield) {
382 28
                $qb = connection::get_em()->createQueryBuilder();
383 28
                $qb->from('midgard:' . $typename, 'c')
384 28
                    ->where('c.' . $parentfield . ' = ?0')
385 28
                    ->setParameter(0, $this->id)
386 28
                    ->select("COUNT(c)");
387
388 28
                $results = (int) $qb->getQuery()->getSingleScalarResult();
389 28
                $stat = $results > 0;
390 28
                if ($stat) {
391 3
                    break;
392
                }
393
            }
394
        }
395
396 35
        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('midgard:' . $this->cm->midgard['parent']);
449 1
            $parentclass = $this->cm->fullyQualifiedClassName($this->cm->midgard['parent']);
450 1
            $parentfield = $parent_cm->midgard['upfield'];
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
        $parameters = [
497 1
            $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('midgard: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
        $constraints = [
573 11
            'domain' => $domain,
574 11
            '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_em()->getClassMetadata('midgard:midgard_parameter')->getName());
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_em()->getClassMetadata('midgard:midgard_attachment')->getName());
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 17
    public function purge(bool $check_dependencies = true) : bool
684
    {
685 17
        if (empty($this->id)) {
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 $e) {
700 2
            exception::not_exists();
701 2
            return false;
702
        } catch (\Exception $e) {
703
            exception::internal($e);
704
            return false;
705
        }
706 17
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
707
708 17
        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 108
    public function set_guid(string $guid)
732
    {
733 108
        parent::__set('guid', $guid);
734 108
    }
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