Completed
Push — master ( fd9a8e...06aafd )
by Andreas
06:23
created

mgdobject   F

Complexity

Total Complexity 159

Size/Duplication

Total Lines 928
Duplicated Lines 0 %

Test Coverage

Coverage 86.88%

Importance

Changes 0
Metric Value
eloc 412
dl 0
loc 928
ccs 417
cts 480
cp 0.8688
rs 2
c 0
b 0
f 0
wmc 159

56 Methods

Rating   Name   Duplication   Size   Complexity  
A update() 0 19 4
A get_by_guid() 0 14 3
A __debugInfo() 0 15 4
A check_fields() 0 13 5
B is_unique() 0 60 11
A check_parent() 0 14 4
A __call() 0 6 2
B delete() 0 29 7
A __get() 0 9 4
A __set() 0 6 2
A load_parent() 0 18 6
A create() 0 24 6
A __construct() 0 7 4
A get_collection() 0 6 2
B get_by_id() 0 31 8
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 unlock() 0 3 1
A is_in_parent_tree() 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 check_upfield() 0 11 5
A emit() 0 3 1
A unapprove() 0 3 1
A get_parameter() 0 13 2
A is_approved() 0 7 2
A has_parameters() 0 3 1
B has_dependents() 0 34 6
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
A purge() 0 26 6
A get_workspace() 0 3 1
A parent() 0 3 1
A get_parent() 0 3 1
B set_parameter() 0 42 8
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
A delete_parameters() 0 3 1
A connect() 0 3 1
A is_in_tree() 0 3 1
A _list() 0 14 2
A serve_attachment() 0 3 1

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
abstract class mgdobject extends dbobject
21
{
22
    protected $metadata; // compat with mgd behavior: If the schema has no metadata, the property is present anyway
23
24
    public $action = ''; // <== does this need to do anything?
25
26
    private $collections = [];
27
28
    /**
29
     *
30
     * @param mixed $id ID or GUID
31
     */
32 135
    public function __construct($id = null)
33 31
    {
34 135
        if ($id !== null) {
35 50
            if (is_int($id)) {
36 41
                $this->get_by_id($id);
37 49
            } elseif (is_string($id)) {
38 14
                $this->get_by_guid($id);
39 13
            }
40 48
        }
41 133
    }
42
43
    /**
44
     *
45
     * @param string $classname
46
     * @return collection
47
     */
48 16
    private function get_collection($classname)
49
    {
50 16
        if (!array_key_exists($classname, $this->collections)) {
51 16
            $this->collections[$classname] = new collection($classname);
52 16
        }
53 16
        return $this->collections[$classname];
54
    }
55
56 1
    public function __debugInfo()
57
    {
58 1
        $ret = parent::__debugInfo();
59 1
        if (property_exists($this, 'metadata')) {
60 1
            $metadata = new \stdClass;
61 1
            foreach ($this->cm->getFieldNames() as $name) {
62 1
                if (strpos($name, 'metadata_') !== false) {
63 1
                    $fieldname = str_replace('metadata_', '', $name);
64 1
                    $metadata->$fieldname = $this->__get($name);
65 1
                }
66 1
            }
67 1
            $ret['metadata'] = $metadata;
68 1
        }
69
70 1
        return $ret;
71
    }
72
73 110
    public function __set($field, $value)
74
    {
75 110
        if ($field == 'guid') {
76 6
            return;
77
        }
78 110
        parent::__set($field, $value);
79 110
    }
80
81 125
    public function __get($field)
82
    {
83
        if (   $field === 'metadata'
84 125
            && $this->metadata === null
85 125
            && $this instanceof metadata_interface) {
86 102
            $this->metadata = new metadata($this);
87 102
        }
88
89 125
        return parent::__get($field);
90
    }
91
92 1
    public function __call($method, $args)
93
    {
94 1
        if ($method === 'list') {
95 1
            return $this->_list();
96
        }
97
        throw new \BadMethodCallException("Unknown method " . $method . " on " . get_class($this));
98
    }
99
100 27
    protected function load_parent(array $candidates)
101 27
    {
102 4
        foreach ($candidates as $candidate) {
103 10
            if ($this->$candidate !== null) {
104
                //Proxies become stale if the object itself is detached, so we have to re-fetch
105 4
                if (   $this->$candidate instanceof Proxy
106 4
                    && $this->$candidate->__isInitialized()) {
107
                    try {
108 1
                        $this->$candidate->get_by_id($this->$candidate->id);
109 1
                    } catch (exception $e) {
110
                        connection::log()->error('Failed to refresh parent from proxy: ' . $e->getMessage());
111
                        return null;
112 27
                    }
113 1
                }
114 4
                return $this->$candidate;
115
            }
116 3
        }
117 1
        return null;
118
    }
119
120
    /**
121
     * @param integer $id
122
     * @return boolean
123
     */
124 43
    public function get_by_id($id)
125
    {
126 43
        $entity = connection::get_em()->find(get_class($this), $id);
127
128 43
        if ($entity === null) {
129 3
            throw exception::not_exists();
130
        }
131
        // According to Doctrine documentation, proxies should be transparent, but in practice,
132
        // there will be problems if we don't force-load
133
        if (   $entity instanceof Proxy
134 42
            && !$entity->__isInitialized()) {
135
            try {
136 7
                $entity->__load();
137 7
            } catch (\Doctrine\ORM\EntityNotFoundException $e) {
138 1
                throw exception::object_purged();
139
            }
140 6
        }
141 42
        if ($entity instanceof metadata_interface && $entity->{metadata_interface::DELETED_FIELD}) {
142
            // This can happen when the "deleted" entity is still in EM's identity map
143
            throw exception::object_deleted();
144
        }
145 42
        if (empty($entity->guid)) {
146
            // This can happen when a reference proxy to a purged entity is still in EM's identity map
147 2
            throw exception::object_purged();
148 2
        }
149
150 42
        $this->populate_from_entity($entity);
151
152 42
        connection::get_em()->detach($entity);
153 42
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
154 42
        return true;
155
    }
156
157
    /**
158
     * @param string $guid
159
     * @return boolean
160
     */
161 17
    public function get_by_guid($guid)
162 1
    {
163 17
        if (!mgd_is_guid($guid)) {
164 2
            throw new \InvalidArgumentException("'$guid' is not a valid guid");
165
        }
166 15
        $entity = connection::get_em()->getRepository(get_class($this))->findOneBy(['guid' => $guid]);
167 15
        if ($entity === null) {
168
            throw exception::not_exists();
169
        }
170 15
        $this->populate_from_entity($entity);
171
172 15
        connection::get_em()->detach($entity);
173 15
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
174 15
        return true;
175 2
    }
176
177
    /**
178
     * @return boolean
179
     */
180 108
    public function create()
181
    {
182 108
        if (!empty($this->id)) {
183 2
            exception::duplicate();
184 2
            return false;
185
        }
186 108
        if (   !$this->is_unique()
187 108
            || !$this->check_parent()) {
188 3
            return false;
189 1
        }
190 108
        if (!$this->check_fields()) {
191 1
            return false;
192
        }
193
        try {
194 107
            $om = new objectmanager(connection::get_em());
195 107
            $om->create($this);
196 107
        } catch (\Exception $e) {
197
            exception::internal($e);
198
            return false;
199
        }
200
201 107
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
202
203 107
        return ($this->id != 0);
204
    }
205
206
    /**
207
     * @return boolean
208
     */
209 16
    public function update()
210
    {
211 16
        if (empty($this->id)) {
212 1
            midgard_connection::get_instance()->set_error(MGD_ERR_INTERNAL);
213 1
            return false;
214
        }
215 15
        if (!$this->check_fields()) {
216 2
            return false;
217
        }
218
        try {
219 13
            $om = new objectmanager(connection::get_em());
220 13
            $om->update($this);
221 13
        } catch (\Exception $e) {
222
            exception::internal($e);
223
            return false;
224
        }
225 13
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
226
227 13
        return true;
228
    }
229
230
    /**
231
     * @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension,
232
     * so we might consider ignoring it, too
233
     *
234
     * @return boolean
235
     */
236 29
    public function delete($check_dependencies = true)
237
    {
238 29
        if (empty($this->id)) {
239 1
            midgard_connection::get_instance()->set_error(MGD_ERR_INVALID_PROPERTY_VALUE);
240 1
            return false;
241
        }
242
        if (   $check_dependencies
243 28
            && $this->has_dependents()) {
244 4
            exception::has_dependants();
245 4
            return false;
246
        }
247 28
        if (!($this instanceof metadata_interface)) {
248 1
            exception::invalid_property_value();
249 1
            return false;
250
        }
251 27
        if ($this->{metadata_interface::DELETED_FIELD}) {
252 1
            return true;
253
        }
254
255
        try {
256 27
            $om = new objectmanager(connection::get_em());
257 27
            $om->delete($this);
258 27
        } catch (\Exception $e) {
259
            exception::internal($e);
260
            return false;
261
        }
262
263 27
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
264 27
        return true;
265
    }
266
267 108
    private function is_unique()
268
    {
269 108
        $this->initialize();
270
271 108
        if (empty($this->cm->midgard['unique_fields'])) {
272 102
            return true;
273
        }
274
275 8
        $qb = connection::get_em()->createQueryBuilder();
276 8
        $qb->from(get_class($this), 'c');
277 8
        $conditions = $qb->expr()->andX();
278 8
        if ($this->id) {
279
            $parameters = [
280
                'id' => $this->id
281
            ];
282
            $conditions->add($qb->expr()->neq('c.id', ':id'));
283
        }
284 8
        $found = false;
285 8
        foreach ($this->cm->midgard['unique_fields'] as $field) {
286 8
            if (empty($this->$field)) {
287
                //empty names automatically pass according to Midgard logic
288 2
                continue;
289
            }
290 7
            $conditions->add($qb->expr()->eq('c.' . $field, ':' . $field));
291 7
            $parameters[$field] = $this->$field;
292 7
            $found = true;
293 8
        }
294
295 8
        if (!$found) {
296 2
            return true;
297
        }
298
299 7
        if (!empty($this->cm->midgard['upfield'])) {
300
            // TODO: This needs to be changed so that value is always numeric, since this is how midgard does it
301 6
            if ($this->{$this->cm->midgard['upfield']} === null) {
302 6
                $conditions->add($qb->expr()->isNull('c.' . $this->cm->midgard['upfield']));
303 6
            } else {
304 3
                $conditions->add($qb->expr()->eq('c.' . $this->cm->midgard['upfield'], ':' . $this->cm->midgard['upfield']));
305 3
                $parameters[$this->cm->midgard['upfield']] = $this->{$this->cm->midgard['upfield']};
306
            }
307 7
        } elseif (!empty($this->cm->midgard['parentfield'])) {
308
            // TODO: This needs to be changed so that value is always numeric, since this is how midgard does it
309 4
            if ($this->{$this->cm->midgard['parentfield']} === null) {
310
                $conditions->add($qb->expr()->isNull('c.' . $this->cm->midgard['parentfield']));
311
            } else {
312 4
                $conditions->add($qb->expr()->eq('c.' . $this->cm->midgard['parentfield'], ':' . $this->cm->midgard['parentfield']));
313 4
                $parameters[$this->cm->midgard['parentfield']] = $this->{$this->cm->midgard['parentfield']};
314
            }
315 4
        }
316 7
        $qb->where($conditions)
317 7
            ->setParameters($parameters);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $parameters does not seem to be defined for all execution paths leading up to this point.
Loading history...
318
319 7
        $qb->select("count(c)");
320 7
        $count = intval($qb->getQuery()->getSingleScalarResult());
321
322 7
        if ($count !== 0) {
323 1
            exception::object_name_exists();
324 1
            return false;
325
        }
326 7
        return true;
327
    }
328
329 108
    private function check_parent()
330
    {
331 108
        $this->initialize();
332
333 108
        if (   empty($this->cm->midgard['parentfield'])
334 108
            || empty($this->cm->midgard['parent'])) {
335 108
            return true;
336
        }
337
338 8
        if (empty($this->{$this->cm->midgard['parentfield']})) {
339 1
            exception::object_no_parent();
340 1
            return false;
341
        }
342 8
        return true;
343
    }
344
345 108
    private function check_fields()
346
    {
347 108
        $this->initialize();
348
349 108
        foreach ($this->cm->fieldMappings as $name => $field) {
350 108
            if (   $field['midgard:midgard_type'] == translator::TYPE_GUID
351 108
                && !empty($this->$name)
352 108
                && !mgd_is_guid($this->$name)) {
353 2
                exception::invalid_property_value("'" . $name . "' property's value is not a guid.");
354 2
                return false;
355
            }
356 108
        }
357 107
        return $this->check_upfield();
358
    }
359
360 107
    private function check_upfield()
361
    {
362 107
        if (   !empty($this->id)
363 107
            && !empty($this->cm->midgard['upfield'])
0 ignored issues
show
Bug introduced by
Accessing midgard on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
364 107
            && $this->__get($this->cm->midgard['upfield']) === $this->id
365 107
            && $this->cm->getAssociationMapping($this->cm->midgard['upfield'])['targetEntity'] === $this->cm->getName()) {
0 ignored issues
show
Bug introduced by
The method getAssociationMapping() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. Did you maybe mean getAssociationNames()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

365
            && $this->cm->/** @scrutinizer ignore-call */ getAssociationMapping($this->cm->midgard['upfield'])['targetEntity'] === $this->cm->getName()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
366 1
            exception::tree_is_circular();
367 1
            return false;
368
        }
369
        // @todo this should be recursive
370 107
        return true;
371
    }
372
373
    public function is_in_parent_tree($root_id, $id)
374
    {
375
        return false;
376
    }
377
378
    public function is_in_tree($root_id, $id)
379
    {
380
        return false;
381
    }
382
383 35
    public function has_dependents()
384
    {
385 35
        $this->initialize();
386
387 35
        $stat = false;
388
389 35
        if (!empty($this->cm->midgard['upfield'])) {
390 30
            $qb = connection::get_em()->createQueryBuilder();
391 30
            $qb->from(get_class($this), 'c')
392 30
                ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0')
393 30
                ->setParameter(0, $this->id)
394 30
                ->select("COUNT(c)");
395 30
            $results = intval($qb->getQuery()->getSingleScalarResult());
396 30
            $stat = ($results > 0);
397 30
        }
398
399
        if (   !$stat
400 35
            && !empty($this->cm->midgard['childtypes'])) {
401 28
            foreach ($this->cm->midgard['childtypes'] as $typename => $parentfield) {
402 28
                $qb = connection::get_em()->createQueryBuilder();
403 28
                $qb->from('midgard:' . $typename, 'c')
404 28
                    ->where('c.' . $parentfield . ' = ?0')
405 28
                    ->setParameter(0, $this->id)
406 28
                    ->select("COUNT(c)");
407
408 28
                $results = intval($qb->getQuery()->getSingleScalarResult());
409 28
                $stat = ($results > 0);
410 28
                if ($stat) {
411 3
                    break;
412
                }
413 28
            }
414 28
        }
415
416 35
        return $stat;
417
    }
418
419
    public function get_parent()
420
    {
421
        return null;
422
    }
423
424
    /**
425
     * This function is called list() in Midgard, but that doesn't work in plain PHP
426
     *
427
     * @return array
428
     */
429 1
    private function _list()
430
    {
431 1
        $this->initialize();
432
433 1
        if (!empty($this->cm->midgard['upfield'])) {
434 1
            $qb = connection::get_em()->createQueryBuilder();
435 1
            $qb->from(get_class($this), 'c')
436 1
                ->where('c.' . $this->cm->midgard['upfield'] . ' = ?0')
437 1
                ->setParameter(0, $this->id)
438 1
                ->select("c");
439 1
            return $qb->getQuery()->getResult();
440
        }
441
442
        return [];
443
    }
444
445
    /**
446
     * This should return child objects, but only if they are of a different type
447
     * For all other input, an empty array is returned
448
     * (not implemented yet)
449
     *
450
     * @param string $classname
451
     * @return array
452
     */
453
    public function list_children($classname)
454
    {
455
        return [];
456
    }
457
458
    /**
459
     * @param string $path
460
     * @return boolean
461
     */
462 1
    public function get_by_path($path)
463
    {
464 1
        $parts = explode('/', trim($path, '/'));
465 1
        if (empty($parts)) {
466
            return false;
467
        }
468 1
        $this->initialize();
469
470 1
        if (count($this->cm->midgard['unique_fields']) != 1) {
471
            return false;
472
        }
473
474 1
        $field = $this->cm->midgard['unique_fields'][0];
475
476 1
        if (!empty($this->cm->midgard['parent'])) {
477 1
            $parent_cm = connection::get_em()->getClassMetadata('midgard:' . $this->cm->midgard['parent']);
478 1
            $parentclass = $this->cm->fullyQualifiedClassName($this->cm->midgard['parent']);
479 1
            $parentfield = $parent_cm->midgard['upfield'];
480 1
            $upfield = $this->cm->midgard['parentfield'];
481 1
        } elseif (!empty($this->cm->midgard['upfield'])) {
482 1
            $parentclass = get_class($this);
483 1
            $upfield = $this->cm->midgard['upfield'];
484 1
            $parentfield = $upfield;
485 1
        } else {
486
            return false;
487
        }
488
489 1
        $name = array_pop($parts);
490 1
        $up = 0;
491 1
        foreach ($parts as $part) {
492 1
            $qb = $this->get_uniquefield_query($parentclass, $field, $part, $parentfield, $up);
493 1
            $qb->select("c.id");
494 1
            $up = intval($qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR));
495 1
            if ($up === 0) {
496 1
                exception::not_exists();
497 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...
498 1
                $this->set_guid('');
499 1
                return false;
500
            }
501 1
        }
502
503 1
        $qb = $this->get_uniquefield_query(get_class($this), $field, $name, $upfield, $up);
504 1
        $qb->select("c");
505
506 1
        $entity = $qb->getQuery()->getOneOrNullResult();
507
508 1
        if ($entity === null) {
509 1
            exception::not_exists();
510 1
            $this->id = 0;
511 1
            $this->set_guid('');
512 1
            return false;
513
        }
514 1
        $this->populate_from_entity($entity);
515
516 1
        return true;
517
    }
