Passed
Push — develop ( 963a3b...7b9cb8 )
by Alex
04:23 queued 01:16
created

JsonApiController::updateToOneRelationship()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 3
eloc 5
nc 2
nop 4
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) {
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
     * Update a named many-to-one relationship association on a specified record.
129
     * http://jsonapi.org/format/#crud-updating-to-one-relationships
130
     *
131
     * @param Request     $request
132
     * @param Model|int   $record
133
     * @param string      $relation
134
     * @param string|null $foreignKey
135
     *
136
     * @return JsonApiResponse
137
     */
138
    public function updateToOneRelationship(Request $request, $record, $relation, $foreignKey = null)
139
    {
140
        $record = $record instanceof Model ? $record : $this->findModelInstance($record);
141
        $data = (array) $request->input('data');
142
143
        $record->update([($foreignKey ?: $relation . '_id') => $data['id']]);
144
145
        return new JsonApiResponse(null, Response::HTTP_OK);
146
    }
147
148
    /**
149
     * Update named many-to-many relationship entries on a specified record.
150
     * http://jsonapi.org/format/#crud-updating-to-many-relationships
151
     *
152
     * @param Request   $request
153
     * @param Model|int $record
154
     * @param string    $relation
155
     *
156
     * @return JsonApiResponse
157
     */
158
    public function updateToManyRelationship(Request $request, $record, $relation)
159
    {
160
        $record = $record instanceof Model ? $record : $this->findModelInstance($record);
161
        $relationships = (array) $request->input('data');
162
        $items = [];
163
164
        foreach ($relationships as $item) {
165
            $items[$item['id']] = $item['attributes'];
166
        }
167
168
        switch ($request->method()) {
169
            case 'PATCH':
170
                $record->{$relation}()->sync($items);
171
                break;
172
            case 'POST':
173
                $record->{$relation}()->sync($items, false);
174
                break;
175
            case 'DELETE':
176
                $record->{$relation}()->detach(array_keys($items));
177
        }
178
179
        return new JsonApiResponse(null, Response::HTTP_OK);
180
    }
181
182
    /**
183
     * Return an instance of the resource by primary key.
184
     *
185
     * @param int $key
186
     *
187
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
188
     *
189
     * @return Model
190
     */
191
    protected function findModelInstance($key)
192
    {
193
        return $this->getModel()->findOrFail($key);
194
    }
195
196
    /**
197
     * Return any JSON API resource parameters from a request.
198
     *
199
     * @param Request $request
200
     *
201
     * @return array
202
     */
203
    protected function getRequestParameters($request)
204
    {
205
        return [
206
            'fields' => $this->getRequestQuerySet($request, 'fields.' . $this->getModelType()),
207
            'include' => $this->getRequestQuerySet($request, 'include'),
208
            'sort' => $this->getRequestQuerySet($request, 'sort'),
209
            'filter' => (array) $request->input('filter'),
210
        ];
211
    }
212
213
    /**
214
     * Return any comma separated values in a request query field as an array.
215
     *
216
     * @param Request $request
217
     * @param string  $key
218
     *
219
     * @return array
220
     */
221
    protected function getRequestQuerySet($request, $key)
222
    {
223
        return preg_split('/,/', $request->input($key), null, PREG_SPLIT_NO_EMPTY);
224
    }
225
226
    /**
227
     * Transform a set of models into a JSON API collection.
228
     *
229
     * @param \Illuminate\Support\Collection $records
230
     * @param array                          $fields
231
     *
232
     * @return array
233
     */
234
    protected function transformCollection($records, array $fields = [])
235
    {
236
        $data = [];
237
238
        foreach ($records as $record) {
239
            $data[] = $this->transformRecord($record, $fields)['data'];
240
        }
241
242
        return compact('data');
243
    }
244
245
    /**
246
     * Transform a model instance into a JSON API object.
247
     *
248
     * @param Model      $record
249
     * @param array|null $fields  Field names of attributes to limit to
250
     * @param array|null $include Relations to include
251
     *
252
     * @return array
253
     */
254
    protected function transformRecord($record, array $fields = [], array $include = [])
255
    {
256
        $relations = array_unique(array_merge($record->getRelations(), $include));
257
        $attributes = $record->load($relations)->toArray();
258
        $relationships = [];
259
        $included = [];
260
261
        foreach ($relations as $relation) {
262
            $relationships[$relation] = [
263
                'data' => []
264
            ];
265
266
            foreach (array_pull($attributes, $relation) as $relatedRecord) {
267
                $relationships[$relation]['data'][] = [
268
                    'type' => $relation,
269
                    'id' => $relatedRecord['id'],
270
                ];
271
272
                if (in_array($relation, $include)) {
273
                    $included[] = [
274
                        'type' => $relation,
275
                        'id' => $relatedRecord['id'],
276
                        'attributes' => array_except($relatedRecord, ['id', 'pivot']),
277
                    ];
278
                }
279
            }
280
        }
281
282
        if (!empty($fields)) {
283
            $attributes = array_only($attributes, $fields);
284
        }
285
286
        $data = array_filter([
287
            'type' => $record->getTable(),
288
            'id' => $record->id,
289
            'attributes' => array_except($attributes, ['id']),
290
            'relationships' => $relationships,
291
        ]);
292
293
        return array_filter(compact('data', 'included'));
294
    }
295
}
296