Passed
Push — master ( 2428ce...06a092 )
by y
02:04
created

AbstractEntity::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace Helix\Asana\Base;
4
5
use RuntimeException;
6
7
/**
8
 * A resource with a GID.
9
 *
10
 * @see https://developers.asana.com/docs/object-hierarchy
11
 */
12
abstract class AbstractEntity extends Data {
13
14
    /**
15
     * All entity classes must redeclare this to match their REST directory.
16
     */
17
    const DIR = '';
18
19
    /**
20
     * All entity classes must redeclare this to match their `resource_type`.
21
     */
22
    const TYPE = '';
23
24
    /**
25
     * Defines `opt_fields` expressions for lazy-loading/reloading fields.
26
     *
27
     * If not set here, the field name is used as-is.
28
     *
29
     * @var string[] `fieldName => (expression)`
30
     */
31
    const OPT_FIELDS = [];
32
33
    /**
34
     * @param self $entity
35
     * @return bool
36
     * @internal pool
37
     */
38
    final public function __merge (self $entity): bool {
39
        $old = $this->toArray();
40
        $this->data = array_merge($this->data, array_diff_key($entity->data, $this->diff));
41
        return $this->toArray() !== $old;
42
    }
43
44
    /**
45
     * The entity's canonical REST path.
46
     *
47
     * @return string
48
     */
49
    final public function __toString (): string {
50
        return static::DIR . '/' . $this->getGid();
51
    }
52
53
    /**
54
     * Lazy-loads missing fields.
55
     *
56
     * @param string $field
57
     * @return mixed
58
     */
59
    protected function _get (string $field) {
60
        if (!array_key_exists($field, $this->data) and $this->hasGid()) {
61
            $this->_reload($field);
62
        }
63
        return parent::_get($field);
64
    }
65
66
    /**
67
     * @param string $field
68
     */
69
    protected function _reload (string $field): void {
70
        $remote = $this->api->get($this, ['opt_fields' => static::OPT_FIELDS[$field] ?? $field]);
71
        $this->_setField($field, $remote[$field]);
72
        $this->api->getPool()->add($this);
73
    }
74
75
    protected function _setData (array $data): void {
76
        // meaningless once the entity is being created. it's constant.
77
        unset($data['resource_type'], $data['type']);
78
79
        parent::_setData($data);
80
    }
81
82
    /**
83
     * @return null|string
84
     */
85
    final public function getGid (): ?string {
86
        return $this->data['gid'] ?? null;
87
    }
88
89
    /**
90
     * Identifiers the entity is pooled with.
91
     *
92
     * @return string[]
93
     */
94
    public function getPoolKeys () {
95
        return [$this->getGid(), (string)$this];
96
    }
97
98
    /**
99
     * All entity classes must declare a `TYPE` constant.
100
     *
101
     * @return string
102
     */
103
    final public function getResourceType (): string {
104
        return static::TYPE;
105
    }
106
107
    /**
108
     * @return bool
109
     */
110
    final public function hasGid (): bool {
111
        return isset($this->data['gid']);
112
    }
113
114
    /**
115
     * Fully reloads the entity from Asana.
116
     *
117
     * @depends after-create
118
     * @return $this
119
     */
120
    public function reload () {
121
        $remote = $this->api->get($this, ['opt_expand' => 'this']);
122
        if (!isset($remote['gid'])) { // null and dir guard
123
            $this->api->getPool()->remove($this->getPoolKeys());
124
            throw new RuntimeException("{$this} was not found upstream.");
125
        }
126
        $this->_setData($remote);
127
        $this->api->getPool()->add($this);
128
        return $this;
129
    }
130
131
}