518
519
    /**
520
     * @return QueryBuilder
521
     */
522 1
    protected function get_uniquefield_query($classname, $field, $part, $upfield, $up)
523
    {
524 1
        $qb = connection::get_em()->createQueryBuilder();
525 1
        $qb->from($classname, 'c');
526 1
        $conditions = $qb->expr()->andX();
527 1
        $conditions->add($qb->expr()->eq('c.' . $field, ':' . $field));
528
        $parameters = [
529
            $field => $part
530 1
        ];
531
532 1
        if (empty($up)) {
533
            // If the database was created by Midgard, it might contain 0 instead of NULL, so...
534 1
            $empty_conditions = $qb->expr()->orX()
535 1
                ->add($qb->expr()->isNull('c.' . $upfield))
536 1
                ->add($qb->expr()->eq('c.' . $upfield, '0'));
537 1
            $conditions->add($empty_conditions);
538 1
        } else {
539 1
            $conditions->add($qb->expr()->eq('c.' . $upfield, ':' . $upfield));
540 1
            $parameters[$upfield] = $up;
541
        }
542
543 1
        $qb->where($conditions)
544 1
            ->setParameters($parameters);
545
546 1
        return $qb;
547
    }
548
549
    /**
550
     * @return boolean
551
     */
552
    public function parent()
