Completed
Push — master ( a2bf82...972aab )
by Joschi
02:43
created

AbstractObject::useRevision()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 40
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 13.125

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 40
ccs 9
cts 18
cp 0.5
rs 6.7272
cc 7
eloc 18
nc 7
nop 1
crap 13.125
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
     * System properties
61
     *
62
     * @var SystemProperties
63
     */
64
    protected $systemProperties;
65
    /**
66
     * Meta properties
67
     *
68
     * @var MetaProperties
69
     */
70
    protected $metaProperties;
71
    /**
72
     * Domain properties
73
     *
74
     * @var AbstractDomainProperties
75
     */
76
    protected $domainProperties;
77
    /**
78
     * Object payload
79
     *
80
     * @var string
81
     */
82
    protected $payload;
83
    /**
84
     * Repository path
85
     *
86
     * @var RepositoryPathInterface
87
     */
88
    protected $path;
89
    /**
90
     * Domain property collection class
91
     *
92
     * @var string
93
     */
94
    protected $domainPropertyCClass = AbstractDomainProperties::class;
95
    /**
96
     * Object relations
97
     *
98
     * @var Relations
99
     */
100
    protected $relations;
101
    /**
102
     * Processing instructions
103
     *
104
     * @var ProcessingInstructions
105
     */
106
    protected $processingInstructions;
107
    /**
108
     * Latest revision index
109
     *
110
     * @var Revision
111
     */
112
    protected $latestRevision;
113
114
    /**
115
     * Object constructor
116
     *
117
     * @param string $payload Object payload
118
     * @param array $propertyData Property data
119
     * @param RepositoryPathInterface $path Object repository path
120
     */
121 18
    public function __construct($payload = '', array $propertyData = [], RepositoryPathInterface $path = null)
122
    {
123
        // If the domain property collection class is invalid
124 18
        if (!$this->domainPropertyCClass
125 18
            || !class_exists($this->domainPropertyCClass)
126 18
            || !(new \ReflectionClass($this->domainPropertyCClass))->isSubclassOf(AbstractDomainProperties::class)
127
        ) {
128 1
            throw new PropertyInvalidArgumentException(
129
                sprintf(
130 1
                    'Invalid domain property collection class "%s"',
131 1
                    $this->domainPropertyCClass
132
                ),
133 1
                PropertyInvalidArgumentException::INVALID_DOMAIN_PROPERTY_COLLECTION_CLASS
134
            );
135
        }
136
137 17
        $this->path = $path;
138
139
        // Load the current revision data
140 17
        $this->loadRevisionData($payload, $propertyData);
141
142
        // Save the latest revision index
143 15
        $this->latestRevision = $this->getRevision();
144 15
    }
145
146
    /**
147
     * Load object revision data
148
     *
149
     * @param string $payload Object payload
150
     * @param array $propertyData Property data
151
     */
152 17
    protected function loadRevisionData($payload = '', array $propertyData = [])
