Passed
Push — request-validation ( 12239e...665368 )
by Alex
03:15
created

JsonApiController   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 213
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 25
c 2
b 0
f 0
lcom 1
cbo 15
dl 0
loc 213
rs 9.1666

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 3
A indexAction() 0 19 4
A storeAction() 0 10 2
A showAction() 0 7 1
A updateAction() 0 12 2
A destroyAction() 0 7 1
A showRelationshipAction() 0 6 1
A updateRelationshipAction() 0 15 4
A findModelInstance() 0 12 3
A validateIncludableRelations() 0 8 4
1
<?php
2
3
namespace Huntie\JsonApi\Http\Controllers;
4
5
use Validator;
6
use Huntie\JsonApi\Contracts\Model\IncludesRelatedResources;
7
use Huntie\JsonApi\Exceptions\InvalidRelationPathException;
8
use Huntie\JsonApi\Http\JsonApiResponse;
9
use Huntie\JsonApi\Http\Concerns\QueriesResources;
10
use Huntie\JsonApi\Http\Concerns\UpdatesModelRelations;
11
use Huntie\JsonApi\Serializers\CollectionSerializer;
12
use Huntie\JsonApi\Serializers\RelationshipSerializer;
13
use Huntie\JsonApi\Serializers\ResourceSerializer;
14
use Huntie\JsonApi\Support\JsonApiErrors;
15
use Illuminate\Database\QueryException;
16
use Illuminate\Database\Eloquent\Model;
17
use Illuminate\Database\Eloquent\ModelNotFoundException;
18
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
19
use Illuminate\Foundation\Validation\ValidatesRequests;
20
use Illuminate\Http\Response;
21
use Illuminate\Routing\Controller;
22
use Illuminate\Validation\ValidationException;
23
24
abstract class JsonApiController extends Controller
25
{
26
    use JsonApiErrors;
27
    use QueriesResources;
28
    use UpdatesModelRelations;
29
    use AuthorizesRequests;
30
    use ValidatesRequests;
31
32
    /**
33
     * The Eloquent Model for the resource.
34
     *
35
     * @var Model|string
36
     */
37
    protected $model;
38
39
    /**
40
     * Create a new JsonApiController instance.
41
     */
42
    public function __construct()
43
    {
44
        if (is_string($this->model)) {
45
            if (!is_subclass_of($this->model, Model::class)) {
46
                $this->model = str_finish(config('jsonapi.model_namespace', app()->getNamespace()), '\\')
47
                    . preg_replace('/Controller$/', '', class_basename($this));
48
            }
49
50
            $this->model = new $this->model;
51
        }
52
    }
53
54
    /**
55
     * Return a listing of the resource.
56
     *
57
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
58
     * @param \Illuminate\Database\Eloquent\Builder|null   $query   Custom resource query
59
     *
60
     * @return JsonApiResponse
61
     */
62
    public function indexAction($request, $query = null)
63
    {
64
        $records = $query ?: $this->model->newQuery();
65
        $this->validateIncludableRelations($request->inputSet('include'));
66
67
        $records = $this->sortQuery($records, $request->inputSet('sort'));
68
        $records = $this->filterQuery($records, (array) $request->input('filter'));
69
70
        try {
71
            $pageSize = min($this->model->getPerPage(), $request->input('page.size'));
72
            $pageNumber = $request->input('page.number') ?: 1;
73
74
            $records = $records->paginate($pageSize, null, 'page', $pageNumber);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
75
        } catch (QueryException $e) {
76
            return $this->error(Response::HTTP_BAD_REQUEST, 'Invalid query parameters');
77
        }
78
79
        return new JsonApiResponse(new CollectionSerializer($records, $request->inputSet('fields'), $request->inputSet('include')));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
80
    }
81
82
    /**
83
     * Store a new record.
84
     *
85
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
86
     *
87
     * @return JsonApiResponse
88
     */
89
    public function storeAction($request)
90
    {
91
        $record = $this->model->create((array) $request->input('data.attributes'));
92
93
        if ($relationships = $request->input('data.relationships')) {
94
            $this->updateResourceRelationships($record, (array) $relationships);
95
        }
96
97
        return new JsonApiResponse(new ResourceSerializer($record), Response::HTTP_CREATED);
98
    }
99
100
    /**
101
     * Return a specified record.
102
     *
103
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
104
     * @param Model|mixed                                  $record
105
     *
106
     * @return JsonApiResponse
107
     */
108
    public function showAction($request, $record)
109
    {
110
        $record = $this->findModelInstance($record);
111
        $this->validateIncludableRelations($request->inputSet('include'));
112
113
        return new JsonApiResponse(new ResourceSerializer($record, $request->inputSet('fields'), $request->inputSet('include')));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 129 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
114
    }
115
116
    /**
117
     * Update a specified record.
118
     *
119
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
120
     * @param Model|mixed                                  $record
121
     *
122
     * @return JsonApiResponse
123
     */
124
    public function updateAction($request, $record)
125
    {
126
        $record = $this->findModelInstance($record);
127
        $record->fill((array) $request->input('data.attributes'));
128
        $record->save();
129
130
        if ($request->has('data.relationships')) {
131
            $this->updateRecordRelationships($record, (array) $request->input('data.relationships'));
0 ignored issues
show
Documentation Bug introduced by
The method updateRecordRelationships does not exist on object<Huntie\JsonApi\Ht...lers\JsonApiController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
132
        }
133
134
        return new JsonApiResponse(new ResourceSerializer($record));
135
    }
136
137
    /**
138
     * Destroy a specified record.
139
     *
140
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
141
     * @param Model|mixed                                  $record
142
     *
143
     * @return JsonApiResponse
144
     */
145
    public function destroyAction($request, $record)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
146
    {
147
        $record = $this->findModelInstance($record);
148
        $record->delete();
149
150
        return new JsonApiResponse(null, Response::HTTP_NO_CONTENT);
151
    }
152
153
    /**
154
     * Return a specified record relationship.
155
     *
156
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
157
     * @param Model|mixed                                  $record
158
     * @param string                                       $relation
159
     *
160
     * @return JsonApiResponse
161
     */
162
    public function showRelationshipAction($request, $record, $relation)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
163
    {
164
        $record = $this->findModelInstance($record);
165
166
        return new JsonApiResponse(new RelationshipSerializer($record, $relation));
167
    }
168
169
    /**
170
     * Update a named relationship on a specified record.
171
     *
172
     * http://jsonapi.org/format/#crud-updating-relationships
173
     *
174
     * @param \Huntie\JsonApi\Http\Requests\JsonApiRequest $request
175
     * @param Model|mixed                                  $record
176
     * @param string                                       $relation
177
     *
178
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
179
     *
180
     * @return JsonApiResponse
181
     */
182
    public function updateRelationshipAction($request, $record, $relation)
183
    {
184
        $record = $this->findModelInstance($record);
185
        $relationType = $this->getRelationType($relation);
186
187
        abort_unless(is_string($relationType) && $this->isFillableRelation($relation), Response::HTTP_NOT_FOUND);
188
189
        if ($relationType === 'To-One') {
190
            $this->updateToOneResourceRelationship($record, $relation, $request->input('data'));
0 ignored issues
show
Bug introduced by
It seems like $request->input('data') targeting Illuminate\Http\Concerns...ractsWithInput::input() can also be of type string; however, Huntie\JsonApi\Http\Conc...eResourceRelationship() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
191
        } else if ($relationType === 'To-Many') {
192
            $this->updateToManyResourceRelationship($record, $relation, $request->input('data'), $request->method());
0 ignored issues
show
Bug introduced by
It seems like $request->input('data') targeting Illuminate\Http\Concerns...ractsWithInput::input() can also be of type string; however, Huntie\JsonApi\Http\Conc...yResourceRelationship() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
193
        }
194
195
        return new JsonApiResponse(new RelationshipSerializer($record, $relation));
196
    }
197
198
    /**
199
     * Return existing instance of the resource or find by primary key.
200
     *
201
     * @param Model|mixed $record
202
     *
203
     * @throws ModelNotFoundException
204
     *
205
     * @return Model
206
     */
207
    protected function findModelInstance($record)
208
    {
209
        if ($record instanceof Model) {
210
            if (is_null($record->getKey())) {
211
                throw new ModelNotFoundException();
212
            }
213
214
            return $record;
215
        }
216
217
        return $this->model->findOrFail($record);
218
    }
219
220
    /**
221
     * Validate the requested included relationships against those that are
222
     * allowed on the requested resource type.
223
     *
224
     * @param array $relations
225
     *
226
     * @throws InvalidRelationPathException
227
     */
228
    protected function validateIncludableRelations(array $relations)
229
    {
230
        foreach ($relations as $relation) {
231
            if (!$this->model instanceof IncludesRelatedResources || !in_array($relation, $this->model->getIncludableRelations())) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
232
                throw new InvalidRelationPathException($relation);
233
            }
234
        }
235
    }
236
}
237