553
    {
554
        return false;
555
    }
556
557
    /**
558
     * @return boolean
559
     */
560 1
    public function has_parameters()
561
    {
562 1
        return !$this->get_collection('midgard_parameter')->is_empty($this->guid);
563
    }
564
565 4
    public function list_parameters($domain = false)
566
    {
567 4
        $constraints = [];
568 4
        if ($domain) {
569 1
            $constraints = ["domain" => $domain];
570 1
        }
571
572 4
        return $this->get_collection('midgard_parameter')->find($this->guid, $constraints);
573
    }
574
575 3
    public function find_parameters(array $constraints = [])
576
    {
577 3
        return $this->get_collection('midgard_parameter')->find($this->guid, $constraints);
578
    }
579
580
    /**
581
     * @param array $constraints
582
     * @return number
583
     */
584 1
    public function delete_parameters(array $constraints = [])
585
    {
586 1
        return $this->get_collection('midgard_parameter')->delete($this->guid, $constraints);
587
    }
588
589
    /**
590
     * @param array $constraints
591
     * @return number
592
     */
593 1
    public function purge_parameters(array $constraints = [])
594
    {
595 1
        return $this->get_collection('midgard_parameter')->purge($this->guid, $constraints);
596
    }
597
598 2
    public function get_parameter($domain, $name)
