Completed
Push — master ( 2ca52b...7d8595 )
by Joschi
02:51
created

AbstractObject::getDomainProperty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\Domain
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Apparat\Object\Domain\Model\Object;
38
39
use Apparat\Kernel\Ports\Kernel;
40
use Apparat\Object\Domain\Model\Author\AuthorInterface;
41
use Apparat\Object\Domain\Model\Path\RepositoryPath;
42
use Apparat\Object\Domain\Model\Path\RepositoryPathInterface;
43
use Apparat\Object\Domain\Model\Properties\AbstractDomainProperties;
44
use Apparat\Object\Domain\Model\Properties\GenericPropertiesInterface;
45
use Apparat\Object\Domain\Model\Properties\InvalidArgumentException as PropertyInvalidArgumentException;
46
use Apparat\Object\Domain\Model\Properties\MetaProperties;
47
use Apparat\Object\Domain\Model\Properties\ProcessingInstructions;
48
use Apparat\Object\Domain\Model\Properties\Relations;
49
use Apparat\Object\Domain\Model\Properties\SystemProperties;
50
use Apparat\Object\Domain\Repository\Service;
51
52
/**
53
 * Abstract object
54
 *
55
 * @package Apparat\Object
56
 * @subpackage Apparat\Object\Domain
57
 */
58
abstract class AbstractObject implements ObjectInterface
59
{
60
    /**
61
     * Clean state
62
     *
63
     * @var int
64
     */
65
    const STATE_CLEAN = 0;
66
    /**
67
     * Dirty state
68
     *
69
     * @var int
70
     */
71
    const STATE_DIRTY = 1;
72
    /**
73
     * Mutated state
74
     *
75
     * @var int
76
     */
77
    const STATE_MUTATED = 2;
78
    /**
79
     * Published state
80
     *
81
     * @var int
82
     */
83
    const STATE_PUBLISHED = 4;
84
    /**
85
     * System properties
86
     *
87
     * @var SystemProperties
88
     */
89
    protected $systemProperties;
90
    /**
91
     * Meta properties
92
     *
93
     * @var MetaProperties
94
     */
95
    protected $metaProperties;
96
    /**
97
     * Domain properties
98
     *
99
     * @var AbstractDomainProperties
100
     */
101
    protected $domainProperties;
102
    /**
103
     * Object payload
104
     *
105
     * @var string
106
     */
107
    protected $payload;
108
    /**
109
     * Repository path
110
     *
111
     * @var RepositoryPathInterface
112
     */
113
    protected $path;
114
    /**
115
     * Domain property collection class
116
     *
117
     * @var string
118
     */
119
    protected $domainPropertyCClass = AbstractDomainProperties::class;
120
    /**
121
     * Object relations
122
     *
123
     * @var Relations
124
     */
125
    protected $relations;
126
    /**
127
     * Processing instructions
128
     *
129
     * @var ProcessingInstructions
130
     */
131
    protected $processingInstructions;
132
    /**
133
     * Latest revision index
134
     *
135
     * @var Revision
136
     */
137
    protected $latestRevision;
138
    /**
139
     * Object state
140
     *
141
     * @var int
142
     */
143
    protected $state = self::STATE_CLEAN;
144
    /**
145
     * Property collection states
146
     *
147
     * @var array
148
     */
149
    protected $collectionStates = [];
150
151
    /**
152
     * Object constructor
153
     *
154
     * @param string $payload Object payload
155
     * @param array $propertyData Property data
156
     * @param RepositoryPathInterface $path Object repository path
157
     */
158 21
    public function __construct($payload = '', array $propertyData = [], RepositoryPathInterface $path = null)
159
    {
160
        // If the domain property collection class is invalid
161 21
        if (!$this->domainPropertyCClass
162 21
            || !class_exists($this->domainPropertyCClass)
163 21
            || !(new \ReflectionClass($this->domainPropertyCClass))->isSubclassOf(AbstractDomainProperties::class)
164
        ) {
165 1
            throw new PropertyInvalidArgumentException(
166
                sprintf(
167 1
                    'Invalid domain property collection class "%s"',
168 1
                    $this->domainPropertyCClass
169
                ),
170 1
                PropertyInvalidArgumentException::INVALID_DOMAIN_PROPERTY_COLLECTION_CLASS
171
            );
172
        }
173
174
        // Right after instantiation it's always the current revision
175 20
        $this->path = $path->setRevision(Revision::current());
0 ignored issues
show
Bug introduced by
It seems like $path is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
176
177
        // Load the current revision data
178 20
        $this->loadRevisionData($payload, $propertyData);
179
180
        // Save the latest revision index
181 18
        $this->latestRevision = $this->getRevision();
182 18
    }
183
184
    /**
185
     * Load object revision data
186
     *
187
     * @param string $payload Object payload
188
     * @param array $propertyData Property data
189
     */
190 20
    protected function loadRevisionData($payload = '', array $propertyData = [])
191
    {
192 20
        $this->payload = $payload;
193
194
        // Instantiate the system properties
195 20
        $systemPropertyData = (empty($propertyData[SystemProperties::COLLECTION]) ||
196 20
            !is_array(
197 20
                $propertyData[SystemProperties::COLLECTION]
198 20
            )) ? [] : $propertyData[SystemProperties::COLLECTION];
199 20
        $this->systemProperties = Kernel::create(SystemProperties::class, [$systemPropertyData, $this]);
200
201
        // Instantiate the meta properties
202 19
        $metaPropertyData = (empty($propertyData[MetaProperties::COLLECTION]) ||
203 18
            !is_array(
204 19
                $propertyData[MetaProperties::COLLECTION]
205 19
            )) ? [] : $propertyData[MetaProperties::COLLECTION];
206
        /** @var MetaProperties $metaPropertyCollection */
207 19
        $metaPropertyCollection = Kernel::create(MetaProperties::class, [$metaPropertyData, $this]);
208 18
        $this->setMetaProperties($metaPropertyCollection, true);
209
210
        // Instantiate the domain properties
211 18
        $domainPropertyData = (empty($propertyData[AbstractDomainProperties::COLLECTION]) ||
212 17
            !is_array(
213 18
                $propertyData[AbstractDomainProperties::COLLECTION]
214 18
            )) ? [] : $propertyData[AbstractDomainProperties::COLLECTION];
215
        /** @var AbstractDomainProperties $domainPropertyCollection */
216 18
        $domainPropertyCollection = Kernel::create($this->domainPropertyCClass, [$domainPropertyData, $this]);
217 18
        $this->setDomainProperties($domainPropertyCollection, true);
218
219
        // Instantiate the processing instructions
220 18
        $procInstData = (empty($propertyData[ProcessingInstructions::COLLECTION]) ||
221 14
            !is_array(
222 18
                $propertyData[ProcessingInstructions::COLLECTION]
223 18
            )) ? [] : $propertyData[ProcessingInstructions::COLLECTION];
224
        /** @var ProcessingInstructions $procInstCollection */
225 18
        $procInstCollection = Kernel::create(ProcessingInstructions::class, [$procInstData, $this]);
226 18
        $this->setProcessingInstructions($procInstCollection, true);
227
228
        // Instantiate the object relations
229 18
        $relationData = (empty($propertyData[Relations::COLLECTION]) ||
230 14
            !is_array(
231 18
                $propertyData[Relations::COLLECTION]
232 18
            )) ? [] : $propertyData[Relations::COLLECTION];
233
        /** @var Relations $relationCollection */
234 18
        $relationCollection = Kernel::create(Relations::class, [$relationData, $this]);
235 18
        $this->setRelations($relationCollection, true);
236
237
        // Reset the object state to clean
238 18
        $this->state = self::STATE_CLEAN;
239 18
    }
240
241
    /**
242
     * Set the meta properties collection
243
     *
244
     * @param MetaProperties $metaProperties Meta property collection
245
     * @param bool $overwrite Overwrite the existing collection (if present)
246
     */
247 18
    protected function setMetaProperties(MetaProperties $metaProperties, $overwrite = false)
248
    {
249 18
        $this->metaProperties = $metaProperties;
250 18
        $metaPropertiesState = spl_object_hash($this->metaProperties);
251
252
        // If the meta property collection state has changed
253 18
        if (!$overwrite
254 18
            && !empty($this->collectionStates[MetaProperties::COLLECTION])
255 18
            && ($metaPropertiesState !== $this->collectionStates[MetaProperties::COLLECTION])
256
        ) {
257
            // Flag this object as mutated
258 1
            $this->setMutatedState();
259
        }
260
261 18
        $this->collectionStates[MetaProperties::COLLECTION] = $metaPropertiesState;
262 18
    }
263
264
    /**
265
     * Set the object state to mutated
266
     */
267 3
    protected function setMutatedState()
268
    {
269
        // If this object is not in mutated state yet
270 3
        if (!($this->state & self::STATE_MUTATED) && !$this->isDraft()) {
271
            // TODO: Send signal
272 3
            $this->convertToDraft();
273
        }
274
275
        // Enable the mutated (and dirty) state
276 3
        $this->state |= (self::STATE_DIRTY | self::STATE_MUTATED);
277 3
    }
278
279
    /**
280
     * Return the object draft mode
281
     *
282
     * @return boolean Object draft mode
283
     */
284 4
    public function isDraft()
285
    {
286 4
        return $this->systemProperties->isDraft() || $this->isPublished();
287
    }
288
289
    /**
290
     * Set the domain properties collection
291
     *
292
     * @param GenericPropertiesInterface $domainProperties Domain property collection
293
     * @param bool $overwrite Overwrite the existing collection (if present)
294
     */
295 18
    protected function setDomainProperties(GenericPropertiesInterface $domainProperties, $overwrite = false)
296
    {
297 18
        $this->domainProperties = $domainProperties;
0 ignored issues
show
Documentation Bug introduced by
$domainProperties is of type object<Apparat\Object\Do...ricPropertiesInterface>, but the property $domainProperties was declared to be of type object<Apparat\Object\Do...stractDomainProperties>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
298 18
        $domainPropertiesState = spl_object_hash($this->domainProperties);
299
300
        // If the domain property collection state has changed
301 18
        if (!$overwrite
302 18
            && !empty($this->collectionStates[AbstractDomainProperties::COLLECTION])
303 18
            && ($domainPropertiesState !== $this->collectionStates[AbstractDomainProperties::COLLECTION])
304
        ) {
305
            // Flag this object as mutated
306 1
            $this->setMutatedState();
307
        }
308
309 18
        $this->collectionStates[AbstractDomainProperties::COLLECTION] = $domainPropertiesState;
310 18
    }
311
312
    /**
313
     * Set the processing instruction collection
314
     *
315
     * @param GenericPropertiesInterface $processingInstructions Processing instruction collection
316
     * @param bool $overwrite Overwrite the existing collection (if present)
317
     */
318 18
    protected function setProcessingInstructions(GenericPropertiesInterface $processingInstructions, $overwrite = false)
319
    {
320 18
        $this->processingInstructions = $processingInstructions;
0 ignored issues
show
Documentation Bug introduced by
$processingInstructions is of type object<Apparat\Object\Do...ricPropertiesInterface>, but the property $processingInstructions was declared to be of type object<Apparat\Object\Do...ProcessingInstructions>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
321 18
        $processingInstructionsState = spl_object_hash($this->processingInstructions);
322
323
        // If the domain property collection state has changed
324 18
        if (!$overwrite
325 18
            && !empty($this->collectionStates[ProcessingInstructions::COLLECTION])
326 18
            && ($processingInstructionsState !== $this->collectionStates[ProcessingInstructions::COLLECTION])
327
        ) {
328
            // Flag this object as dirty
329 1
            $this->setDirtyState();
330
        }
331
332 18
        $this->collectionStates[ProcessingInstructions::COLLECTION] = $processingInstructionsState;
333 18
    }
334
335
    /**
336
     * Set the object state to dirty
337
     */
338 1
    protected function setDirtyState()
339
    {
340
        // If this object is not in dirty state yet
341 1
        if (!($this->state & self::STATE_DIRTY)) {
342
            // TODO: Send signal
343
        }
344
345
        // Enable the dirty state
346 1
        $this->state |= self::STATE_DIRTY;
347 1
    }
348
349
    /**
350
     * Set the object state to published
351
     */
352 1
    protected function setPublishedState()
353
    {
354
        // If this object is not in dirty state yet
355 1
        if (!($this->state & self::STATE_PUBLISHED)) {
356
            // TODO: Send signal
357
        }
358
359
        // Enable the dirty state
360 1
        $this->state |= (self::STATE_DIRTY | self::STATE_PUBLISHED);
361 1
    }
362
363
    /**
364
     * Set the relations collection
365
     *
366
     * @param Relations $relations Relations collection
367
     * @param bool $overwrite Overwrite the existing collection (if present)
368
     */
369 18
    protected function setRelations(Relations $relations, $overwrite = false)
370
    {
371 18
        $this->relations = $relations;
372 18
        $relationsState = spl_object_hash($this->relations);
373
374
        // If the domain property collection state has changed
375 18
        if (!$overwrite
376 18
            && !empty($this->collectionStates[Relations::COLLECTION])
377 18
            && ($relationsState !== $this->collectionStates[Relations::COLLECTION])
378
        ) {
379
            // Flag this object as dirty
380
            $this->setDirtyState();
381
        }
382
383 18
        $this->collectionStates[Relations::COLLECTION] = $relationsState;
384 18
    }
385
386
    /**
387
     * Return the object revision
388
     *
389
     * @return Revision Object revision
390
     */
391 18
    public function getRevision()
392
    {
393 18
        return $this->systemProperties->getRevision();
394
    }
395
396
    /**
397
     * Return whether the object is in mutated state
398
     *
399
     * @return boolean Mutated state
400
     */
401 3
    public function isMutated()
402
    {
403 3
        return !!($this->state & self::STATE_MUTATED);
404
    }
405
406
    /**
407
     * Return whether the object is in published state
408
     *
409
     * @return boolean Published state
410
     */
411 4
    public function isPublished()
412
    {
413 4
        return !!($this->state & self::STATE_PUBLISHED);
414
    }
415
416
    /**
417
     * Use a specific object revision
418
     *
419
     * @param Revision $revision Revision to be used
420
     * @return ObjectInterface Object
421
     * @throws OutOfBoundsException If the requested revision is invalid
422
     */
423 17
    public function useRevision(Revision $revision)
424
    {
425 17
        $isCurrentRevision = false;
426
427
        // If the requested revision is invalid
428 17
        if (!$revision->isCurrent() &&
429 17
            (($revision->getRevision() < 1) || ($revision->getRevision() > $this->latestRevision->getRevision()))
430
        ) {
431
            throw new OutOfBoundsException(sprintf('Invalid object revision "%s"', $revision->getRevision()),
432
                OutOfBoundsException::INVALID_OBJECT_REVISION);
433
        }
434
435
        // If the current revision got requested
436 17
        if ($revision->isCurrent()) {
437 17
            $isCurrentRevision = true;
438 17
            $revision = $this->latestRevision;
439
        }
440
441
        // If the requested revision is not already used
442 17
        if ($revision != $this->getRevision()) {
443
            /** @var ManagerInterface $objectManager */
444
            $objectManager = Kernel::create(Service::class)->getObjectManager();
445
446
            // Load the requested object revision resource
447
            /** @var Revision $newRevision */
448
            $newRevision = $isCurrentRevision ? Revision::current() : $revision;
449
            /** @var RepositoryPath $newRevisionPath */
450
            $newRevisionPath = $this->path->setRevision($newRevision);
451
            $revisionResource = $objectManager->loadObject($newRevisionPath);
452
453
            // Load the revision resource data
454
            $this->loadRevisionData($revisionResource->getPayload(), $revisionResource->getPropertyData());
455
456
            // Set the current revision path
457
            $this->path = $newRevisionPath;
458
        }
459
460 17
        return $this;
461
    }
462
463
    /**
464
     * Return the object ID
465
     *
466
     * @return Id Object ID
467
     */
468 5
    public function getId()
469
    {
470 5
        return $this->systemProperties->getId();
471
    }
472
473
    /**
474
     * Return the object type
475
     *
476
     * @return Type Object type
477
     */
478 1
    public function getType()
479
    {
480 1
        return $this->systemProperties->getType();
481
    }
482
483
    /**
484
     * Return the creation date & time
485
     *
486
     * @return \DateTimeImmutable Creation date & time
487
     */
488 1
    public function getCreated()
489
    {
490 1
        return $this->systemProperties->getCreated();
491
    }
492
493
    /**
494
     * Return the publication date & time
495
     *
496
     * @return \DateTimeImmutable|null Publication date & time
497
     */
498 1
    public function getPublished()
499
    {
500 1
        return $this->systemProperties->getPublished();
501
    }
502
503
    /**
504
     * Return the object hash
505
     *
506
     * @return string Object hash
507
     */
508 1
    public function getHash()
509
    {
510 1
        return $this->systemProperties->getHash();
511
    }
512
513
    /**
514
     * Return the object title
515
     *
516
     * @return string Object title
517
     */
518 1
    public function getTitle()
519
    {
520 1
        return $this->metaProperties->getTitle();
521
    }
522
523
    /**
524
     * Set the title
525
     *
526
     * @param string $title Title
527
     * @return ObjectInterface Self reference
528
     */
529 1
    public function setTitle($title)
530
    {
531 1
        $this->setMetaProperties($this->metaProperties->setTitle($title));
532 1
        return $this;
533
    }
534
535
    /**
536
     * Return the object slug
537
     *
538
     * @return string Object slug
539
     */
540 1
    public function getSlug()
541
    {
542 1
        return $this->metaProperties->getSlug();
543
    }
544
545
    /**
546
     * Set the slug
547
     *
548
     * @param string $slug Slug
549
     * @return ObjectInterface Self reference
550
     */
551 1
    public function setSlug($slug)
552
    {
553 1
        $this->setMetaProperties($this->metaProperties->setSlug($slug));
554 1
        return $this;
555
    }
556
557
    /**
558
     * Return the object description
559
     *
560
     * @return string Object description
561
     */
562 2
    public function getDescription()
563
    {
564 2
        return $this->metaProperties->getDescription();
565
    }
566
567
    /**
568
     * Set the description
569
     *
570
     * @param string $description Description
571
     * @return ObjectInterface Self reference
572
     */
573 1
    public function setDescription($description)
574
    {
575 1
        $this->setMetaProperties($this->metaProperties->setDescription($description));
576 1
        return $this;
577
    }
578
579
    /**
580
     * Return the object abstract
581
     *
582
     * @return string Object abstract
583
     */
584 2
    public function getAbstract()
585
    {
586 2
        return $this->metaProperties->getAbstract();
587
    }
588
589
    /**
590
     * Set the abstract
591
     *
592
     * @param string $abstract Abstract
593
     * @return ObjectInterface Self reference
594
     */
595 1
    public function setAbstract($abstract)
596
    {
597 1
        $this->setMetaProperties($this->metaProperties->setAbstract($abstract));
598 1
        return $this;
599
    }
600
601
    /**
602
     * Return all object keywords
603
     *
604
     * @return array Object keywords
605
     */
606 2
    public function getKeywords()
607
    {
608 2
        return $this->metaProperties->getKeywords();
609
    }
610
611
    /**
612
     * Set the keywords
613
     *
614
     * @param array $keywords Keywords
615
     * @return ObjectInterface Self reference
616
     */
617 1
    public function setKeywords(array $keywords)
618
    {
619 1
        $this->setMetaProperties($this->metaProperties->setKeywords($keywords));
620 1
        return $this;
621
    }
622
623
    /**
624
     * Return all object categories
625
     *
626
     * @return array Object categories
627
     */
628 2
    public function getCategories()
629
    {
630 2
        return $this->metaProperties->getCategories();
631
    }
632
633
    /**
634
     * Set the categories
635
     *
636
     * @param array $categories Categories
637
     * @return ObjectInterface Self reference
638
     */
639 1
    public function setCategories(array $categories)
640
    {
641 1
        $this->setMetaProperties($this->metaProperties->setCategories($categories));
642 1
        return $this;
643
    }
644
645
    /**
646
     * Return all object authors
647
     *
648
     * @return AuthorInterface[] Authors
649
     */
650 2
    public function getAuthors()
651
    {
652 2
        return $this->metaProperties->getAuthors();
653
    }
654
655
    /**
656
     * Add an object author
657
     *
658
     * @param AuthorInterface $author Author
659
     * @return ObjectInterface Self reference
660
     */
661 1
    public function addAuthor(AuthorInterface $author)
662
    {
663 1
        $authors = $this->metaProperties->getAuthors();
664 1
        $authors[] = $author;
665 1
        $this->metaProperties->setAuthors($authors);
666 1
        return $this;
667
    }
668
669
    /**
670
     * Return the object repository path
671
     *
672
     * @return RepositoryPathInterface Object repository path
673
     */
674 18
    public function getRepositoryPath()
675
    {
676 18
        return $this->path;
677
    }
678
679
    /**
680
     * Return the object property data
681
     *
682
     * @return array Object property data
683
     */
684 5
    public function getPropertyData()
685
    {
686 5
        $propertyData = array_filter([
687 5
            SystemProperties::COLLECTION => $this->systemProperties->toArray(),
688 5
            MetaProperties::COLLECTION => $this->metaProperties->toArray(),
689 5
            AbstractDomainProperties::COLLECTION => $this->domainProperties->toArray(),
690 5
            ProcessingInstructions::COLLECTION => $this->processingInstructions->toArray(),
691 5
            Relations::COLLECTION => $this->relations->toArray(),
692 5
        ], function (array $collection) {
693 5
            return (boolean)count($collection);
694 5
        });
695
696 5
        return $propertyData;
697
    }
698
699
    /**
700
     * Return the object payload
701
     *
702
     * @return string Object payload
703
     */
704 2
    public function getPayload()
705
    {
706 2
        return $this->payload;
707
    }
708
709
    /**
710
     * Set the payload
711
     *
712
     * @param string $payload Payload
713
     * @return ObjectInterface Self reference
714
     */
715 1
    public function setPayload($payload)
716
    {
717
        // If the payload is changed
718 1
        if ($payload !== $this->payload) {
719 1
            $this->setMutatedState();
720
        }
721
722 1
        $this->payload = $payload;
723 1
        return $this;
724
    }
725
726
    /**
727
     * Return the absolute object URL
728
     *
729
     * @return string
730
     */
731 4
    public function getAbsoluteUrl()
732
    {
733 4
        return getenv('APPARAT_BASE_URL').ltrim($this->path->getRepository()->getUrl(), '/').strval($this->path);
734
    }
735
736
    /**
737
     * Get a domain property value
738
     *
739
     * Multi-level properties might be traversed by property name paths separated with colons (":").
740
     *
741
     * @param string $property Property name
742
     * @return mixed Property value
743
     */
744 2
    public function getDomainProperty($property)
745
    {
746 2
        return $this->domainProperties->getProperty($property);
747
    }
748
749
    /**
750
     * Set a domain property value
751
     *
752
     * @param string $property Property name
753
     * @param mixed $value Property value
754
     * @return ObjectInterface Self reference
755
     */
756 1
    public function setDomainProperty($property, $value)
757
    {
758 1
        $this->setDomainProperties($this->domainProperties->setProperty($property, $value));
759 1
        return $this;
760
    }
761
762
    /**
763
     * Get a processing instruction
764
     *
765
     * @param string $procInst Processing instruction name
766
     * @return mixed Processing instruction
767
     */
768
    public function getProcessingInstruction($procInst)
769
    {
770
        return $this->processingInstructions->getProperty($procInst);
771
    }
772
773
    /**
774
     * Set a processing instruction
775
     *
776
     * @param string $procInst Processing instruction name
777
     * @param mixed $value Processing instruction
778
     * @return ObjectInterface Self reference
779
     */
780 1
    public function setProcessingInstruction($procInst, $value)
781
    {
782 1
        $this->setProcessingInstructions($this->processingInstructions->setProperty($procInst, $value));
783 1
        return $this;
784
    }
785
786
    /**
787
     * Persist the current object revision
788
     *
789
     * @return ObjectInterface Object
790
     */
791 1
    public function persist()
792
    {
793
        // If this is not the latest revision
794 1
        if ($this->getRevision() != $this->latestRevision) {
795
            throw new RuntimeException(
796
                sprintf(
797
                    'Cannot persist revision %s/%s',
798
                    $this->getRevision()->getRevision(),
799
                    $this->latestRevision->getRevision()
800
                ),
801
                RuntimeException::CANNOT_PERSIST_EARLIER_REVISION
802
            );
803
        }
804
805
        // Update the object repository
806 1
        $this->path->getRepository()->updateObject($this);
807
808
        // Reset state
809 1
        $this->state = self::STATE_CLEAN;
810
811 1
        return $this;
812
    }
813
814
    /**
815
     * Publish the current object revision
816
     *
817
     * @return ObjectInterface Object
818
     */
819 1
    public function publish() {
820
        // If this is a draft
821 1
        if ($this->isDraft()) {
822
            // TODO: Send signal
823
824
            // Create draft system properties
825 1
            $this->systemProperties = $this->systemProperties->publish();
826
827
            // Adapt the system properties collection state
828 1
            $this->collectionStates[SystemProperties::COLLECTION] = spl_object_hash($this->systemProperties);
829
830
            // Set the draft flag on the repository path
831 1
            $this->path = $this->path->setDraft(false);
832
833
            // Flag this object as dirty
834 1
            $this->setPublishedState();
835
        }
836
837 1
        return $this;
838
    }
839
840
    /**
841
     * Convert this object revision into a draft
842
     */
843 3
    protected function convertToDraft() {
844
        // Increment the latest revision number
845 3
        $this->latestRevision = $this->latestRevision->increment();
846
847
        // Create draft system properties
848 3
        $this->systemProperties = $this->systemProperties->createDraft($this->latestRevision);
849
850
        // Adapt the system properties collection state
851 3
        $this->collectionStates[SystemProperties::COLLECTION] = spl_object_hash($this->systemProperties);
852
853
        // Set the draft flag on the repository path
854 3
        $this->path = $this->path->setDraft(true)->setRevision(Revision::current());
855
856
        // If this is not already a draft ...
857
        // Recreate the system properties
858
        // Copy the object ID
859
        // Copy the object type
860
        // Set the revision number to latest revision + 1
861
        // Set the creation date to now
862
        // Set no publication date
863
        // Set the draft flag on the repository path
864
        // Increase the latest revision by 1
865
866
        // Else if this is a draft
867
        // No action needed
868 3
    }
869
870
    /**
871
     * Return whether the object is in dirty state
872
     *
873
     * @return boolean Dirty state
874
     */
875 3
    public function isDirty()
876
    {
877 3
        return !!($this->state & self::STATE_DIRTY);
878
    }
879
}
880