Completed
Push — master ( 1df19b...3d6a4b )
by Trevor N.
11s
created

Processor::buildModel()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 4
nop 4
crap 3
1
<?php
2
/**
3
 * Incoming
4
 *
5
 * @author    Trevor Suarez (Rican7)
6
 * @copyright (c) Trevor Suarez
7
 * @link      https://github.com/Rican7/incoming
8
 * @license   MIT
9
 */
10
11
declare(strict_types=1);
12
13
namespace Incoming;
14
15
use Incoming\Hydrator\Builder;
16
use Incoming\Hydrator\BuilderFactory;
17
use Incoming\Hydrator\ContextualBuilder;
18
use Incoming\Hydrator\ContextualHydrator;
19
use Incoming\Hydrator\Exception\UnresolvableBuilderException;
20
use Incoming\Hydrator\Exception\UnresolvableHydratorException;
21
use Incoming\Hydrator\Hydrator;
22
use Incoming\Hydrator\HydratorFactory;
23
use Incoming\Structure\Map;
24
use Incoming\Transformer\StructureBuilderTransformer;
25
use Incoming\Transformer\Transformer;
26
27
/**
28
 * A default implementation of both the `ModelProcessor` and `TypeProcessor`
29
 * for processing input data with an optional input transformation phase and
30
 * automatic hydrator and builder resolution.
31
 */
