Completed
Push — master ( 2d5c6f...ea456d )
by Alexander
06:30
created

SuccessResponseFactory   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 200
Duplicated Lines 12.5 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
dl 25
loc 200
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 9

10 Methods

Rating   Name   Duplication   Size   Complexity  
A make() 0 10 1
A transform() 0 21 4
A transformModel() 12 12 3
A transformCollection() 13 13 3
A transformBuilder() 0 4 1
A transformPaginator() 0 7 1
A transformData() 0 8 2
A serialize() 0 15 4
A includeStatusCode() 0 10 2
A resolveModel() 0 16 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Flugg\Responder\Factories;
4
5
use Flugg\Responder\Contracts\Manager;
6
use Flugg\Responder\Contracts\Transformable;
7
use Flugg\Responder\Transformer;
8
use Illuminate\Contracts\Pagination\LengthAwarePaginator as Paginator;
9
use Illuminate\Database\Eloquent\Builder;
10
use Illuminate\Http\JsonResponse;
11
use Illuminate\Support\Collection;
12
use InvalidArgumentException;
13
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
14
use League\Fractal\Resource\Collection as FractalCollection;
15
use League\Fractal\Resource\Item as FractalItem;
16
use League\Fractal\Resource\NullResource as FractalNull;
17
use League\Fractal\Resource\ResourceInterface;
18
19
/**
20
 *
21
 *
22
 * @package Laravel Responder
23
 * @author  Alexander Tømmerås <[email protected]>
24
 * @license The MIT License
25
 */