599
    {
600 2
        if (!$this->guid) {
601 1
            return false;
602
        }
603 2
        $qb = connection::get_em()->createQueryBuilder();
604
        $qb
605 2
            ->select('c.value')
606 2
            ->from('midgard:midgard_parameter', 'c')
607 2
            ->where('c.domain = :domain AND c.name = :name AND c.parentguid = :parentguid')
608 2
            ->setParameters(['domain' => $domain, 'name' => $name, 'parentguid' => $this->guid]);
609
610 2
        return $qb->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
611
    }
612
613
    /**
614
     * @param string $domain
615
     * @param string $name
616
     * @param mixed $value
617
     * @return boolean
618
     */
619 11
    public function set_parameter($domain, $name, $value)
620
    {
621
        $constraints = [
622 11
            'domain' => $domain,
623 11
            'name' => $name,
624 11
        ];
625 11
        $params = $this->get_collection('midgard_parameter')->find($this->guid, $constraints);
626
627
        // check value
628 11
        if ($value === false || $value === null || $value === "") {
629 2
            if (count($params) == 0) {
630 1
                exception::not_exists();
631 1
                return false;
632
            }
633 2
            foreach ($params as $param) {
634 2
                $stat = $param->delete();
635 2
            }
636 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 633. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
637
        }
638
639 11
        $om = new objectmanager(connection::get_em());
640
        try {
641
            // create new
642 11
            if (count($params) == 0) {
643 11
                $parameter = $om->new_instance(connection::get_em()->getClassMetadata('midgard:midgard_parameter')->getName());
644 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...
645 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...
646 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...
647 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...
648 11
                $om->create($parameter);
649 11
            }
650
            // use existing
651
            else {
652 1
                $parameter = array_shift($params);
653 1
                $parameter->value = $value;
654 1
                $om->update($parameter);
655
            }
656 11
            midgard_connection::get_instance()->set_error(MGD_ERR_OK);
657 11
            return true;
658
        } catch (\Exception $e) {
659
            exception::internal($e);
660
            return false;
661
        }
662
    }
