Passed
Push — query-params ( 645af4...55d9e2 )
by Alex
02:43
created

JsonApiController::indexAction()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 4
Metric Value
c 4
b 0
f 4
dl 0
loc 23
rs 8.5906
cc 5
eloc 14
nc 12
nop 1
1
<?php
2
3
namespace Huntie\JsonApi\Http\Controllers;
4
5
use Huntie\JsonApi\Http\JsonApiResponse;
6
use Huntie\JsonApi\Support\JsonApiErrors;
7
use Illuminate\Database\QueryException;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Http\Request;
10
use Illuminate\Http\Response;
11
use Illuminate\Routing\Controller;
12
13
abstract class JsonApiController extends Controller
14
{
15
    use JsonApiErrors;
16
17
    /**
18
     * Return the Eloquent Model for the resource.
19
     *
20
     * @return Model
21
     */
22
    abstract protected function getModel();
23
24
    /**
25
     * Return the type name of the resource.
26
     *
27
     * @return string
28
     */
29
    protected function getModelType()
30
    {
31
        return $this->getModel()->getTable();
32
    }
33
34
    /**
35
     * Return a listing of the resource.
36
     *
37
     * @param Request $request
38
     *
39
     * @return JsonApiResponse
40
     */
41
    public function indexAction(Request $request)
42
    {
43
        $records = $this->getModel()->newQuery();
44
        $params = $this->getRequestParameters($request);
45
46
        foreach ($params['sort'] as $expression) {
47
            $direction = substr($expression, 0, 1) === '-' ? 'desc' : 'asc';
48
            $column = preg_replace('/^\-/', '', $expression);
49
            $records = $records->orderby($column, $direction);
50
        }
51
52
        foreach ($params['filter'] as $attribute => $value) {
0 ignored issues
show
Bug introduced by
The expression $params['filter'] of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
53
            $records = $records->where($attribute, '=', $value);
54
        }
55
56
        try {
57
            $records = $records->get();
58
        } catch (QueryException $e) {
59
            return $this->error(Response::HTTP_BAD_REQUEST, 'Invalid query parameters');
60
        }
61
62
        return new JsonApiResponse($this->transformCollection($records, $params['fields']));
63
    }
64
65
    /**
66
     * Store a new record.
67
     *
68
     * @param Request $request
69
     *
70
     * @return JsonApiResponse
71
     */
72
    public function storeAction(Request $request)
73
    {
74
        $record = $this->getModel()->create((array) $request->input('data.attributes'));
75
76
        return new JsonApiResponse($this->transformRecord($record), Response::HTTP_CREATED);
77
    }
78
79
    /**
80
     * Return a specified record.
81
     *
82
     * @param Request   $request
83
     * @param Model|int $record
84
     *
85
     * @return JsonApiResponse
86
     */
87
    public function showAction(Request $request, $record)
88
    {
89
        $record = $record instanceof Model ? $record : $this->findModelInstance($record);
90
        $params = $this->getRequestParameters($request);
91
92
        return new JsonApiResponse($this->transformRecord($record, $params['fields'], $params['include']));
93
    }
94
95
    /**
96
     * Update a specified record.
97
     *
98
     * @param Request   $request
99
     * @param Model|int $record
100
     *
101
     * @return JsonApiResponse
102
     */
103
    public function updateAction(Request $request, $record)
104
    {
105
        $record = $record instanceof Model ? $record : $this->findModelInstance($record);
106
        $record->update((array) $request->input('data.attributes'));
107
108
        return $this->showAction($request, $record);
109
    }
110
111
    /**
112
     * Destroy a specified record.
113
     *
114
     * @param Request   $request
115
     * @param Model|int $record
116
     *
117
     * @return JsonApiResponse
118
     */
119
    public function destroyAction(Request $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...
120
    {
121
        $record = $record instanceof Model ? $record : $this->findModelInstance($record);
122
        $record->delete();
123
124
        return new JsonApiResponse(null, Response::HTTP_NO_CONTENT);
125
    }
126
127
    /**
128
     * Return an instance of the resource by primary key.
129
     *
130
     * @param int $key
131
     *
132
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
133
     *
134
     * @return Model
135
     */
136
    protected function findModelInstance($key)
137
    {
138
        return $this->getModel()->findOrFail($key);
139
    }
140
141
    /**
142
     * Return any JSON API resource parameters from a request.
143
     *
144
     * @param Request $request
145
     *
146
     * @return array
147
     */
148
    protected function getRequestParameters($request)
149
    {
150
        return [
151
            'fields' => $this->getRequestQuerySet($request, 'fields.' . $this->getModelType()),
152
            'include' => $this->getRequestQuerySet($request, 'include'),
153
            'sort' => $this->getRequestQuerySet($request, 'sort'),
154
            'filter' => $request->input('filter'),
155
        ];
156
    }
157
158
    /**
159
     * Return any comma separated values in a request query field as an array.
160
     *
161
     * @param Request $request
162
     * @param string  $key
163
     *
164
     * @return array
165
     */
166
    protected function getRequestQuerySet($request, $key)
167
    {
168
        return preg_split('/,/', $request->input($key), null, PREG_SPLIT_NO_EMPTY);
169
    }
170
171
    /**
172
     * Transform a set of models into a JSON API collection.
173
     *
174
     * @param \Illuminate\Support\Collection $records
175
     * @param array                          $fields
176
     *
177
     * @return array
178
     */
179
    protected function transformCollection($records, array $fields = [])
180
    {
181
        $data = [];
182
183
        foreach ($records as $record) {
184
            $data[] = $this->transformRecord($record, $fields)['data'];
185
        }
186
187
        return compact('data');
188
    }
189
190
    /**
191
     * Transform a model instance into a JSON API object.
192
     *
193
     * @param Model      $record
194
     * @param array|null $fields  Field names of attributes to limit to
195
     * @param array|null $include Relations to include
196
     *
197
     * @return array
198
     */
199
    protected function transformRecord($record, array $fields = [], array $include = [])
200
    {
201
        $relations = array_unique(array_merge($record->getRelations(), $include));
202
        $attributes = $record->load($relations)->toArray();
203
        $relationships = [];
204
        $included = [];
205
206
        foreach ($relations as $relation) {
207
            $relationships[$relation] = [
208
                'data' => []
209
            ];
210
211
            foreach (array_pull($attributes, $relation) as $relatedRecord) {
212
                $relationships[$relation]['data'][] = [
213
                    'type' => $relation,
214
                    'id' => $relatedRecord['id'],
215
                ];
216
217
                if (in_array($relation, $include)) {
218
                    $included[] = [
219
                        'type' => $relation,
220
                        'id' => $relatedRecord['id'],
221
                        'attributes' => array_except($relatedRecord, ['id', 'pivot']),
222
                    ];
223
                }
224
            }
225
        }
226
227
        if (!empty($fields)) {
228
            $attributes = array_only($attributes, $fields);
229
        }
230
231
        $data = array_filter([
232
            'type' => $record->getTable(),
233
            'id' => $record->id,
234
            'attributes' => array_except($attributes, ['id']),
235
            'relationships' => $relationships,
236
        ]);
237
238
        return array_filter(compact('data', 'included'));
239
    }
240
}
241