Completed
Push — master ( acf07d...e75790 )
by Joschi
02:54
created

AbstractObject::setPublishedState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 1
b 0
f 1
ccs 4
cts 4
cp 1
crap 2
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 description
515
     *
516
     * @return string Object description
517
     */
518 2
    public function getDescription()
519
    {
520 2
        return $this->metaProperties->getDescription();
521
    }
522
523
    /**
524
     * Set the description
525
     *
526
     * @param string $description Description
527
     * @return ObjectInterface Self reference
528
     */
529 1
    public function setDescription($description)
530
    {
531 1
        $this->setMetaProperties($this->metaProperties->setDescription($description));
532 1
        return $this;
533
    }
534
535
    /**
536
     * Return the object abstract
537
     *
538
     * @return string Object abstract
539
     */
540 2
    public function getAbstract()
541
    {
542 2
        return $this->metaProperties->getAbstract();
543
    }
544
545
    /**
546
     * Set the abstract
547
     *
548
     * @param string $abstract Abstract
549
     * @return ObjectInterface Self reference
550
     */
551 1
    public function setAbstract($abstract)
552
    {
553 1
        $this->setMetaProperties($this->metaProperties->setAbstract($abstract));
554 1
        return $this;
555
    }
556
557
    /**
558
     * Return all object keywords
559
     *
560
     * @return array Object keywords
561
     */
562 2
    public function getKeywords()
563
    {
564 2
        return $this->metaProperties->getKeywords();
565
    }
566
567
    /**
568
     * Set the keywords
569
     *
570
     * @param array $keywords Keywords
571
     * @return ObjectInterface Self reference
572
     */
573 1
    public function setKeywords(array $keywords)
574
    {
575 1
        $this->setMetaProperties($this->metaProperties->setKeywords($keywords));
576 1
        return $this;
577
    }
578
579
    /**
580
     * Return all object categories
581
     *
582
     * @return array Object categories
583
     */
584 2
    public function getCategories()
585
    {
586 2
        return $this->metaProperties->getCategories();
587
    }
588
589
    /**
590
     * Set the categories
591
     *
592
     * @param array $categories Categories
593
     * @return ObjectInterface Self reference
594
     */
595 1
    public function setCategories(array $categories)
596
    {
597 1
        $this->setMetaProperties($this->metaProperties->setCategories($categories));
598 1
        return $this;
599
    }
600
601
    /**
602
     * Return all object authors
603
     *
604
     * @return AuthorInterface[] Authors
605
     */
606 2
    public function getAuthors()
607
    {
608 2
        return $this->metaProperties->getAuthors();
609
    }
610
611
    /**
612
     * Add an object author
613
     *
614
     * @param AuthorInterface $author Author
615
     * @return ObjectInterface Self reference
616
     */
617 1
    public function addAuthor(AuthorInterface $author)
618
    {
619 1
        $authors = $this->metaProperties->getAuthors();
620 1
        $authors[] = $author;
621 1
        $this->metaProperties->setAuthors($authors);
622 1
        return $this;
623
    }
624
625
    /**
626
     * Return the object repository path
627
     *
628
     * @return RepositoryPathInterface Object repository path
629
     */
630 18
    public function getRepositoryPath()
631
    {
632 18
        return $this->path;
633
    }
634
635
    /**
636
     * Return the object property data
637
     *
638
     * @return array Object property data
639
     */
640 5
    public function getPropertyData()
641
    {
642 5
        $propertyData = array_filter([
643 5
            SystemProperties::COLLECTION => $this->systemProperties->toArray(),
644 5
            MetaProperties::COLLECTION => $this->metaProperties->toArray(),
645 5
            AbstractDomainProperties::COLLECTION => $this->domainProperties->toArray(),
646 5
            ProcessingInstructions::COLLECTION => $this->processingInstructions->toArray(),
647 5
            Relations::COLLECTION => $this->relations->toArray(),
648 5
        ], function (array $collection) {
649 5
            return (boolean)count($collection);
650 5
        });
651
652 5
        return $propertyData;
653
    }
654
655
    /**
656
     * Return the object payload
657
     *
658
     * @return string Object payload
659
     */
660 2
    public function getPayload()
661
    {
662 2
        return $this->payload;
663
    }
664
665
    /**
666
     * Set the payload
667
     *
668
     * @param string $payload Payload
669
     * @return ObjectInterface Self reference
670
     */
671 1
    public function setPayload($payload)
672
    {
673
        // If the payload is changed
674 1
        if ($payload !== $this->payload) {
675 1
            $this->setMutatedState();
676
        }
677
678 1
        $this->payload = $payload;
679 1
        return $this;
680
    }