153
    {
154 17
        $this->payload = $payload;
155
156
        // Instantiate the system properties
157 17
        $systemPropertyData = (empty($propertyData[SystemProperties::COLLECTION]) ||
158 17
            !is_array(
159 17
                $propertyData[SystemProperties::COLLECTION]
160 17
            )) ? [] : $propertyData[SystemProperties::COLLECTION];
161 17
        $this->systemProperties = Kernel::create(SystemProperties::class, [$systemPropertyData, $this]);
162
163
        // Instantiate the meta properties
164 16
        $metaPropertyData = (empty($propertyData[MetaProperties::COLLECTION]) ||
165 15
            !is_array(
166 16
                $propertyData[MetaProperties::COLLECTION]
167 16
            )) ? [] : $propertyData[MetaProperties::COLLECTION];
168 16
        $this->metaProperties = Kernel::create(MetaProperties::class, [$metaPropertyData, $this]);
169
170
        // Instantiate the domain properties
171 15
        $domainPropertyData = (empty($propertyData[AbstractDomainProperties::COLLECTION]) ||
172 14
            !is_array(
173 15
                $propertyData[AbstractDomainProperties::COLLECTION]
174 15
            )) ? [] : $propertyData[AbstractDomainProperties::COLLECTION];
175 15
        $this->domainProperties = Kernel::create($this->domainPropertyCClass, [$domainPropertyData, $this]);
176
177
        // Instantiate the processing instructions
178 15
        $procInstData = (empty($propertyData[ProcessingInstructions::COLLECTION]) ||
179 11
            !is_array(
180 15
                $propertyData[ProcessingInstructions::COLLECTION]
181 15
            )) ? [] : $propertyData[ProcessingInstructions::COLLECTION];
182 15
        $this->processingInstructions = Kernel::create(ProcessingInstructions::class, [$procInstData, $this]);
183
184
        // Instantiate the object relations
185 15
        $relationData = (empty($propertyData[Relations::COLLECTION]) ||
186 11
            !is_array(
187 15
                $propertyData[Relations::COLLECTION]
188 15
            )) ? [] : $propertyData[Relations::COLLECTION];
189 15
        $this->relations = Kernel::create(Relations::class, [$relationData, $this]);
190 15
    }
191
192
    /**
193
     * Return the object revision
194
     *
195
     * @return Revision Object revision
196
     */
197 15
    public function getRevision()
198
    {
199 15
        return $this->systemProperties->getRevision();
200
    }
201
202
    /**
203
     * Use a specific object revision
204
     *
205
     * @param Revision $revision Revision to be used
206
     * @return ObjectInterface Object
207
     * @throws OutOfBoundsException If the requested revision is invalid
208
     */
209 14
    public function useRevision(Revision $revision)
210
    {
211 14
        $isCurrentRevision = false;
212
213
        // If the requested revision is invalid
214 14
        if (!$revision->isCurrent() &&
215 14
            (($revision->getRevision() < 1) || ($revision->getRevision() > $this->latestRevision->getRevision()))
216
        ) {
217
            throw new OutOfBoundsException(sprintf('Invalid object revision "%s"', $revision->getRevision()),
218
                OutOfBoundsException::INVALID_OBJECT_REVISION);
219
        }
220
221
        // If the current revision got requested
222 14
        if ($revision->isCurrent()) {
223 14
            $isCurrentRevision = true;
224 14
            $revision = $this->latestRevision;
225
        }
226
227
        // If the requested revision is not already used
228 14
        if ($revision != $this->getRevision()) {
229
            /** @var ManagerInterface $objectManager */
230
            $objectManager = Kernel::create(Service::class)->getObjectManager();
231
232
            // Load the requested object revision resource
233
            /** @var Revision $newRevision */
234
            $newRevision = $isCurrentRevision ? Kernel::create(Revision::class, [Revision::CURRENT]) :
235
                $revision;
236
            /** @var RepositoryPath $newRevisionPath */
237
            $newRevisionPath = $this->path->setRevision($newRevision);
238
            $revisionResource = $objectManager->loadObject($newRevisionPath);
239
240
            // Load the revision resource data
241
            $this->loadRevisionData($revisionResource->getPayload(), $revisionResource->getPropertyData());
242
243
            // Set the current revision path
244
            $this->path = $newRevisionPath;
245
        }
246
247 14
        return $this;
248
    }
249
250
    /**
251
     * Return the object ID
252
     *
253
     * @return Id Object ID
254
     */
255 5
    public function getId()
256
    {
257 5
        return $this->systemProperties->getId();
258
    }
259
260
    /**
261
     * Return the object type
262
     *
263
     * @return Type Object type
264
     */
265 1
    public function getType()
266
    {
267 1
        return $this->systemProperties->getType();
268
    }
269
270
    /**
271
     * Return the creation date & time
272
     *
273
     * @return \DateTimeImmutable Creation date & time
274
     */
275 1
    public function getCreated()
