AbstractEntity::_get()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 2
nop 1
dl 0
loc 5
rs 10
c 0
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
        assert($this->hasGid());
71
        $remote = $this->api->get($this, ['opt_fields' => static::OPT_FIELDS[$field] ?? $field]);
72
        assert(array_key_exists($field, $remote));
73
        $this->_setField($field, $remote[$field]);
74
        $this->api->getPool()->add($this);
75
    }
76
77
    protected function _setData (array $data): void {
78
        // meaningless once the entity is being created. it's constant.
79
        unset($data['resource_type'], $data['type']);
80
81
        parent::_setData($data);
82
    }
83
84
    /**
85
     * @return null|string
86
     */
87
    final public function getGid (): ?string {
88
        return $this->data['gid'] ?? null;
89
    }
90
91
    /**
92
     * Identifiers the entity is pooled with.
93
     *
94
     * @return string[]
95
     */
96
    public function getPoolKeys () {
97
        return [$this->getGid(), (string)$this];
98
    }
99
100
    /**
101
     * All entity classes must declare a `TYPE` constant.
102
     *
103
     * @return string
104
     */
105
    final public function getResourceType (): string {
106
        return static::TYPE;
107
    }
108
109
    /**
110
     * @return bool
111
     */
112
    final public function hasGid (): bool {
113
        return isset($this->data['gid']);
114
    }
115
116
    /**
117
     * Fully reloads the entity from Asana.
118
     *
119
     * @return $this
120
     */
121
    public function reload () {
122
        assert($this->hasGid());
123
        $remote = $this->api->get($this, ['opt_expand' => 'this']);
124
        if (!isset($remote['gid'])) { // deleted?
125
            $this->api->getPool()->remove($this->getPoolKeys());
126
            throw new RuntimeException("{$this} was deleted upstream.");
127
        }
128
        $this->_setData($remote);
129
        $this->api->getPool()->add($this);
130
        return $this;
131
    }
132
133
}