26
class SuccessResponseFactory extends ResponseFactory
27
{
28
    /**
29
     * Generate a successful JSON response.
30
     *
31
     * @param  mixed $data
32
     * @param  int   $statusCode
33
     * @param  array $meta
34
     * @return JsonResponse
35
     */
36
    public function make( $data = null, $statusCode = 200, $meta = [ ] ):JsonResponse
37
    {
38
        $resource = $this->transform( $data );
39
        $resource->setMeta( $meta );
40
41
        $data = $this->serialize( $resource );
42
        $data = $this->includeStatusCode( $statusCode, $data );
43
44
        return parent::make( $data, $statusCode );
45
    }
46
47
    /**
48
     * Transforms the data.
49
     *
50
     * @param  mixed            $data
51
     * @param  Transformer|null $transformer
52
     * @return ResourceInterface
53
     */
54
    protected function transform( $data = null, Transformer $transformer = null ):ResourceInterface
55
    {
56
        if ( is_null( $data ) ) {
57
            return new FractalNull();
58
        }
59
60
        $transforms = [
61
            Transformable::class => 'transformModel',
62
            Collection::class => 'transformCollection',
63
            Builder::class => 'transformBuilder',
64
            Paginator::class => 'transformPaginator'
65
        ];
66
67
        foreach ( $transforms as $class => $transform ) {
68
            if ( $data instanceof $class ) {
69
                return $this->$transform( $data, $transformer );
70
            }
71
        }
72
73
        throw new InvalidArgumentException( 'Data must be one or multiple models implementing the Transformable contract.' );
74
    }
75
76
    /**
77
     * Transform a transformable Eloquent model.
78
     *
79
     * @param  Transformable    $model
80
     * @param  Transformer|null $transformer
81
     * @return FractalItem
82
     */
83 View Code Duplication
    protected function transformModel( Transformable $model, Transformer $transformer = null ):FractalItem
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
84
    {
85
        $transformer = $transformer ?: $model::transformer();
86
87
        if ( is_null( $transformer ) ) {
88
            return new FractalItem( $model, function () use ( $model ) {
89
                return $model->toArray();
0 ignored issues
show
Bug introduced by
The method toArray() does not seem to exist on object<Flugg\Responder\Contracts\Transformable>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
90
            } );
91
        }
92
93
        return $this->transformData( $model, new $transformer( $model ), $model->getTable() );
94
    }
95
96
    /**
97
     * Transform a collection of Eloquent models.
98
     *
99
     * @param  Collection       $collection
100
     * @param  Transformer|null $transformer
101
     * @return FractalCollection
102
     */
103 View Code Duplication
    protected function transformCollection( Collection $collection, Transformer $transformer = null ):FractalCollection
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
    {
105
        $model = $this->resolveModel( $collection );
106
        $transformer = $transformer ?: $model::transformer();
107
108
        if ( is_null( $transformer ) ) {
109
            return new FractalCollection( $collection, function () use ( $collection ) {
110
                return $collection->toArray();
111
            } );
112
        }
113
114
        return $this->transformData( $collection, new $transformer( $model ), $model->getTable() );
115
    }
116
117
    /**
118
     * Transform an Eloquent builder.
119
     *
120
     * @param  Builder          $collection
0 ignored issues
show
Bug introduced by
There is no parameter named $collection. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
121
     * @param  Transformer|null $transformer
122
     * @return FractalCollection
123
     */
124
    protected function transformBuilder( Builder $query, Transformer $transformer = null ):FractalCollection
125
    {
126
        return $this->transformCollection( $query->get(), $transformer );
127
    }
128
129
    /**
130
     * Transform paginated data using Laravel's paginator.
131
     *
132
     * @param Paginator        $paginator
133
     * @param Transformer|null $transformer
134
     * @return FractalCollection
135
     */
136
    protected function transformPaginator( Paginator $paginator, Transformer $transformer = null ):FractalCollection
137
    {
138
        $resource = $this->transformCollection( $paginator->getCollection(), $transformer );
139
        $resource->setPaginator( new IlluminatePaginatorAdapter( $paginator ) );
140
141
        return $resource;
142
    }
143
144
    /**
145
     * Transform the data using the given transformer.
146
     *
147
     * @param  Transformable|Collection $data
148
     * @param  Transformer|null         $transformer
149
     * @param  string                   $resourceKey
150
     * @return ResourceInterface
151
     */
152
    protected function transformData( $data, Transformer $transformer, string $resourceKey ):ResourceInterface
153
    {
154
        $class = $data instanceof Transformable ? FractalItem::class : FractalCollection::class;
155
        $resource = new $class( $data, $transformer );
156
        $resource->setResourceKey( $resourceKey );
157
158
        return $resource;
159
    }
160
161
    /**
162
     * Serializes the data.
163
     *
164
     * @param  ResourceInterface $resource
165
     * @return array
166
     */
167
    protected function serialize( ResourceInterface $resource ):array
168
    {
169
        $manager = app( Manager::class );
170
171
        $data = $resource->getData();
172
        $model = $data instanceof Collection ? $this->resolveModel( $data ) : $data;
173
174
        if ( ! is_null( $data ) ) {
175
            $transformer = $model::transformer();
176
            $includes = is_string( $transformer ) ? ( new $transformer( $model ) )->getAvailableIncludes() : [ ];
177
            $manager = $manager->parseIncludes( $includes );
178
        }
179
180
        return $manager->createData( $resource )->toArray();
181
    }
182
183
    /**
184
     * Here we prepend a status code to the response data, if status code is enabled in
185
     * the configuration file.
186
     *
187
     * @param  int   $statusCode
188
     * @param  array $data
189
     * @return array
190
     */
191
    protected function includeStatusCode( int $statusCode, array $data ):array
192
    {
193
        if ( ! $this->includeStatusCode ) {
194
            return $data;
195
        }
196
197
        return array_merge( [
198
            'status' => $statusCode
199
        ], $data );
200
    }
201
202
    /**
203
     * Resolves model class path from a collection of models.
204
     *
205
     * @param  Collection $collection
206
     * @return Transformable
207
     * @throws InvalidArgumentException
208
     */
209
    protected function resolveModel( Collection $collection ):Transformable
210
    {
211
        $class = $collection->first();
212
213
        if ( ! $class instanceof Transformable ) {
214
            throw new InvalidArgumentException( 'Data must only contain models implementing the Transformable contract.' );
215
        }
216
217
        $collection->each( function ( $model ) use ( $class ) {
218
            if ( get_class( $model ) !== get_class( $class ) ) {
219
                throw new InvalidArgumentException( 'You cannot transform arrays or collections with multiple model types.' );
220
            }
221
        } );
222
223
        return $class;
224
    }
225
}