663
664
    /**
665
     * The signature is a little different from original, because Doctrine doesn't support func_get_args() in proxies
666
     */
667 2
    public function parameter($domain, $name, $value = '__UNINITIALIZED__')
668
    {
669 2
        if ($value === '__UNINITIALIZED__') {
670 1
            return $this->get_parameter($domain, $name);
671
        }
672 2
        return $this->set_parameter($domain, $name, $value);
673
    }
674
675
    /**
676
     * @return boolean
677
     */
678 1
    public function has_attachments()
679
    {
680 1
        return !$this->get_collection('midgard_attachment')->is_empty($this->guid);
681
    }
682
683 2
    public function list_attachments()
684
    {
685 2
        return $this->get_collection('midgard_attachment')->find($this->guid, []);
686
    }
687
688
    public function find_attachments(array $constraints = [])
689
    {
690
        return $this->get_collection('midgard_attachment')->find($this->guid, $constraints);
691
    }
692
693
    /**
694
     * @param array $constraints
695
     * @return number
696
     */
697
    public function delete_attachments(array $constraints = [])
698
    {
699
        return $this->get_collection('midgard_attachment')->delete($this->guid, $constraints);
700
    }
701
702
    /**
703
     *
704
     * @param array $constraints
705
     * @param boolean $delete_blob
706
     * @return boolean False if one or more attachments couldn't be deleted
707
     * @todo Implement delete_blob & return value
708
     */
709
    public function purge_attachments(array $constraints = [], $delete_blob = true)
710
    {
711
        return $this->get_collection('midgard_attachment')->purge($this->guid, $constraints);
712
    }
713
714 3
    public function create_attachment($name, $title = '', $mimetype = '')
715
    {
716 3
        $existing = $this->get_collection('midgard_attachment')->find($this->guid, ['name' => $name]);
717 3
        if (count($existing) > 0) {
718 1
            exception::object_name_exists();
719 1
            return null;
720
        }
721 3
        $om = new objectmanager(connection::get_em());
722 3
        $att = $om->new_instance(connection::get_em()->getClassMetadata('midgard:midgard_attachment')->getName());
723
724 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...
725 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...
726 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...
727 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...
728
        try {
729 3
            $om->create($att);
730 3
            midgard_connection::get_instance()->set_error(MGD_ERR_OK);
731 3
            return $att;
732
        } catch (\Exception $e) {
733
            exception::internal($e);
734
            return null;
735
        }
736
    }
737
738
    /**
739
     * @return boolean
740
     */
741
    public static function serve_attachment($guid)
742
    {
743
        return false;
744
    }
745
746
    /**
747
     * @todo: Tests indicate that $check_dependencies is ignored in the mgd2 extension,
748
     * so we might consider ignoring it, too
749
     * @return boolean
750
     */
