Completed
Push — master ( 178a89...75468d )
by Iqbal
07:18
created

ViewModel::getRawData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/*
3
 * This file is part of the Borobudur-Cqrs package.
4
 *
5
 * (c) Hexacodelabs <http://hexacodelabs.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Borobudur\Cqrs\ViewModel;
12
13
use Borobudur\Cqrs\Collection;
14
use Borobudur\Cqrs\Exception\InvalidArgumentException;
15
use Borobudur\Cqrs\ParameterBag;
16
use Borobudur\Cqrs\ReadModel\ReadModelInterface;
17
use ReflectionObject;
18
19
/**
20
 * @author      Iqbal Maulana <[email protected]>
21
 * @created     8/20/15
22
 */
23
class ViewModel extends AbstractViewModel
24
{
25
    /**
26
     * @var Aggregate|null
27
     */
28
    protected $aggregate;
29
30
    /**
31
     * @var string
32
     */
33
    protected $prefixMethod = 'normalize';
34
35
    /**
36
     * @var bool
37
     */
38
    protected $allowForceSingle = true;
39
40
    /**
41
     * @var ParameterBag
42
     */
43
    protected $parameter;
44
45
    /**
46
     * @var array
47
     */
48
    private $fields = array();
49
50
    /**
51
     * @var array
52
     */
53
    private $append = array();
54
55
    /**
56
     * @var array
57
     */
58
    private $hidden = array();
59
60
    /**
61
     * @var mixed
62
     */
63
    private $data;
64
65
    /**
66
     * @var array|null
67
     */
68
    private $built;
69
70
    /**
71
     * @var bool
72
     */
73
    private $forceSingle = false;
74
75
    /**
76
     * Constructor.
77
     *
78
     * @param mixed $data
79
     * @param array $parameter
80
     */
81
    public function __construct($data, array $parameter = array())
82
    {
83
        if (empty($data)) {
84
            $this->built = array();
85
        } else {
86
            $this->fields = $this->fields() ?: array();
87
            $this->append = $this->append() ?: array();
88
            $this->hidden = $this->hidden() ?: array();
89
            $this->parameter = new ParameterBag($parameter);
90
            $this->setData($data);
91
        }
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function build()
98
    {
99
        if (null !== $this->built) {
100
            return $this->built;
101
        }
102
103
        $record = $this->data;
104
105
        if ($record instanceof ReadModelInterface) {
106
            $built = $this->serialize($record, $record->serialize());
107
        } elseif (is_array($record)) {
108
            $built = $this->serialize($record, $record);
109
        } else {
110
            $built = array_map(function ($readModel) use ($record) {
111
                if ($readModel instanceof ReadModelInterface) {
112
                    $readModel = $readModel->serialize();
113
                }
114
115
                return $this->serialize($record, $readModel);
116
            }, $record->toArray());
117
        }
118
119
        if ($this->forceSingle) {
120
            $built = $built[0];
121
122
        }
123
124
        return $this->built = $built;
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function isCollection()
131
    {
132
        $build = $this->build();
133
        if (isset($build[0]) && is_array($build[0])) {
134
            return true;
135
        }
136
137
        return false;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function addParameter(array $parameters)
144
    {
145
        $this->parameter->add($parameters);
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function setForceSingle()
152
    {
153
        if ($this->allowForceSingle) {
154
            $this->forceSingle = true;
155
        }
156
    }
157
158
    /**
159
     * Add fields that should be viewed.
160
     *
161
     * @param array $fields
162
     */
163
    public function addFields(array $fields)
164
    {
165
        $this->fields = array_merge($this->fields, $fields);
166
    }
167
168
    /**
169
     * Add fields that should be appended.
170
     *
171
     * @param array $fields
172
     */
173
    public function addAppend(array $fields)
174
    {
175
        $this->append = array_merge($this->append, $fields);
176
    }
177
178
    /**
179
     * Add fields that should be hidden.
180
     *
181
     * @param array $fields
182
     */
183
    public function addHidden(array $fields)
184
    {
185
        $this->hidden = array_merge($this->fields, $fields);
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191
    public function buildViewModelArgs($results, array $record, $field)
192
    {
193
        $args = array();
194
        if (array_key_exists($field, $record)) {
195
            $args[] = $record[$field];
196
        }
197
198
        $args[] = $record;
199
200
        if ($results instanceof Collection) {
201
            $args[] = $results;
202
        }
203
204
        return $args;
205
    }
206
207
    /**
208
     * Get view model raw data.
209
     *
210
     * @return mixed
211
     */
212
    public function getRawData()
213
    {
214
        return $this->data;
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220
    protected function fields()
221
    {
222
        return array();
223
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228
    protected function append()
229
    {
230
        return array();
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     */
236
    protected function hidden()
237
    {
238
        return array();
239
    }
240
241
    /**
242
     * Serialize record based on view model.
243
     *
244
     * @param mixed $results
245
     * @param array $record
246
     *
247
     * @return array
248
     */
249
    private function serialize($results, array $record)
250
    {
251
        $fields = (array) $this->fields();
252
        $fields = !empty($fields) ? $fields : array_keys($record);
253
        $fields = $this->mergeFields($fields, (array) $this->append());
254
        $hidden = (array) $this->hidden();
255
256
        $data = array();
257
        foreach ($fields as $field) {
258
            if (in_array($field, $hidden)) {
259
                continue;
260
            }
261
262
            $data[$field] = $this->computeViewModelValue($results, $record, $field);
263
        }
264
265
        return $data;
266
    }
267
268
    /**
269
     * Normalize array record.
270
     *
271
     * @param mixed $record
272
     *
273
     * @return array
274
     */
275
    private function normalize($record)
276
    {
277
        if (is_array($record)) {
278
            foreach ($record as $index => $value) {
279
                $record[$index] = $this->normalize($value);
280
            }
281
        } elseif ($record instanceof Collection) {
282
            return $record->toArray();
283
        }
284
285
        return $record;
286
    }
287
288
    /**
289
     * Compute view model value.
290
     *
291
     * @param mixed  $results
292
     * @param array  $record
293
     * @param string $field
294
     *
295
     * @return mixed|null
296
     */
297
    private function computeViewModelValue($results, array $record, $field)
298
    {
299
        $method = $this->prefixMethod . ucfirst($field);
300
        if (method_exists($this, $method)) {
301
            return call_user_func_array(
302
                array($this, $method),
303
                $this->buildViewModelArgs($results, $record, $field)
304
            );
305
        }
306
307
        if (isset($record[$field])) {
308
            return $record[$field];
309
        }
310
311
        return null;
312
    }
313
314
    /**
315
     * Merged key fields.
316
     *
317
     * @param array $recordFields
318
     * @param array $viewModelFields
319
     *
320
     * @return array
321
     */
322
    private function mergeFields(array $recordFields, array $viewModelFields)
323
    {
324
        $merged = array_merge(array_flip($recordFields), array_flip($viewModelFields));
325
326
        return array_keys($merged);
327
    }
328
329
    /**
330
     * Set data.
331
     *
332
     * @param mixed $data
333
     */
334
    private function setData($data)
335
    {
336
        $data = $this->buildData($this->normalize($data));
337
        $this->assertDataType($data);
338
        $this->data = $data;
339
340
        if ($this->data instanceof Collection) {
341
            $this->aggregate = new Aggregate(
342
                $this->data->toArray(),
343
                $this,
344
                new ReflectionObject($this),
345
                $this->prefixMethod
346
            );
347
        }
348
    }
349
350
    /**
351
     * Build data.
352
     *
353
     * @param mixed $data
354
     *
355
     * @return mixed
356
     */
357
    private function buildData($data)
358
    {
359
        if (!$data instanceof ReadModelInterface) {
360
            if ($data instanceof ViewModel) {
361
                $data = $data->isCollection() ? new Collection($data->build()) : $data->build();
362
            } elseif (!$data instanceof Collection) {
363
                $data = new Collection($data);
364
            }
365
        }
366
367
        return $data;
368
    }
369
370
    /**
371
     * Assert data type.
372
     *
373
     * @param mixed $data
374
     */
375
    private function assertDataType($data)
376
    {
377
        if (!$data instanceof ReadModelInterface && !$data instanceof Collection && !is_array($data)) {
378
            throw new InvalidArgumentException(sprintf(
379
                'Acceptable data type is \Borobudur\Cqrs\ReadModel\ReadModelInterface, ' .
380
                '\Borobudur\Cqrs\Collection, Borobudur\Cqrs\ViewModel\ViewModel or nested array, but got "%s".',
381
                gettype($data)
382
            ));
383
        }
384
    }
385
}
386