Data::toArray()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
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 1
nop 0
dl 0
loc 8
rs 10
1
<?php
2
3
namespace Helix\Shopify\Base;
4
5
use Base\Data\JsonSerializableTrait;
6
use Base\Data\MagicMethodTrait;
7
use Helix\Shopify\Api;
8
use Helix\Shopify\Api\Pool;
9
use JsonSerializable;
10
use Serializable;
11
12
/**
13
 * A data-object supporting annotated magic access.
14
 */
15
class Data extends \Base\Data implements JsonSerializable, Serializable
16
{
17
18
    use MagicMethodTrait;
19
    use JsonSerializableTrait;
20
21
    /**
22
     * @var array
23
     */
24
    const MAP = [];
25
26
    /**
27
     * Fields always added to diffs.
28
     *
29
     * @var string[]
30
     */
31
    const PATCH = [];
32
33
    /**
34
     * @var Api
35
     */
36
    protected $api;
37
38
    /**
39
     * @var Pool
40
     */
41
    protected $pool;
42
43
    /**
44
     * @param mixed $caller
45
     * @return Api
46
     * @internal
47
     */
48
    final protected static function _getApi($caller)
49
    {
50
        if ($caller instanceof self) {
51
            return $caller->api;
52
        }
53
        assert($caller instanceof Api);
54
        return $caller;
55
    }
56
57
    /**
58
     * @param Api|Data $caller
59
     * @param array $data
60
     */
61
    public function __construct($caller, array $data = [])
62
    {
63
        parent::__construct();
64
        $this->api = self::_getApi($caller);
65
        $this->pool = $this->api->getPool();
66
        $this->_setData($data);
67
    }
68
69
    /**
70
     * @param string $method
71
     * @param array $args
72
     * @return mixed
73
     */
74
    public function __call(string $method, array $args)
75
    {
76
        static $magic = [];
77
        if (!$call =& $magic[$method]) {
78
            preg_match('/^([a-z]+)(.+)$/', $method, $call);
79
            $call[1] = '_' . $call[1];
80
            if ($call[1] !== '_select') { // _select() calls getters
81
                $call[2] = preg_replace_callback('/[A-Z]/', function (array $match) {
82
                    return '_' . lcfirst($match[0]);
83
                }, lcfirst($call[2]));
84
            }
85
        }
86
        return $this->{$call[1]}($call[2], ...$args);
87
    }
88
89
    /**
90
     * A factory that also hydrates / caches entities.
91
     *
92
     * @param string $class
93
     * @param mixed $item
94
     * @return mixed
95
     */
96
    protected function _hydrate(string $class, $item)
97
    {
98
        if (!isset($item) or $item instanceof self) {
99
            return $item;
100
        }
101
        // hydrate entities
102
        if (is_subclass_of($class, AbstractEntity::class)) {
103
            if (is_string($item)) { // convert ids to lazy stubs
104
                $item = ['id' => $item];
105
            }
106
            return $this->pool->get($item['id'], $this, function () use ($class, $item) {
107
                return $this->api->factory($this, $class, $item);
108
            });
109
        }
110
        // hydrate simple
111
        return $this->api->factory($this, $class, $item);
112
    }
113
114
    /**
115
     * Magic method: `selectField(callable $filter)`
116
     *
117
     * Where `Field` has an accessor at `getField()`, either real or magic.
118
     *
119
     * This can also be used to select from an arbitrary iterable.
120
     *
121
     * @see __call()
122
     *
123
     * @param string|iterable $subject
124
     * @param callable $filter `fn( Data $object ): bool`
125
     * @param array $args
126
     * @return array
127
     */
128
    protected function _select($subject, callable $filter, ...$args)
129
    {
130
        if (is_string($subject)) {
131
            $subject = $this->{'get' . $subject}(...$args) ?? [];
132
        }
133
        $selected = [];
134
        foreach ($subject as $item) {
135
            if (call_user_func($filter, $item)) {
136
                $selected[] = $item;
137
            }
138
        }
139
        return $selected;
140
    }
141
142
    /**
143
     * Clears all diffs and sets all data, hydrating mapped fields.
144
     *
145
     * @param array $data
146
     * @return $this
147
     */
148
    protected function _setData(array $data)
149
    {
150
        $this->data = $this->diff = [];
151
        foreach ($data as $field => $value) {
152
            $this->_setField($field, $value);
153
        }
154
        return $this;
155
    }
156
157
    /**
158
     * Sets a value, hydrating if mapped, and clears the diff.
159
     *
160
     * @param string $field
161
     * @param mixed $value
162
     * @return $this
163
     */
164
    protected function _setField(string $field, $value)
165
    {
166
        if (isset(static::MAP[$field])) {
167
            $class = static::MAP[$field];
168
            if (is_array($class)) {
169
                $value = array_map(function ($each) use ($class) {
170
                    return $this->_hydrate($class[0], $each);
171
                }, $value);
172
            } elseif (isset($value)) {
173
                $value = $this->_hydrate($class, $value);
174
            }
175
        }
176
        $this->data[$field] = $value;
177
        unset($this->diff[$field]);
178
        return $this;
179
    }
180
181
    /**
182
     * Dehydrated JSON encode.
183
     *
184
     * @return string
185
     */
186
    public function serialize(): string
187
    {
188
        return json_encode($this, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
189
    }
190
191
    /**
192
     * @return array
193
     */
194
    public function toArray(): array
195
    {
196
        return array_map(function ($value) {
197
            if ($value instanceof self) {
198
                return $value->toArray();
199
            }
200
            return $value;
201
        }, $this->data);
202
    }
203
204
    /**
205
     * @param $serialized
206
     */
207
    public function unserialize($serialized): void
208
    {
209
        $this->data = json_decode($serialized, true);
210
    }
211
}