Completed
Push — master ( 68c6f3...91ba10 )
by Joschi
02:27
created

AbstractObject::loadRevisionData()   F

Complexity

Conditions 11
Paths 1024

Size

Total Lines 44
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 11

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 44
ccs 30
cts 30
cp 1
rs 3.1764
cc 11
eloc 29
nc 1024
nop 2
crap 11

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
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\InvalidArgumentException as PropertyInvalidArgumentException;
45
use Apparat\Object\Domain\Model\Properties\MetaProperties;
46
use Apparat\Object\Domain\Model\Properties\ProcessingInstructions;
47
use Apparat\Object\Domain\Model\Properties\Relations;
48
use Apparat\Object\Domain\Model\Properties\SystemProperties;
49
use Apparat\Object\Domain\Repository\Service;
50
51
/**
52
 * Abstract object
53
 *
54
 * @package Apparat\Object
55
 * @subpackage Apparat\Object\Domain
56
 */
57
abstract class AbstractObject implements ObjectInterface
58
{
59
    /**
60
     * Clean state
61
     *
62
     * @var int
63
     */
64
    const STATE_CLEAN = 0;
65
    /**
66
     * Dirty state
67
     *
68
     * @var int
69
     */
70
    const STATE_DIRTY = 1;
71
    /**
72
     * Mutated state
73
     *
74
     * @var int
75
     */
76
    const STATE_MUTATED = 2;
77
    /**
78
     * System properties
79
     *
80
     * @var SystemProperties
81
     */
82
    protected $systemProperties;
83
    /**
84
     * Meta properties
85
     *
86
     * @var MetaProperties
87
     */
88
    protected $metaProperties;
89
    /**
90
     * Domain properties
91
     *
92
     * @var AbstractDomainProperties
93
     */
94
    protected $domainProperties;
95
    /**
96
     * Object payload
97
     *
98
     * @var string
99
     */
100
    protected $payload;
101
    /**
102
     * Repository path
103
     *
104
     * @var RepositoryPathInterface
105
     */
106
    protected $path;
107
    /**
108
     * Domain property collection class
109
     *
110
     * @var string
111
     */
112
    protected $domainPropertyCClass = AbstractDomainProperties::class;
113
    /**
114
     * Object relations
115
     *
116
     * @var Relations
117
     */
118
    protected $relations;
119
    /**
120
     * Processing instructions
121
     *
122
     * @var ProcessingInstructions
123
     */
124
    protected $processingInstructions;
125
    /**
126
     * Latest revision index
127
     *
128
     * @var Revision
129
     */
130
    protected $latestRevision;
131
    /**
132
     * Object state
133
     *
134
     * @var int
135
     */
136
    protected $state = self::STATE_CLEAN;
137
    /**
138
     * Property collection states
139
     *
140
     * @var array
141
     */
142
    protected $collectionStates = [];
143
144
    /**
145
     * Object constructor
146
     *
147
     * @param string $payload Object payload
148
     * @param array $propertyData Property data
149
     * @param RepositoryPathInterface $path Object repository path
150
     */
151 19
    public function __construct($payload = '', array $propertyData = [], RepositoryPathInterface $path = null)
152
    {
153
        // If the domain property collection class is invalid
154 19
        if (!$this->domainPropertyCClass
155 19
            || !class_exists($this->domainPropertyCClass)
156 19
            || !(new \ReflectionClass($this->domainPropertyCClass))->isSubclassOf(AbstractDomainProperties::class)
157 19
        ) {
158 1
            throw new PropertyInvalidArgumentException(
159 1
                sprintf(
160 1
                    'Invalid domain property collection class "%s"',
161 1
                    $this->domainPropertyCClass
162 1
                ),
163
                PropertyInvalidArgumentException::INVALID_DOMAIN_PROPERTY_COLLECTION_CLASS
164 1
            );
165
        }
166
167 18
        $this->path = $path;
168
169
        // Load the current revision data
170 18
        $this->loadRevisionData($payload, $propertyData);
171
172
        // Save the latest revision index
173 16
        $this->latestRevision = $this->getRevision();
174 16
    }
175
176
    /**
177
     * Load object revision data
178
     *
179
     * @param string $payload Object payload
180
     * @param array $propertyData Property data
181
     */
182 18
    protected function loadRevisionData($payload = '', array $propertyData = [])
183
    {
184 18
        $this->payload = $payload;
185
186
        // Instantiate the system properties
187 18
        $systemPropertyData = (empty($propertyData[SystemProperties::COLLECTION]) ||
188 18
            !is_array(
189 18
                $propertyData[SystemProperties::COLLECTION]
190 18
            )) ? [] : $propertyData[SystemProperties::COLLECTION];
191 18
        $this->systemProperties = Kernel::create(SystemProperties::class, [$systemPropertyData, $this]);
192
193
        // Instantiate the meta properties
194 17
        $metaPropertyData = (empty($propertyData[MetaProperties::COLLECTION]) ||
195 16
            !is_array(
196 16
                $propertyData[MetaProperties::COLLECTION]
197 17
            )) ? [] : $propertyData[MetaProperties::COLLECTION];
198
        /** @var MetaProperties $metaPropertyCollection */
199 17
        $metaPropertyCollection = Kernel::create(MetaProperties::class, [$metaPropertyData, $this]);
200 16
        $this->setMetaProperties($metaPropertyCollection, true);
201
202
        // Instantiate the domain properties
203 16
        $domainPropertyData = (empty($propertyData[AbstractDomainProperties::COLLECTION]) ||
204 15
            !is_array(
205 15
                $propertyData[AbstractDomainProperties::COLLECTION]
206 16
            )) ? [] : $propertyData[AbstractDomainProperties::COLLECTION];
207 16
        $this->domainProperties = Kernel::create($this->domainPropertyCClass, [$domainPropertyData, $this]);
208
209
        // Instantiate the processing instructions
210 16
        $procInstData = (empty($propertyData[ProcessingInstructions::COLLECTION]) ||
211 12
            !is_array(
212 12
                $propertyData[ProcessingInstructions::COLLECTION]
213 16
            )) ? [] : $propertyData[ProcessingInstructions::COLLECTION];
214 16
        $this->processingInstructions = Kernel::create(ProcessingInstructions::class, [$procInstData, $this]);
215
216
        // Instantiate the object relations
217 16
        $relationData = (empty($propertyData[Relations::COLLECTION]) ||
218 12
            !is_array(
219 12
                $propertyData[Relations::COLLECTION]
220 16
            )) ? [] : $propertyData[Relations::COLLECTION];
221 16
        $this->relations = Kernel::create(Relations::class, [$relationData, $this]);
222
223
        // Reset the object state to clean
224 16
        $this->state = self::STATE_CLEAN;
225 16
    }
226
227
    /**
228
     * Set the meta properties collection
229
     *
230
     * @param MetaProperties $metaProperties Meta property collection
231
     * @param bool $overwrite Overwrite the existing collection (if present)
232
     */
233 16
    protected function setMetaProperties(MetaProperties $metaProperties, $overwrite = false)
234
    {
235 16
        $this->metaProperties = $metaProperties;
236 16
        $metaPropertiesState = spl_object_hash($this->metaProperties);
237
238
        // If the meta property collection state has changed
239 16
        if (!$overwrite && !empty($this->collectionStates[MetaProperties::COLLECTION]) &&
240 1
            ($metaPropertiesState !== $this->collectionStates[MetaProperties::COLLECTION])
241 16
        ) {
242
            // Flag this object as mutated
243 1
            $this->setMutatedState();
244 1
        }
245
246 16
        $this->collectionStates[MetaProperties::COLLECTION] = $metaPropertiesState;
247 16
    }
248
249
    /**
250
     * Set the object state to mutated
251
     */
252 1
    protected function setMutatedState()
253
    {
254
        // If this object is not in mutated state yet
255 1
        if (!($this->state & self::STATE_MUTATED)) {
256
            // TODO: Take actions to make this the most recent revision (in draft mode)
257 1
        }
258
259
        // Enable the mutated (and dirty) state
260 1
        $this->state |= (self::STATE_DIRTY | self::STATE_MUTATED);
261 1
    }
262
263
    /**
264
     * Return the object revision
265
     *
266
     * @return Revision Object revision
267
     */
268 16
    public function getRevision()
269
    {
270 16
        return $this->systemProperties->getRevision();
271
    }
272
273
    /**
274
     * Return whether the object is in dirty state
275
     *
276
     * @return boolean Dirty state
277
     */
278
    public function isDirty()
279
    {
280
        return !!($this->state & self::STATE_DIRTY);
281
    }
282
283
    /**
284
     * Return whether the object is in mutated state
285
     *
286
     * @return boolean Mutated state
287
     */
288
    public function isMutated()
289
    {
290
        return !!($this->state & self::STATE_MUTATED);
291
    }
292
293
    /**
294
     * Return the object draft mode
295
     *
296
     * @return boolean Object draft mode
297
     */
298 1
    public function isDraft()
299
    {
300 1
        return $this->systemProperties->isDraft();
301
    }
302
303
    /**
304
     * Use a specific object revision
305
     *
306
     * @param Revision $revision Revision to be used
307
     * @return ObjectInterface Object
308
     * @throws OutOfBoundsException If the requested revision is invalid
309
     */
310 15
    public function useRevision(Revision $revision)
311
    {
312 15
        $isCurrentRevision = false;
313
314
        // If the requested revision is invalid
315 15
        if (!$revision->isCurrent() &&
316
            (($revision->getRevision() < 1) || ($revision->getRevision() > $this->latestRevision->getRevision()))
317 15
        ) {
318
            throw new OutOfBoundsException(sprintf('Invalid object revision "%s"', $revision->getRevision()),
319
                OutOfBoundsException::INVALID_OBJECT_REVISION);
320
        }
321
322
        // If the current revision got requested
323 15
        if ($revision->isCurrent()) {
324 15
            $isCurrentRevision = true;
325 15
            $revision = $this->latestRevision;
326 15
        }
327
328
        // If the requested revision is not already used
329 15
        if ($revision != $this->getRevision()) {
330
            /** @var ManagerInterface $objectManager */
331
            $objectManager = Kernel::create(Service::class)->getObjectManager();
332
333
            // Load the requested object revision resource
334
            /** @var Revision $newRevision */
335
            $newRevision = $isCurrentRevision ? Kernel::create(Revision::class, [Revision::CURRENT]) :
336
                $revision;
337
            /** @var RepositoryPath $newRevisionPath */
338
            $newRevisionPath = $this->path->setRevision($newRevision);
339
            $revisionResource = $objectManager->loadObject($newRevisionPath);
340
341
            // Load the revision resource data
342
            $this->loadRevisionData($revisionResource->getPayload(), $revisionResource->getPropertyData());
343
344
            // Set the current revision path
345
            $this->path = $newRevisionPath;
346
        }
347
348 15
        return $this;
349
    }
350
351
    /**
352
     * Return the object ID
353
     *
354
     * @return Id Object ID
355
     */
356 5
    public function getId()
357
    {
358 5
        return $this->systemProperties->getId();
359
    }
360
361
    /**
362
     * Return the object type
363
     *
364
     * @return Type Object type
365
     */
366 1
    public function getType()
367
    {
368 1
        return $this->systemProperties->getType();
369
    }
370
371
    /**
372
     * Return the creation date & time
373
     *
374
     * @return \DateTimeImmutable Creation date & time
375
     */
376 1
    public function getCreated()
377
    {
378 1
        return $this->systemProperties->getCreated();
379
    }
380
381
    /**
382
     * Return the publication date & time
383
     *
384
     * @return \DateTimeImmutable Publication date & time
385
     */
386 1
    public function getPublished()
387
    {
388 1
        return $this->systemProperties->getPublished();
389
    }
390
391
    /**
392
     * Return the object hash
393
     *
394
     * @return string Object hash
395
     */
396 1
    public function getHash()
397
    {
398 1
        return $this->systemProperties->getHash();
399
    }
400
401
    /**
402
     * Return the object description
403
     *
404
     * @return string Object description
405
     */
406 1
    public function getDescription()
407
    {
408 1
        return $this->metaProperties->getDescription();
409
    }
410
411
    /**
412
     * Set the description
413
     *
414
     * @param string $description Description
415
     * @return ObjectInterface Self reference
416
     */
417 1
    public function setDescription($description)
418
    {
419 1
        $this->setMetaProperties($this->metaProperties->setDescription($description));
420 1
        return $this;
421
    }
422
423
    /**
424
     * Return the object abstract
425
     *
426
     * @return string Object abstract
427
     */
428 1
    public function getAbstract()
429
    {
430 1
        return $this->metaProperties->getAbstract();
431
    }
432
433
    /**
434
     * Set the abstract
435
     *
436
     * @param string $abstract Abstract
437
     * @return ObjectInterface Self reference
438
     */
439
    public function setAbstract($abstract)
440
    {
441
        $this->setMetaProperties($this->metaProperties->setAbstract($abstract));
442
        return $this;
443
    }
444
445
    /**
446
     * Return all object keywords
447
     *
448
     * @return array Object keywords
449
     */
450 1
    public function getKeywords()
451
    {
452 1
        return $this->metaProperties->getKeywords();
453
    }
454
455
    /**
456
     * Set the keywords
457
     *
458
     * @param array $keywords Keywords
459
     * @return ObjectInterface Self reference
460
     */
461
    public function setKeywords(array $keywords)
462
    {
463
        $this->setMetaProperties($this->metaProperties->setKeywords($keywords));
464
        return $this;
465
    }
466
467
    /**
468
     * Return all object categories
469
     *
470
     * @return array Object categories
471
     */
472 1
    public function getCategories()
473
    {
474 1
        return $this->metaProperties->getCategories();
475
    }
476
477
    /**
478
     * Set the categories
479
     *
480
     * @param array $categories Categories
481
     * @return ObjectInterface Self reference
482
     */
483
    public function setCategories(array $categories)
484
    {
485
        $this->setMetaProperties($this->metaProperties->setCategories($categories));
486
        return $this;
487
    }
488
489
    /**
490
     * Return all object authors
491
     *
492
     * @return AuthorInterface[] Authors
493
     */
494 2
    public function getAuthors()
495
    {
496 2
        return $this->metaProperties->getAuthors();
497
    }
498
499
    /**
500
     * Add an object author
501
     *
502
     * @param AuthorInterface $author Author
503
     * @return ObjectInterface Self reference
504
     */
505 1
    public function addAuthor(AuthorInterface $author)
506
    {
507 1
        $authors = $this->metaProperties->getAuthors();
508 1
        $authors[] = $author;
509 1
        $this->metaProperties->setAuthors($authors);
510 1
        return $this;
511
    }
512
513
    /**
514
     * Return the object repository path
515
     *
516
     * @return RepositoryPathInterface Object repository path
517
     */
518 16
    public function getRepositoryPath()
519
    {
520 16
        return $this->path;
521
    }
522
523
    /**
524
     * Return the object property data
525
     *
526
     * @return array Object property data
527
     */
528 3
    public function getPropertyData()
529
    {
530 3
        $propertyData = array_filter([
531 3
            SystemProperties::COLLECTION => $this->systemProperties->toArray(),
532 3
            MetaProperties::COLLECTION => $this->metaProperties->toArray(),
533 3
            AbstractDomainProperties::COLLECTION => $this->domainProperties->toArray(),
534 3
            ProcessingInstructions::COLLECTION => $this->processingInstructions->toArray(),
535 3
            Relations::COLLECTION => $this->relations->toArray(),
536 3
        ], function (array $collection) {
537 3
            return (boolean)count($collection);
538 3
        });
539
540 3
        return $propertyData;
541
    }
542
543
    /**
544
     * Return the object payload
545
     *
546
     * @return string Object payload
547
     */
548 2
    public function getPayload()
549
    {
550 2
        return $this->payload;
551
    }
552
553
    /**
554
     * Return the absolute object URL
555
     *
556
     * @return string
557
     */
558 1
    public function getAbsoluteUrl()
559
    {
560 1
        return getenv('APPARAT_BASE_URL').ltrim($this->path->getRepository()->getUrl(), '/').strval($this->path);
561
    }
562
563
    /**
564
     * Get a particular property value
565
     *
566
     * Multi-level properties might be traversed by property name paths separated with colons (":").
567
     *
568
     * @param string $property Property name
569
     * @return mixed Property value
570
     */
571 2
    public function getDomainProperty($property)
572
    {
573 2
        return $this->domainProperties->getProperty($property);
574
    }
575
576
    protected function setDirtyState()
577
    {
578
579
        // If this object is not in dirty state yet
580
        if (!($this->state & self::STATE_DIRTY)) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
581
582
        }
583
584
        // Enable the dirty state
585
        $this->state |= self::STATE_DIRTY;
586
    }
587
}
588