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()); |
|
|
|
|
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; |
|
|
|
|
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; |
|
|
|
|
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
|
|
|
|
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: