Passed
Push — master ( 02e6b9...8f2f39 )
by y
01:57
created

AbstractEntity::_addWithPost()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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