681
682
    /**
683
     * Return the absolute object URL
684
     *
685
     * @return string
686
     */
687 4
    public function getAbsoluteUrl()
688
    {
689 4
        return getenv('APPARAT_BASE_URL').ltrim($this->path->getRepository()->getUrl(), '/').strval($this->path);
690
    }
691
692
    /**
693
     * Get a domain property value
694
     *
695
     * Multi-level properties might be traversed by property name paths separated with colons (":").
696
     *
697
     * @param string $property Property name
698
     * @return mixed Property value
699
     */
700 2
    public function getDomainProperty($property)
701
    {
702 2
        return $this->domainProperties->getProperty($property);
703
    }
704
705
    /**
706
     * Set a domain property value
707
     *
708
     * @param string $property Property name
709
     * @param mixed $value Property value
710
     * @return ObjectInterface Self reference
711
     */
712 1
    public function setDomainProperty($property, $value)
713
    {
714 1
        $this->setDomainProperties($this->domainProperties->setProperty($property, $value));
715 1
        return $this;
716
    }
717
718
    /**
719
     * Get a processing instruction
720
     *
721
     * @param string $procInst Processing instruction name
722
     * @return mixed Processing instruction
723
     */
724
    public function getProcessingInstruction($procInst)
725
    {
726
        return $this->processingInstructions->getProperty($procInst);
727
    }
728
729
    /**
730
     * Set a processing instruction
731
     *
732
     * @param string $procInst Processing instruction name
733
     * @param mixed $value Processing instruction
734
     * @return ObjectInterface Self reference
735
     */
736 1
    public function setProcessingInstruction($procInst, $value)
737
    {
738 1
        $this->setProcessingInstructions($this->processingInstructions->setProperty($procInst, $value));
739 1
        return $this;
740
    }
741
742
    /**
743
     * Persist the current object revision
744
     *
745
     * @return ObjectInterface Object
746
     */
747 1
    public function persist()
748
    {
749
        // If this is not the latest revision
750 1
        if ($this->getRevision() != $this->latestRevision) {
751
            throw new RuntimeException(
752
                sprintf(
753
                    'Cannot persist revision %s/%s',
754
                    $this->getRevision()->getRevision(),
755
                    $this->latestRevision->getRevision()
756
                ),
757
                RuntimeException::CANNOT_PERSIST_EARLIER_REVISION
758
            );
759
        }
760
761
        // Update the object repository
762 1
        $this->path->getRepository()->updateObject($this);
763
764
        // Reset state
765 1
        $this->state = self::STATE_CLEAN;
766
767 1
        return $this;
768
    }
769
770
    /**
771
     * Publish the current object revision
772
     *
773
     * @return ObjectInterface Object
774
     */
775 1
    public function publish() {
776
        // If this is a draft
777 1
        if ($this->isDraft()) {
778
            // TODO: Send signal
779
780
            // Create draft system properties
781 1
            $this->systemProperties = $this->systemProperties->publish();
782
783
            // Adapt the system properties collection state
784 1
            $this->collectionStates[SystemProperties::COLLECTION] = spl_object_hash($this->systemProperties);
785
786
            // Set the draft flag on the repository path
787 1
            $this->path = $this->path->setDraft(false);
788
789
            // Flag this object as dirty
790 1
            $this->setPublishedState();
791
        }
792
793 1
        return $this;
794
    }
795
796
    /**
797
     * Convert this object revision into a draft
798
     */
799 3
    protected function convertToDraft() {
800
        // Increment the latest revision number
801 3
        $this->latestRevision = $this->latestRevision->increment();
802
803
        // Create draft system properties
804 3
        $this->systemProperties = $this->systemProperties->createDraft($this->latestRevision);
805
806
        // Adapt the system properties collection state
807 3
        $this->collectionStates[SystemProperties::COLLECTION] = spl_object_hash($this->systemProperties);
808
809
        // Set the draft flag on the repository path
810 3
        $this->path = $this->path->setDraft(true)->setRevision(Revision::current());
811
812
        // If this is not already a draft ...
813
        // Recreate the system properties
814
        // Copy the object ID
815
        // Copy the object type
816
        // Set the revision number to latest revision + 1
817
        // Set the creation date to now
818
        // Set no publication date
819
        // Set the draft flag on the repository path
820
        // Increase the latest revision by 1
821
822
        // Else if this is a draft
823
        // No action needed
824 3
    }
825
826
    /**
827
     * Return whether the object is in dirty state
828
     *
829
     * @return boolean Dirty state
830
     */
831 3
    public function isDirty()
832
    {
833 3
        return !!($this->state & self::STATE_DIRTY);
834
    }
835
}
836