751 17
    public function purge($check_dependencies = true)
752
    {
753 17
        if (empty($this->id)) {
754
            // This usually means that the object has been purged already
755
            exception::not_exists();
756
            return false;
757
        }
758
        if (   $check_dependencies
759 17
            && $this->has_dependents()) {
760 2
            exception::has_dependants();
761 2
            return false;
762
        }
763
764
        try {
765 17
            $om = new objectmanager(connection::get_em());
766 17
            $om->purge($this);
767 17
        } catch (\Doctrine\ORM\EntityNotFoundException $e) {
768 2
            exception::not_exists();
769 2
            return false;
770
        } catch (\Exception $e) {
771
            exception::internal($e);
772
            return false;
773
        }
774 17
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
775
776 17
        return true;
777
    }
778
779
    /**
780
     * @return boolean
781
     */
782 2
    public static function undelete($guid)
783
    {
784 2
        return \midgard_object_class::undelete($guid);
785
    }
786
787
    /**
788
     * @return boolean
789
     */
790
    public function connect($signal, $callback, $user_data)
791
    {
792
        return false;
793
    }
794
795
    /**
796
     * @return \midgard_query_builder
797
     */
798 1
    public static function new_query_builder()
799
    {
800 1
        return new \midgard_query_builder(get_called_class());
801
    }
