Passed
Push — master ( 50a671...528684 )
by y
02:25
created

AbstractEntity::_setData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Helix\Asana\Base;
4
5
use Closure;
6
use Helix\Asana\Api\Cache;
7
use LogicException;
8
use RuntimeException;
9
10
/**
11
 * A resource with a GID.
12
 *
13
 * @method string   getGid          ()
14
 * @method bool     hasGid          ()
15
 */
16
abstract class AbstractEntity extends Data {
17
18
    /**
19
     * All entity classes must redeclare this to match their `resource_type`.
20
     */
21
    const TYPE = '';
22
23
    /**
24
     * `GET/PUT/DELETE` path.
25
     *
26
     * @return string
27
     */
28
    abstract public function __toString (): string;
29
30
    /**
31
     * Maps lazy-loaded / reloadable mapped {@see Data} to their proper expanded field expression.
32
     *
33
     * If not set here, the field name is used alone.
34
     *
35
     * @var string[]
36
     */
37
    protected static $optFields = [];
38
39
    /**
40
     * This is only provided for internal functionality related to the cache.
41
     *
42
     * Merges fresher data into the pooled instance.
43
     *
44
     * @param string $from
45
     * @param AbstractEntity $entity
46
     * @internal
47
     */
48
    public function __set (string $from, self $entity) {
49
        if ($from === Cache::class and $entity !== $this) {
50
            foreach ($entity->data as $field => $value) {
51
                if (!isset($this->diff[$field])) {
52
                    $this->data[$field] = $value;
53
                }
54
            }
55
        }
56
    }
57
58
    /**
59
     * @param string $addPath
60
     * @param array $data
61
     * @param string $field
62
     * @param array $diff
63
     * @return $this
64
     * @internal
65
     */
66
    protected function _addWithPost (string $addPath, array $data, string $field, array $diff) {
67
        if ($this->hasGid()) {
68
            return $this->_setWithPost($addPath, $data, $field);
69
        }
70
        return $this->_set($field, array_merge($this->data[$field] ?? [], array_values($diff)));
71
    }
72
73
    /**
74
     * Shortcut to update the cache.
75
     *
76
     * @internal
77
     */
78
    protected function _cache (): void {
79
        $this->api->getCache()->add($this);
80
    }
81
82
    /**
83
     * Activated by trait.
84
     *
85
     * @internal
86
     */
87
    protected function _delete (): void {
88
        $this->api->delete($this);
89
        $this->api->getCache()->remove($this);
90
    }
91
92
    /**
93
     * Lazy-loads missing fields.
94
     *
95
     * @param string $field
96
     * @return mixed
97
     */
98
    protected function _get (string $field) {
99
        if (!array_key_exists($field, $this->data) and isset($this->data['gid'])) {
100
            $this->reload($field);
101
        }
102
        return parent::_get($field);
103
    }
104
105
    /**
106
     * @param string $rmPath
107
     * @param array $data
108
     * @param string $field
109
     * @param array|Closure $diff An array to diff, or a filter closure.
110
     * @return $this
111
     * @internal
112
     */
113
    protected function _removeWithPost (string $rmPath, array $data, string $field, $diff) {
114
        if ($this->hasGid()) {
115
            return $this->_setWithPost($rmPath, $data, $field);
116
        }
117
        elseif (is_array($diff)) {
118
            return $this->_set($field, array_values(array_diff($this->data[$field] ?? [], $diff)));
119
        }
120
        return $this->_set($field, array_values(array_filter($this->data[$field] ?? [], $diff)));
121
    }
122
123
    /**
124
     * Called by create/update traits.
125
     *
126
     * @param string $dir `POST` directory for creation.
127
     * @return $this
128
     * @internal
129
     */
130
    protected function _save (string $dir = null) {
131
        if (isset($dir)) {
132
            $remote = $this->api->post($dir, $this->getDiff(), ['expand' => 'this']);
133
        }
134
        elseif ($this->isDiff()) {
135
            $remote = $this->api->put($this, $this->getDiff(), ['expand' => 'this']);
136
        }
137
        else {
138
            return $this;
139
        }
140
        /** @var array $remote */
141
        $this->_setData($remote);
142
        $this->_cache();
143
        return $this;
144
    }
145
146
    protected function _setData (array $data): void {
147
        // the resource type is constant. there's no need to keep it per-instance.
148
        unset($data['resource_type']);
149
        parent::_setData($data);
150
    }
151
152
    /**
153
     * Sets/reloads data via `POST` for existing entities. Otherwise stages a value.
154
     *
155
     * @param string $path
156
     * @param array $data
157
     * @param string $field
158
     * @param mixed $value Ignored for existing entities.
159
     * @return $this
160
     * @internal
161
     */
162
    protected function _setWithPost (string $path, array $data, string $field, $value = null) {
163
        if ($this->hasGid()) {
164
            /** @var array $remote */
165
            $remote = $this->api->post($path, $data, ['fields' => static::$optFields[$field] ?? $field]);
166
            if (array_key_exists($field, $this->data)) {
167
                $this->_setMapped($field, $remote[$field]);
168
                $this->_cache();
169
            }
170
            return $this;
171
        }
172
        return $this->_set($field, $value);
173
    }
174
175
    /**
176
     * Cache keys for the entity.
177
     *
178
     * @return string[]
179
     */
180
    public function getCacheKeys () {
181
        return [$this->getGid(), (string)$this];
182
    }
183
184
    /**
185
     * All entity classes must declare a `TYPE` constant.
186
     *
187
     * @return string
188
     */
189
    final public function getResourceType (): string {
190
        return $this::TYPE;
191
    }
192
193
    /**
194
     * Reloads a specific field, or the whole entity.
195
     *
196
     * @depends after-create
197
     * @param string $field
198
     * @return $this
199
     */
200
    public function reload (string $field = null) {
201
        if (!$this->hasGid()) {
202
            throw new LogicException(static::class . " has no GID, it can't be reloaded.");
203
        }
204
        if (isset($field)) {
205
            $optField = static::$optFields[$field] ?? $field;
206
            $value = $this->api->get($this, [], ['fields' => $optField])[$field] ?? null;
207
            $this->_setMapped($field, $value);
208
        }
209
        elseif ($remote = $this->api->get($this, [], ['expand' => 'this'])) {
210
            $this->_setData($remote);
211
        }
212
        else { // deleted upstream.
213
            $this->api->getCache()->remove($this);
214
            throw new RuntimeException("{$this} was deleted upstream.");
215
        }
216
        $this->_cache();
217
        return $this;
218
    }
219
220
}