Passed
Push — master ( 6daea9...4e43a7 )
by Alexander
04:11
created

TransformBuilder::relationsWithoutParameters()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 0
dl 0
loc 17
ccs 7
cts 7
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Flugg\Responder;
4
5
use Flugg\Responder\Contracts\Pagination\PaginatorFactory;
6
use Flugg\Responder\Contracts\Resources\ResourceFactory;
7
use Flugg\Responder\Contracts\TransformFactory;
8
use Flugg\Responder\Exceptions\InvalidSuccessSerializerException;
9
use Flugg\Responder\Pagination\CursorPaginator;
10
use Flugg\Responder\Transformers\Transformer as BaseTransformer;
11
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
12
use Illuminate\Database\Eloquent\Collection;
13
use Illuminate\Database\Eloquent\Model;
14
use League\Fractal\Pagination\Cursor;
15
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
16
use League\Fractal\Resource\Collection as CollectionResource;
17
use League\Fractal\Resource\NullResource;
18
use League\Fractal\Serializer\SerializerAbstract;
19
20
/**
21
 * A builder class responsible for building transformed arrays.
22
 *
23
 * @package flugger/laravel-responder
24
 * @author  Alexander Tømmerås <[email protected]>
25
 * @license The MIT License
26
 */
27
class TransformBuilder
28
{
29
    /**
30
     * A factory class for making Fractal resources.
31
     *
32
     * @var \Flugg\Responder\Contracts\Resources\ResourceFactory
33
     */
34
    protected $resourceFactory;
35
36
    /**
37
     * A factory for making transformed arrays.
38
     *
39
     * @var \Flugg\Responder\Contracts\TransformFactory
40
     */
41
    private $transformFactory;
42
43
    /**
44
     * A factory used to build Fractal paginator adapters.
45
     *
46
     * @var \Flugg\Responder\Contracts\Pagination\PaginatorFactory
47
     */
48
    protected $paginatorFactory;
49
50
    /**
51
     * The resource that's being built.
52
     *
53
     * @var \League\Fractal\Resource\ResourceInterface
54
     */
55
    protected $resource;
56
57
    /**
58
     * A serializer for formatting data after transforming.
59
     *
60
     * @var \League\Fractal\Serializer\SerializerAbstract
61
     */
62
    protected $serializer;
63
64
    /**
65
     * A list of included relations.
66
     *
67
     * @var array
68
     */
69
    protected $with = [];
70
71
    /**
72
     * A list of excluded relations.
73
     *
74
     * @var array
75
     */
76
    protected $without = [];
77
78
    /**
79
     * A list of sparse fieldsets.
80
     *
81
     * @var array
82
     */
83
    protected $only = [];
84
85
    /**
86
     * Construct the builder class.
87
     *
88
     * @param \Flugg\Responder\Contracts\Resources\ResourceFactory   $resourceFactory
89
     * @param \Flugg\Responder\Contracts\TransformFactory            $transformFactory
90
     * @param \Flugg\Responder\Contracts\Pagination\PaginatorFactory $paginatorFactory
91
     */
92 18
    public function __construct(ResourceFactory $resourceFactory, TransformFactory $transformFactory, PaginatorFactory $paginatorFactory)
93
    {
94 18
        $this->resourceFactory = $resourceFactory;
95 18
        $this->transformFactory = $transformFactory;
96 18
        $this->paginatorFactory = $paginatorFactory;
97 18
    }
98
99
    /**
100
     * Make a resource from the given data and transformer and set the resource key.
101
     *
102
     * @param  mixed                                                          $data
103
     * @param  \Flugg\Responder\Transformers\Transformer|callable|string|null $transformer
104
     * @param  string|null                                                    $resourceKey
105
     * @return $this
106
     */
107 17
    public function resource($data = null, $transformer = null, string $resourceKey = null)
108
    {
109 17
        $this->resource = $this->resourceFactory->make($data, $transformer, $resourceKey);
110
111 17
        if ($data instanceof CursorPaginator) {
112 1
            $this->cursor($this->paginatorFactory->makeCursor($data));
113
        } elseif ($data instanceof LengthAwarePaginator) {
114 1
            $this->paginator($this->paginatorFactory->make($data));
0 ignored issues
show
Compatibility introduced by
$this->paginatorFactory->make($data) of type object<League\Fractal\Pa...ion\PaginatorInterface> is not a sub-type of object<League\Fractal\Pa...minatePaginatorAdapter>. It seems like you assume a concrete implementation of the interface League\Fractal\Pagination\PaginatorInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
115
        }
116
117 17
        return $this;
118
    }
119
120
    /**
121
     * Manually set the cursor on the resource.
122
     *
123
     * @param  \League\Fractal\Pagination\Cursor $cursor
124
     * @return $this
125
     */
126 2
    public function cursor(Cursor $cursor)
127
    {
128 2
        if ($this->resource instanceof CollectionResource) {
129 2
            $this->resource->setCursor($cursor);
130
        }
131
132 2
        return $this;
133
    }
134
135
    /**
136
     * Manually set the paginator on the resource.
137
     *
138
     * @param  \League\Fractal\Pagination\IlluminatePaginatorAdapter $paginator
139
     * @return $this
140
     */
141 2
    public function paginator(IlluminatePaginatorAdapter $paginator)
142
    {
143 2
        if ($this->resource instanceof CollectionResource) {
144 2
            $this->resource->setPaginator($paginator);
145
        }
146
147 2
        return $this;
148
    }
149
150
    /**
151
     * Add meta data appended to the response data.
152
     *
153
     * @param  array $data
154
     * @return $this
155
     */
156 1
    public function meta(array $data)
157
    {
158 1
        $this->resource->setMeta($data);
159
160 1
        return $this;
161
    }
162
163
    /**
164
     * Include relations to the transform.
165
     *
166
     * @param  string[]|string $relations
167
     * @return $this
168
     */
169 4
    public function with($relations)
170
    {
171 4
        $this->with = array_merge($this->with, is_array($relations) ? $relations : func_get_args());
172
173 4
        return $this;
174
    }
175
176
    /**
177
     * Exclude relations from the transform.
178
     *
179
     * @param  string[]|string $relations
180
     * @return $this
181
     */
182 2
    public function without($relations)
183
    {
184 2
        $this->without = array_merge($this->without, is_array($relations) ? $relations : func_get_args());
185
186 2
        return $this;
187
    }
188
189
    /**
190
     * Filter fields to output using sparse fieldsets.
191
     *
192
     * @param  string[]|string $fields
193
     * @return $this
194
     */
195 2
    public function only($fields)
196
    {
197 2
        $this->only = array_merge($this->only, is_array($fields) ? $fields : func_get_args());
198
199 2
        return $this;
200
    }
201
202
    /**
203
     * Set the serializer.
204
     *
205
     * @param  \League\Fractal\Serializer\SerializerAbstract|string $serializer
206
     * @return $this
207
     * @throws \Flugg\Responder\Exceptions\InvalidSuccessSerializerException
208
     */
209 18
    public function serializer($serializer)
210
    {
211 18
        if (is_string($serializer)) {
212 2
            $serializer = new $serializer;
213
        }
214
215 18
        if (! $serializer instanceof SerializerAbstract) {
216 1
            throw new InvalidSuccessSerializerException;
217
        }
218
219 18
        $this->serializer = $serializer;
220
221 18
        return $this;
222
    }
223
224
    /**
225
     * Transform and serialize the data and return the transformed array.
226
     *
227
     * @return array
228
     */
229 11
    public function transform(): array
230
    {
231 11
        $this->prepareRelations($this->resource->getData(), $this->resource->getTransformer());
232
233 11
        return $this->transformFactory->make($this->resource ?: new NullResource, $this->serializer, [
234 11
            'includes' => $this->with,
235 11
            'excludes' => $this->without,
236 11
            'fieldsets' => $this->only,
237
        ]);
238
    }
239
240
    /**
241
     * Prepare requested relations for the transformation.
242
     *
243
     * @param  mixed                                                          $data
244
     * @param  \Flugg\Responder\Transformers\Transformer|callable|string|null $transformer
245
     * @return void
246
     */
247 11
    protected function prepareRelations($data, $transformer)
248
    {
249 11
        if ($transformer instanceof BaseTransformer) {
250 2
            $this->includeTransformerRelations($transformer);
251
        }
252
253 11
        if ($data instanceof Model || $data instanceof Collection) {
254 2
            $data->load($this->relationsWithoutParameters());
255
        }
256
257 11
        $this->with = $this->stripEagerLoadConstraints($this->with);
258 11
    }
259
260
    /**
261
     * Include default relationships and add eager load constraints from transformer.
262
     *
263
     * @param  \Flugg\Responder\Transformers\Transformer $transformer
264
     * @return void
265
     */
266
    protected function includeTransformerRelations(BaseTransformer $transformer)
267
    {
268 2
        $relations = array_filter(array_keys($this->with), function ($relation) {
269 2
            return ! is_numeric($relation);
270 2
        });
271
272 2
        $this->with(Collection::make($transformer->defaultRelations())
273 2
            ->filter(function ($constrain, $relation) use ($relations) {
274 1
                return ! in_array(is_numeric($relation) ? $constrain : $relation, $relations);
275 2
            })->all());
276 2
    }
277
278
    /**
279
     * Remove eager load constraint functions from the given relations.
280
     *
281
     * @param  array $relations
282
     * @return array
283
     */
284
    protected function stripEagerLoadConstraints(array $relations): array
285
    {
286 11
        return collect($relations)->map(function ($value, $key) {
287 4
            return is_numeric($key) ? $value : $key;
288 11
        })->values()->all();
289
    }
290
291
    /**
292
     * Remove parameters from relations that must be loaded.
293
     *
294
     * @return array
295
     */
296 2
    protected function relationsWithoutParameters(): array
297
    {
298 2
        $cleanedRelations = [];
299 2
        foreach ($this->with as $key => $value) {
300
            // If the key is numeric, value is the relation name:
301
            //  we remove parameters from the value and return the relation name
302
            // Otherwise the key is the relation name and the value is a custom scope:
303
            //  we remove parameters from the key and return the relation with the value untouched
304 2
            if(is_numeric($key)) {
305 2
                $cleanedRelations[$key] = explode(':', $value)[0];
306
            } else {
307 2
                $cleanedRelations[explode(':', $key)[0]] = $value;
308
            }
309
        }
310
311 2
        return $cleanedRelations;
312
    }
313
}