Passed
Push — master ( f968a7...f474ea )
by y
07:21
created

AbstractEntity::_setData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Helix\Asana\Base;
4
5
use Closure;
6
use LogicException;
7
use RuntimeException;
8
9
/**
10
 * A resource with a GID.
11
 *
12
 * @see https://developers.asana.com/docs/object-hierarchy
13
 */
14
abstract class AbstractEntity extends Data {
15
16
    /**
17
     * All entity classes must redeclare this to match their `resource_type`.
18
     */
19
    const TYPE = '';
20
21
    /**
22
     * Defines `opt_fields` expressions for lazy-loading/reloading fields.
23
     *
24
     * If not set here, the field name is used as-is.
25
     *
26
     * @var string[] `fieldName => (expression)`
27
     */
28
    const OPT_FIELDS = [];
29
30
    /**
31
     * `GET/PUT/DELETE` path.
32
     *
33
     * @return string
34
     */
35
    abstract public function __toString (): string;
36
37
    /**
38
     * @param self $entity
39
     * @return bool
40
     * @internal pool
41
     */
42
    final public function __merge (self $entity): bool {
43
        $old = $this->toArray();
44
        $this->data = array_merge($this->data, array_diff_key($entity->data, $this->diff));
45
        return $this->toArray() !== $old;
46
    }
47
48
    /**
49
     * @param string $addPath
50
     * @param array $data
51
     * @param string $field
52
     * @param array $diff
53
     * @return $this
54
     * @internal
55
     */
56
    protected function _addWithPost (string $addPath, array $data, string $field, array $diff) {
57
        if ($this->hasGid()) {
58
            return $this->_setWithPost($addPath, $data, $field);
59
        }
60
        return $this->_set($field, array_merge($this->data[$field] ?? [], array_values($diff)));
61
    }
62
63
    /**
64
     * Lazy-loads missing fields.
65
     *
66
     * @param string $field
67
     * @return mixed
68
     */
69
    protected function _get (string $field) {
70
        if (!array_key_exists($field, $this->data) and $this->hasGid()) {
71
            $this->reload($field);
72
        }
73
        return parent::_get($field);
74
    }
75
76
    /**
77
     * @param string $rmPath
78
     * @param array $data
79
     * @param string $field
80
     * @param array|Closure $diff An array to diff, or a filter closure.
81
     * @return $this
82
     * @internal
83
     */
84
    protected function _removeWithPost (string $rmPath, array $data, string $field, $diff) {
85
        if ($this->hasGid()) {
86
            return $this->_setWithPost($rmPath, $data, $field);
87
        }
88
        elseif (is_array($diff)) {
89
            return $this->_set($field, array_values(array_diff($this->data[$field] ?? [], $diff)));
90
        }
91
        return $this->_set($field, array_values(array_filter($this->data[$field] ?? [], $diff)));
92
    }
93
94
    protected function _setData (array $data): void {
95
        // make sure the gid is consistently null|string across all entities.
96
        $data['gid'] = (string)$data['gid'];
97
        if (empty($data['gid'])) {
98
            $data['gid'] = null;
99
        }
100
101
        // meaningless once the entity is being created. it's constant.
102
        unset($data['resource_type'], $data['type']);
103
104
        parent::_setData($data);
105
    }
106
107
    /**
108
     * Sets/reloads data via `POST` for existing entities. Otherwise stages a value.
109
     *
110
     * @param string $path
111
     * @param array $data
112
     * @param string $field
113
     * @param mixed $value Ignored for existing entities.
114
     * @return $this
115
     * @internal
116
     */
117
    protected function _setWithPost (string $path, array $data, string $field, $value = null) {
118
        if ($this->hasGid()) {
119
            /** @var array $remote */
120
            $remote = $this->api->post($path, $data, ['fields' => static::OPT_FIELDS[$field] ?? $field]);
121
            if (array_key_exists($field, $this->data)) {
122
                $this->_setMapped($field, $remote[$field]);
123
                $this->api->getPool()->add($this);
124
            }
125
            return $this;
126
        }
127
        return $this->_set($field, $value);
128
    }
129
130
    /**
131
     * @return null|string
132
     */
133
    final public function getGid (): ?string {
134
        return $this->data['gid'];
135
    }
136
137
    /**
138
     * Identifiers the entity is pooled with.
139
     *
140
     * @return string[]
141
     */
142
    public function getPoolKeys () {
143
        return [$this->getGid(), (string)$this];
144
    }
145
146
    /**
147
     * All entity classes must declare a `TYPE` constant.
148
     *
149
     * @return string
150
     */
151
    final public function getResourceType (): string {
152
        return $this::TYPE;
153
    }
154
155
    /**
156
     * @return bool
157
     */
158
    final public function hasGid (): bool {
159
        return isset($this->data['gid']);
160
    }
161
162
    /**
163
     * Reloads a specific field, or the whole entity.
164
     *
165
     * @depends after-create
166
     * @param string $field
167
     * @return $this
168
     */
169
    public function reload (string $field = null) {
170
        if (!$this->hasGid()) { // guards against loading a remote dir.
171
            throw new LogicException(static::class . " has no GID, it can't be reloaded.");
172
        }
173
        if (isset($field)) {
174
            $remote = $this->api->get($this, [], ['fields' => static::OPT_FIELDS[$field] ?? $field]);
175
            $this->_setMapped($field, $remote[$field] ?? null);
176
        }
177
        elseif ($remote = $this->api->get($this, [], ['expand' => 'this'])) {
178
            $this->_setData($remote);
179
        }
180
        else { // deleted upstream.
181
            $this->api->getPool()->remove($this->getPoolKeys());
182
            throw new RuntimeException("{$this} was deleted upstream.");
183
        }
184
        $this->api->getPool()->add($this);
185
        return $this;
186
    }
187
188
}