Passed
Push — master ( 866c3b...50a671 )
by y
08:13
created

AbstractEntity   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Importance

Changes 8
Bugs 1 Features 1
Metric Value
eloc 49
c 8
b 1
f 1
dl 0
loc 182
rs 10
wmc 26

10 Methods

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