276
    {
277 1
        return $this->systemProperties->getCreated();
278
    }
279
280
    /**
281
     * Return the publication date & time
282
     *
283
     * @return \DateTimeImmutable Publication date & time
284
     */
285 1
    public function getPublished()
286
    {
287 1
        return $this->systemProperties->getPublished();
288
    }
289
290
    /**
291
     * Return the object hash
292
     *
293
     * @return string Object hash
294
     */
295 1
    public function getHash()
296
    {
297 1
        return $this->systemProperties->getHash();
298
    }
299
300
    /**
301
     * Return the object description
302
     *
303
     * @return string Object description
304
     */
305 1
    public function getDescription()
306
    {
307 1
        return $this->metaProperties->getDescription();
308
    }
309
310
    /**
311
     * Return the object abstract
312
     *
313
     * @return string Object abstract
314
     */
315 1
    public function getAbstract()
316
    {
317 1
        return $this->metaProperties->getAbstract();
318
    }
319
320
    /**
321
     * Return all object keywords
322
     *
323
     * @return array Object keywords
324
     */
325 1
    public function getKeywords()
326
    {
327 1
        return $this->metaProperties->getKeywords();
328
    }
329
330
    /**
331
     * Return all object categories
332
     *
333
     * @return array Object categories
334
     */
335 1
    public function getCategories()
336
    {
337 1
        return $this->metaProperties->getCategories();
338
    }
339
340
    /**
341
     * Return all object authors
342
     *
343
     * @return AuthorInterface[] Authors
344
     */
345 2
    public function getAuthors()
346
    {
347 2
        return $this->metaProperties->getAuthors();
348
    }
349
350
    /**
351
     * Add an object author
352
     *
353
     * @param AuthorInterface $author Author
354
     * @return ObjectInterface Self reference
355
     */
356 1
    public function addAuthor(AuthorInterface $author)
357
    {
358 1
        $authors = $this->metaProperties->getAuthors();
359 1
        $authors[] = $author;
360 1
        $this->metaProperties->setAuthors($authors);
361 1
        return $this;
362
    }
363
364
    /**
365
     * Return the object repository path
366
     *
367
     * @return RepositoryPathInterface Object repository path
368
     */
369 15
    public function getRepositoryPath()
370
    {
371 15
        return $this->path;
372
    }
373
374
    /**
375
     * Return the object property data
376
     *
377
     * @return array Object property data
378
     */
379 2
    public function getPropertyData()
380
    {
381 2
        $propertyData = array_filter([
382 2
            SystemProperties::COLLECTION => $this->systemProperties->toArray(),
383 2
            MetaProperties::COLLECTION => $this->metaProperties->toArray(),
384 2
            AbstractDomainProperties::COLLECTION => $this->domainProperties->toArray(),
385 2
            ProcessingInstructions::COLLECTION => $this->processingInstructions->toArray(),
386 2
            Relations::COLLECTION => $this->relations->toArray(),
387 2
        ], function (array $collection) {
388 2
            return (boolean)count($collection);
389 2
        });
390
391 2
        return $propertyData;
392
    }
393
394
    /**
395
     * Return the object payload
396
     *
397
     * @return string Object payload
398
     */
399 2
    public function getPayload()
400
    {
401 2
        return $this->payload;
402
    }
403
404
    /**
405
     * Return the absolute object URL
406
     *
407
     * @return string
408
     */
409 1
    public function getAbsoluteUrl()
410
    {
411 1
        return getenv('APPARAT_BASE_URL').ltrim($this->path->getRepository()->getUrl(), '/').strval($this->path);
412
    }
413
414
    /**
415
     * Get a particular property value
416
     *
417
     * Multi-level properties might be traversed by property name paths separated with colons (":").
418
     *
419
     * @param string $property Property name
420
     * @return mixed Property value
421
     */
422 2
    public function getDomainProperty($property)
423
    {
424 2
        return $this->domainProperties->getProperty($property);
425
    }
426
}
427