32
class Processor implements ModelProcessor, TypeProcessor
33
{
34
35
    /**
36
     * Properties
37
     */
38
39
    /**
40
     * An input transformer to pre-process the input data before hydration.
41
     *
42
     * @var Transformer
43
     */
44
    private $input_transformer;
45
46
    /**
47
     * A factory for building hydrators for a given model.
48
     *
49
     * @var HydratorFactory|null
50
     */
51
    private $hydrator_factory;
52
53
    /**
54
     * A factory for building builders for a given model.
55
     *
56
     * @var BuilderFactory|null
57
     */
58
    private $builder_factory;
59
60
    /**
61
     * A configuration flag that denotes whether hydration should always be run
62
     * after building a new model when processing specified types.
63
     *
64
     * @var bool
65
     */
66
    private $always_hydrate_after_building = false;
67
68
69
    /**
70
     * Methods
71
     */
72
73
    /**
74
     * Constructor
75
     *
76
     * @param Transformer|null $input_transformer The input transformer.
77
     * @param HydratorFactory|null $hydrator_factory A hydrator factory.
78
     * @param BuilderFactory|null $builder_factory A builder factory.
79
     * @param bool $always_hydrate_after_building A configuration flag that
80
     *  denotes whether hydration should always be run after building a new
81
     *  model when processing specified types.
82
     */
83 45
    public function __construct(
84
        Transformer $input_transformer = null,
85
        HydratorFactory $hydrator_factory = null,
86
        BuilderFactory $builder_factory = null,
87
        bool $always_hydrate_after_building = false
88
    ) {
89 45
        $this->input_transformer = $input_transformer ?: new StructureBuilderTransformer();
90 45
        $this->hydrator_factory = $hydrator_factory;
91 45
        $this->builder_factory = $builder_factory;
92 45
        $this->always_hydrate_after_building = $always_hydrate_after_building;
93 45
    }
94
95
    /**
96
     * Get the input transformer.
97
     *
98
     * @return Transformer The input transformer.
99
     */
100 3
    public function getInputTransformer(): Transformer
101
    {
102 3
        return $this->input_transformer;
103
    }
104
105
    /**
106
     * Set the input transformer.
107
     *
108
     * @param Transformer $input_transformer The input transformer.
109
     * @return $this This instance.
110
     */
111 3
    public function setInputTransformer(Transformer $input_transformer): self
112
    {
113 3
        $this->input_transformer = $input_transformer;
114
115 3
        return $this;
116
    }
117
118
    /**
119
     * Get the hydrator factory.
120
     *
121
     * @return HydratorFactory|null The hydrator factory.
122
     */
123 3
    public function getHydratorFactory()
124
    {
125 3
        return $this->hydrator_factory;
126
    }
127
128
    /**
129
     * Set the hydrator factory.
130
     *
131
     * @param HydratorFactory|null $hydrator_factory The hydrator factory.
132
     * @return $this This instance.
133
     */
134 9
    public function setHydratorFactory(HydratorFactory $hydrator_factory = null): self
135
    {
136 9
        $this->hydrator_factory = $hydrator_factory;
137
138 9
        return $this;
139
    }
140
141
    /**
142
     * Get the builder factory.
143
     *
144
     * @return BuilderFactory|null The builder factory.
145
     */
146 3
    public function getBuilderFactory()
147
    {
148 3
        return $this->builder_factory;
149
    }
150
151
    /**
152
     * Set the builder factory.
153
     *
154
     * @param BuilderFactory|null $builder_factory The builder factory.
155
     * @return $this This instance.
156
     */
157 6
    public function setBuilderFactory(BuilderFactory $builder_factory = null): self
158
    {
159 6
        $this->builder_factory = $builder_factory;
160
161 6
        return $this;
162
    }
163
164
    /**
165
     * Get the value of the configuration flag that denotes whether hydration
166
     * should always be run after building a new model when processing
167
     * specified types.
168
     *
169
     * @return bool The value of the flag.
170
     */
171 3
    public function getAlwaysHydrateAfterBuilding(): bool
172
    {
173 3
        return $this->always_hydrate_after_building;
174
    }
175
176
    /**
177
     * Set the value of the configuration flag that denotes whether hydration
178
     * should always be run after building a new model when processing
179
     * specified types.
180
     *
181
     * @param bool $always_hydrate_after_building Whether or not to always
182
     *  hydrate after building a new model when processing types.
183
     * @return $this This instance.
184
     */
185 9
    public function setAlwaysHydrateAfterBuilding(bool $always_hydrate_after_building): self
186
    {
187 9
        $this->always_hydrate_after_building = $always_hydrate_after_building;
188
189 9
        return $this;
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     *
195
     * If a hydrator isn't provided, an attempt will be made to automatically
196
     * resolve and build an appropriate hydrator from the provided factory.
197
     *
198
     * @param mixed $input_data The input data.
199
     * @param mixed $model The model to hydrate.
200
     * @param Hydrator|null $hydrator The hydrator to use in the process.
201
     * @param Map|null $context An optional generic key-value map, for providing
202
     *  contextual values during the process.
203
     * @return mixed The hydrated model.
204
     */
205 12
    public function processForModel($input_data, $model, Hydrator $hydrator = null, Map $context = null)
206
    {
207 12
        $input_data = $this->transformInput($input_data);
208
209 12
        return $this->hydrateModel($input_data, $model, $hydrator, $context);
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     *
215
     * If a builder isn't provided, an attempt will be made to automatically
216
     * resolve and build an appropriate builder from the provided factory.
217
     *
218
     * If a hydrator is provided, it will be used to hydrate the provided type
219
     * after building via the builder.
220
     *
221
     * If a hydrator isn't provided, but the "always_hydrate_after_building"
222
     * property is set to true, an attempt to hydrate the type will be made
223
     * after building via the builder, and the hydrator will be automatically
224
     * resolved from the provided factory.
225
     *
226
     * @param mixed $input_data The input data.
227
     * @param string $type The type to build.
228
     * @param Builder|null $builder The builder to use in the process.
229
     * @param Hydrator|null $hydrator An optional hydrator to use in the
230
     *  process, after the type is built, to aid in the full hydration of the
231
     *  resulting model.
232
     * @param Map|null $context An optional generic key-value map, for providing
233
     *  contextual values during the process.
234
     * @return mixed The built model.
235
     */
236 21
    public function processForType(
237
        $input_data,
238
        string $type,
239
        Builder $builder = null,
240
        Hydrator $hydrator = null,
241
        Map $context = null
242
    ) {
243 21
        $input_data = $this->transformInput($input_data);
244
245 21
        $model = $this->buildModel($input_data, $type, $builder, $context);
246
247 18
        if (null !== $hydrator || $this->always_hydrate_after_building) {
248 9
            $model = $this->hydrateModel($input_data, $model, $hydrator, $context);
249
        }
250
251 15
        return $model;
252
    }
253
254
    /**
255
     * Transform the input data.
256
     *
257
     * @param mixed $input_data The input data.
258
     * @return mixed The resulting transformed data.
259
     */
260 33
    protected function transformInput($input_data)
261
    {
262 33
        return $this->input_transformer->transform($input_data);
263
    }
264
265
    /**
266
     * Hydrate a model from incoming data.
267
     *
268
     * If a hydrator isn't provided, an attempt will be made to automatically
269
     * resolve and build an appropriate hydrator from the provided factory.
270
     *
271
     * @param mixed $input_data The input data.
272
     * @param mixed $model The model to hydrate.
273
     * @param Hydrator|null $hydrator The hydrator to use.
274
     * @param Map|null $context An optional generic key-value map, for providing
275
     *  contextual values during the process.
276
     * @return mixed The hydrated model.
277
     */
278 21
    protected function hydrateModel($input_data, $model, Hydrator $hydrator = null, Map $context = null)
279
    {
280 21
        if (null === $hydrator) {
281 12
            $hydrator = $this->getHydratorForModel($model);
282
        }
283
284 15
        if ($hydrator instanceof ContextualHydrator) {
285 3
            return $hydrator->hydrate($input_data, $model, $context);
286
        }
287
288 12
        return $hydrator->hydrate($input_data, $model);
289
    }
290
291
    /**
292
     * Build a model from incoming data.
293
     *
294
     * If a builder isn't provided, an attempt will be made to automatically
295
     * resolve and build an appropriate builder from the provided factory.
296
     *
297
     * @param mixed $input_data The input data.
298
     * @param string $type The type to build.
299
     * @param Builder|null $builder The builder to use.
300
     * @param Map|null $context An optional generic key-value map, for providing
301
     *  contextual values during the process.
302
     * @return mixed The built model.
303
     */
304 21
    protected function buildModel($input_data, string $type, Builder $builder = null, Map $context = null)
305
    {
306 21
        if (null === $builder) {
307 6
            $builder = $this->getBuilderForType($type);
308
        }
309
310 18
        if ($builder instanceof ContextualBuilder) {
311 3
            return $builder->build($input_data, $context);
312
        }
313
314 15
        return $builder->build($input_data);
315
    }
316
317
    /**
318
     * Get a Hydrator for a given model.
319
     *
320
     * @param mixed $model The model to get a hydrator for.
321
     * @throws UnresolvableHydratorException If a hydrator can't be resolved for
322
     *  the given model.
323
     * @return Hydrator The resulting hydrator.
324
     */
325 12
    protected function getHydratorForModel($model): Hydrator
326
    {
327 12
        if (null === $this->hydrator_factory) {
328 6
            throw UnresolvableHydratorException::forModel($model);
329
        }
330
331 6
        return $this->hydrator_factory->buildForModel($model);
332
    }
333
334
    /**
335
     * Get a Builder for a given model.
336
     *
337
     * @param string $type The type to get a builder for.
338
     * @throws UnresolvableBuilderException If a builder can't be resolved for
339
     *  the given model.
340
     * @return Builder The resulting builder.
341
     */
342 6
    protected function getBuilderForType(string $type): Builder
343
    {
344 6
        if (null === $this->builder_factory) {
345 3
            throw UnresolvableBuilderException::forType($type);
346
        }
347
348 3
        return $this->builder_factory->buildForType($type);
349
    }
350
}
351