Completed
Push — master ( c9de9a...8fa92c )
by Joschi
06:37
created

SystemProperties::isPublished()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\Application
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\Properties;
38
39
use Apparat\Kernel\Tests\Kernel;
40
use Apparat\Object\Domain\Model\Object\Id;
41
use Apparat\Object\Domain\Model\Object\ObjectInterface;
42
use Apparat\Object\Domain\Model\Object\Revision;
43
use Apparat\Object\Domain\Model\Object\RuntimeException;
44
use Apparat\Object\Domain\Model\Object\Type;
45
46
/**
47
 * Object system properties collection
48
 *
49
 * In general, the system properties are used as read-only collection, with one exception: Draft objects don't have the
50
 * `published` property set, so there's a {@link publish()} method for advancing an object's state.
51
 *
52
 * @package Apparat\Object
53
 * @subpackage Apparat\Object\Application
54
 */
55
class SystemProperties extends AbstractProperties
56
{
57
    /**
58
     * Collection name
59
     *
60
     * @var string
61
     */
62
    const COLLECTION = 'system';
63
    /**
64
     * ID property
65
     *
66
     * @var string
67
     */
68
    const PROPERTY_ID = 'id';
69
    /**
70
     * Type property
71
     *
72
     * @var string
73
     */
74
    const PROPERTY_TYPE = 'type';
75
    /**
76
     * Revision property
77
     *
78
     * @var string
79
     */
80
    const PROPERTY_REVISION = 'revision';
81
    /**
82
     * Created property
83
     *
84
     * @var string
85
     */
86
    const PROPERTY_CREATED = 'created';
87
    /**
88
     * Modified property
89
     *
90
     * @var string
91
     */
92
    const PROPERTY_MODIFIED = 'modified';
93
    /**
94
     * Published property
95
     *
96
     * @var string
97
     */
98
    const PROPERTY_PUBLISHED = 'published';
99
    /**
100
     * Deleted property
101
     *
102
     * @var string
103
     */
104
    const PROPERTY_DELETED = 'deleted';
105
    /**
106
     * Language property
107
     *
108
     * @var string
109
     */
110
    const PROPERTY_LANGUAGE = 'language';
111
    /**
112
     * Location property
113
     *
114
     * @var string
115
     */
116
    const PROPERTY_LOCATION = 'location';
117
    /**
118
     * Object ID (constant throughout revisions)
119
     *
120
     * @var Id
121
     */
122
    protected $uid = null;
123
    /**
124
     * Object type (constant throughout revisions)
125
     *
126
     * @var Type
127
     */
128
    protected $type = null;
129
    /**
130
     * Object revision
131
     *
132
     * @var Revision
133
     */
134
    protected $revision = null;
135
    /**
136
     * Creation date of this revision
137
     *
138
     * @var \DateTimeImmutable
139
     */
140
    protected $created = null;
141
    /**
142
     * Modification date of this revision
143
     *
144
     * @var \DateTimeImmutable
145
     */
146
    protected $modified = null;
147
    /**
148
     * Publication date of this revision
149
     *
150
     * @var \DateTimeImmutable
151
     */
152
    protected $published = null;
153
    /**
154
     * Deletion date of this revision
155
     *
156
     * @var \DateTimeImmutable
157
     */
158
    protected $deleted = null;
159
    /**
160
     * Location
161
     *
162
     * @var LocationProperties
163
     */
164
    protected $location = null;
165
    /**
166
     * Language (BCP 47 compliant)
167
     *
168
     * @var string
169
     * @see https://tools.ietf.org/html/bcp47
170
     */
171
    protected $language = null;
172
173
    /**
174
     * System properties constructor
175
     *
176
     * @param array $data Property data
177
     * @param ObjectInterface $object Owner object
178
     */
179 30
    public function __construct(array $data, ObjectInterface $object)
180
    {
181 30
        parent::__construct($data, $object);
182
183
        // Initialize the object ID
184 30
        if (array_key_exists(self::PROPERTY_ID, $data)) {
185 28
            $this->uid = Id::unserialize($data[self::PROPERTY_ID]);
186 28
        }
187
188
        // Initialize the object type
189 30
        if (array_key_exists(self::PROPERTY_TYPE, $data)) {
190 29
            $this->type = Type::unserialize($data[self::PROPERTY_TYPE]);
191 28
        }
192
193
        // Initialize the object creation date
194 29
        if (array_key_exists(self::PROPERTY_CREATED, $data)) {
195
            // Dumb fix for https://github.com/symfony/symfony/issues/18859
196 28
            $this->created = new \DateTimeImmutable((preg_match('%^\d+$%', $data[self::PROPERTY_CREATED]) ? '@' : '').$data[self::PROPERTY_CREATED]);
197 28
        }
198
199
        // Initialize the object modification date
200 29
        if (array_key_exists(self::PROPERTY_MODIFIED, $data)) {
201
            // Dumb fix for https://github.com/symfony/symfony/issues/18859
202 28
            $this->modified = new \DateTimeImmutable((preg_match('%^\d+$%', $data[self::PROPERTY_MODIFIED]) ? '@' : '').$data[self::PROPERTY_MODIFIED]);
203 28
        }
204
205
        // Initialize the object publication date
206 29
        if (array_key_exists(self::PROPERTY_PUBLISHED, $data)) {
207
            // Dumb fix for https://github.com/symfony/symfony/issues/18859
208 22
            $this->published = new \DateTimeImmutable((preg_match('%^\d+$%', $data[self::PROPERTY_PUBLISHED]) ? '@' : '').$data[self::PROPERTY_PUBLISHED]);
209 22
        }
210
211
        // Initialize the object deletion date
212 29
        if (array_key_exists(self::PROPERTY_DELETED, $data)) {
213
            // Dumb fix for https://github.com/symfony/symfony/issues/18859
214 2
            $this->deleted = new \DateTimeImmutable((preg_match('%^\d+$%', $data[self::PROPERTY_DELETED]) ? '@' : '').$data[self::PROPERTY_DELETED]);
215 2
        }
216
217
        // Initialize the object language
218 29
        if (array_key_exists(self::PROPERTY_LANGUAGE, $data)) {
219 28
            $this->language = trim($data[self::PROPERTY_LANGUAGE]);
220 28
        }
221
222
        // Initialize the location
223 29
        $this->location = Kernel::create(
224 29
            LocationProperties::class,
225 29
            [empty($data[self::PROPERTY_LOCATION]) ? [] : $data[self::PROPERTY_LOCATION], $this->object]
226 29
        );
227
228
        // Initialize the object revision
229 29
        if (array_key_exists(self::PROPERTY_REVISION, $data)) {
230 28
            $this->revision = Revision::unserialize($data[self::PROPERTY_REVISION])->setDraft($this->isDraft());
231 28
        }
232
233
        // Test if all mandatory properties are set
234 29
        if (!($this->uid instanceof Id)
235 29
            || !($this->type instanceof Type)
236 28
            || !($this->revision instanceof Revision)
237 28
            || !($this->created instanceof \DateTimeImmutable)
238 28
            || !($this->modified instanceof \DateTimeImmutable)
239 28
            || !strlen($this->language)
240 29
        ) {
241 1
            throw new InvalidArgumentException(
242 1
                'Invalid system properties',
243
                InvalidArgumentException::INVALID_SYSTEM_PROPERTIES
244 1
            );
245
        }
246 28
    }
247
248
    /**
249
     * Return the object ID
250
     *
251
     * @return Id Object ID
252
     */
253 8
    public function getId()
254
    {
255 8
        return $this->uid;
256
    }
257
258
    /**
259
     * Return the object type
260
     *
261
     * @return Type Object type
262
     */
263 4
    public function getType()
264
    {
265 4
        return $this->type;
266
    }
267
268
    /**
269
     * Return the object revision
270
     *
271
     * @return Revision Object revision
272
     */
273 27
    public function getRevision()
274
    {
275 27
        return $this->revision;
276
    }
277
278
    /**
279
     * Return the object draft mode
280
     *
281
     * @return boolean Object draft mode
282
     */
283 28
    public function isDraft()
284
    {
285 28
        return !($this->published instanceof \DateTimeImmutable);
286
    }
287
288
    /**
289
     * Return the object publication state
290
     *
291
     * @return boolean Object is published
292
     */
293 1
    public function isPublished()
294
    {
295 1
        return ($this->published instanceof \DateTimeImmutable);
296
    }
297
298
    /**
299
     * Return the object deletion state
300
     *
301
     * @return boolean Object is deleted
302
     */
303 27
    public function isDeleted()
304
    {
305 27
        return ($this->deleted instanceof \DateTimeImmutable);
306
    }
307
308
    /**
309
     * Return the creation date & time of this revision
310
     *
311
     * @return \DateTimeImmutable Creation date & time
312
     */
313 1
    public function getCreated()
314
    {
315 1
        return $this->created;
316
    }
317
318
    /**
319
     * Return the modification date & time of this revision
320
     *
321
     * @return \DateTimeImmutable Modification date & time
322
     */
323 1
    public function getModified()
324
    {
325 1
        return $this->modified;
326
    }
327
328
    /**
329
     * Return the publication date & time of this revision
330
     *
331
     * @return \DateTimeImmutable|null Publication date & time
332
     */
333 1
    public function getPublished()
334
    {
335 1
        return $this->published;
336
    }
337
338
    /**
339
     * Return the deletion date & time of this revision
340
     *
341
     * @return \DateTimeImmutable|null Deletion date & time
342
     */
343 1
    public function getDeleted()
344
    {
345 1
        return $this->deleted;
346
    }
347
348
    /**
349
     * Return the object language
350
     *
351
     * @return string
352
     */
353 1
    public function getLanguage()
354
    {
355 1
        return $this->language;
356
    }
357
358
    /**
359
     * Return the latitude
360
     *
361
     * @return float Latitude
362
     */
363 1
    public function getLatitude()
364
    {
365 1
        return $this->location->getLatitude();
366
    }
367
368
    /**
369
     * Set the latitude
370
     *
371
     * @param float $latitude Latitude
372
     * @return SystemProperties Self reference
373
     */
374 1
    public function setLatitude($latitude)
375
    {
376 1
        return $this->mutatePropertiesProperty(
377 1
            self::PROPERTY_LOCATION,
378 1
            $this->location->setLatitude($latitude)
379 1
        );
380
    }
381
382
    /**
383
     * Return the longitude
384
     *
385
     * @return float Longitude
386
     */
387 1
    public function getLongitude()
388
    {
389 1
        return $this->location->getLongitude();
390
    }
391
392
    /**
393
     * Set the longitude
394
     *
395
     * @param float $longitude Longitude
396
     * @return SystemProperties Self reference
397
     */
398 1
    public function setLongitude($longitude)
399
    {
400 1
        return $this->mutatePropertiesProperty(
401 1
            self::PROPERTY_LOCATION,
402 1
            $this->location->setLongitude($longitude)
403 1
        );
404
    }
405
406
    /**
407
     * Return the elevation
408
     *
409
     * @return float Elevation
410
     */
411 1
    public function getElevation()
412
    {
413 1
        return $this->location->getElevation();
414
    }
415
416
    /**
417
     * Set the elevation
418
     *
419
     * @param float $elevation
420
     * @return SystemProperties Self reference
421
     */
422 1
    public function setElevation($elevation)
423
    {
424 1
        return $this->mutatePropertiesProperty(
425 1
            self::PROPERTY_LOCATION,
426 1
            $this->location->setElevation($elevation)
427 1
        );
428
    }
429
430
    /**
431
     * Derive draft system properties
432
     *
433
     * @param Revision $draftRevision Draft revision
434
     * @return SystemProperties Draft system properties
435
     */
436 4
    public function createDraft(Revision $draftRevision)
437
    {
438 4
        $now = time();
439 4
        return new static(
440
            [
441 4
                self::PROPERTY_ID => $this->uid->getId(),
442 4
                self::PROPERTY_TYPE => $this->type->getType(),
443 4
                self::PROPERTY_REVISION => $draftRevision->getRevision(),
444 4
                self::PROPERTY_CREATED => $now,
445 4
                self::PROPERTY_MODIFIED => $now,
446 4
                self::PROPERTY_LANGUAGE => $this->language,
447 4
            ],
448 4
            $this->object
449 4
        );
450
    }
451
452
    /**
453
     * Indicate that the object got published
454
     *
455
     * @return SystemProperties System properties
456
     * @throws RuntimeException If the object is already published
457
     */
458 2
    public function publish()
459
    {
460
        // If the object is already published
461 2
        if ($this->published instanceof \DateTimeImmutable) {
462 1
            throw new RuntimeException(
463 1
                'Cannot republish object previously published at '.$this->published->format('c'),
464
                RuntimeException::CANNOT_REPUBLISH_OBJECT
465 1
            );
466
        }
467
468 2
        $systemProperties = clone $this;
469 2
        $systemProperties->published = new \DateTimeImmutable();
470 2
        $systemProperties->revision = $this->revision->setDraft(false);
471 2
        return $systemProperties;
472
    }
473
474
    /**
475
     * Update the object's modification timestamp
476
     *
477
     * @return SystemProperties System properties
478
     */
479 9
    public function touch()
480
    {
481 9
        $systemProperties = clone $this;
482 9
        $systemProperties->modified = new \DateTimeImmutable();
483 9
        return $systemProperties;
484
    }
485
486
    /**
487
     * Set the object's deletion timestamp
488
     *
489
     * @return SystemProperties System properties
490
     */
491 3
    public function delete()
492
    {
493 3
        $systemProperties = clone $this;
494 3
        $systemProperties->deleted = new \DateTimeImmutable();
495 3
        return $systemProperties;
496
    }
497
498
    /**
499
     * Unset the object's deletion timestamp
500
     *
501
     * @return SystemProperties System properties
502
     */
503 2
    public function undelete()
504
    {
505 2
        $systemProperties = clone $this;
506 2
        $systemProperties->deleted = null;
507 2
        return $systemProperties;
508
    }
509
510
    /**
511
     * Return the property values as array
512
     *
513
     * @return array Property values
514
     */
515 7
    public function toArray()
516
    {
517 7
        return array_filter([
518 7
            self::PROPERTY_ID => $this->uid->getId(),
519 7
            self::PROPERTY_TYPE => $this->type->getType(),
520 7
            self::PROPERTY_REVISION => $this->revision->getRevision(),
521 7
            self::PROPERTY_CREATED => $this->created->format('c'),
522 7
            self::PROPERTY_MODIFIED => $this->modified->format('c'),
523 7
            self::PROPERTY_PUBLISHED => ($this->published instanceof \DateTimeImmutable) ?
524 7
                $this->published->format('c') : null,
525 7
            self::PROPERTY_DELETED => ($this->deleted instanceof \DateTimeImmutable) ?
526 7
                $this->deleted->format('c') : null,
527 7
            self::PROPERTY_LOCATION => $this->location->toArray(),
528 7
            self::PROPERTY_LANGUAGE => $this->language,
529 7
        ]);
530
    }
531
}
532