802
803
    /**
804
     *
805
     * @param string $field
806
     * @param mixed $value
807
     * @return \midgard_collector
808
     */
809 1
    public static function new_collector($field, $value)
810
    {
811 1
        return new \midgard_collector(get_called_class(), $field, $value);
812
    }
813
814
    /**
815
     * @return \midgard_reflection_property
816
     */
817
    public static function new_reflection_property()
818
    {
819
        return new \midgard_reflection_property(get_called_class());
820
    }
821
822 108
    public function set_guid($guid)
823
    {
824 108
        parent::__set('guid', $guid);
825 108
    }
826
827
    /**
828
     * @return boolean
829
     */
830
    public function emit($signal)
831
    {
832
        return false;
833
    }
834
835
    /**
836
     * Helper for managing the isapproved and islocked metadata properties
837
     *
838
     * @param string $action the property to manage (either approve or lock)
839
     * @param bool $value
840
     * @return boolean
841
     */
842 8
    private function manage_meta_property($action, $value)
843
    {
844 8
        if (!($this instanceof metadata_interface)) {
845 4
            exception::no_metadata();
846 4
            return false;
847
        }
848 4
        $user = connection::get_user();
849 4
        if ($user === null) {
850 4
            exception::access_denied();
851 4
            return false;
852
        }
853 4
        if ($action == 'lock') {
854 2
            $flag = 'islocked';
855 4
        } elseif ($action == 'approve') {
856 2
            $flag = 'isapproved';
857 2
        } else {
858
            throw new exception('Unsupported action ' . $action);
859
        }
860
        // same val
861 4
        if ($this->__get('metadata')->$flag === $value) {
862 3
            return false;
863
        }
864 4
        if ($value === false) {
865 2
            $action = 'un' . $action;
866 2
        }
867
868 4
        if ($this->id) {
869
            try {
870 4
                $om = new objectmanager(connection::get_em());
871 4
                $om->{$action}($this);
872 4
            } catch (\Exception $e) {
873
                exception::internal($e);
874
                return false;
875
            }
876 4
        }
877 4
        midgard_connection::get_instance()->set_error(MGD_ERR_OK);
878
879 4
        return true;
880
    }
881
882
    /**
883
     * @return boolean
884
     */
885 3
    public function approve()
886
    {
887 3
        return $this->manage_meta_property("approve", true);
888
    }
889
890
    /**
891
     * @return boolean
892
     */
893 2
    public function is_approved()
894
    {
895 2
        if (!($this instanceof metadata_interface)) {
896
            exception::no_metadata();
897
            return false;
898
        }
899 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...
900
    }
901
902
    /**
903
     * @return boolean
904
     */
905 2
    public function unapprove()
906
    {
907 2
        return $this->manage_meta_property("approve", false);
908
    }
909
910
    /**
911
     * @return boolean
912
     */
913 3
    public function lock()
914
    {
915 3
        if ($this->is_locked()) {
916 1
            exception::object_is_locked();
917 1
            return false;
918
        }
919 3
        return $this->manage_meta_property("lock", true);
920
    }
921
922
    /**
923
     * @return boolean
924
     */
925 3
    public function is_locked()
926
    {
927 3
        if (!($this instanceof metadata_interface)) {
928 1
            exception::no_metadata();
929 1
            return false;
930
        }
931 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...
932
    }
933
934
    /**
935
     * @return boolean
936
     */
937 2
    public function unlock()
938
    {
939 2
        return $this->manage_meta_property("lock", false);
940
    }
941
942
    /**
943
     * @return boolean
944
     */
945
    public function get_workspace()
946
    {
947
        return false;
948
    }
949
}
950