BaseController::destroy()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace DDiimmkkaass\Api\Skeleton;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Support\Facades\Response;
7
use Illuminate\Support\Facades\Validator;
8
use Laravel\Lumen\Routing\Controller as LumenController;
9
use League\Fractal\Manager;
10
use League\Fractal\Pagination\Cursor;
11
use League\Fractal\Resource\Collection;
12
use League\Fractal\Resource\Item;
13
use League\Fractal\Serializer\DataArraySerializer;
14
15
abstract class BaseController extends LumenController
16
{
17
    /**
18
     * HTTP header status code.
19
     *
20
     * @var int
21
     */
22
    protected $statusCode = 200;
23
24
    /**
25
     * Fractal Manager instance.
26
     *
27
     * @var Manager
28
     */
29
    protected $fractal;
30
31
    /**
32
     * Eloquent model instance.
33
     *
34
     * @var \Illuminate\Database\Eloquent\Model;
35
     */
36
    protected $model;
37
38
    /**
39
     * Fractal Transformer instance.
40
     *
41
     * @var \League\Fractal\TransformerAbstract
42
     */
43
    protected $transformer;
44
45
    /**
46
     * Illuminate\Http\Request instance.
47
     *
48
     * @var Request
49
     */
50
    protected $request;
51
52
    /**
53
     * Do we need to unguard the model before create/update?
54
     *
55
     * @var bool
56
     */
57
    protected $unguard = false;
58
59
    /**
60
     * Number of items displayed at once if not specified.
61
     * There is no limit if it is 0 or false.
62
     *
63
     * @var int|bool
64
     */
65
    protected $defaultLimit = false;
66
67
    /**
68
     * Maximum limit that can be set via $_GET['limit'].
69
     *
70
     * @var int|bool
71
     */
72
    protected $maximumLimit = false;
73
74
    /**
75
     * Resource key for an item.
76
     *
77
     * @var string
78
     */
79
    protected $resourceKeySingular = 'data';
80
81
    /**
82
     * Resource key for a collection.
83
     *
84
     * @var string
85
     */
86
    protected $resourceKeyPlural = 'data';
87
88
    /**
89
     * Constructor.
90
     *
91
     * @param Request $request
92
     */
93
    public function __construct(Request $request)
94
    {
95
        $this->model = $this->model();
96
        $this->transformer = $this->transformer();
97
98
        $this->fractal = new Manager();
99
        $this->fractal->setSerializer($this->serializer());
100
101
        $this->request = $request;
102
103
        if ($this->request->has('include')) {
104
            $this->fractal->parseIncludes(camel_case($this->request->input('include')));
105
        }
106
    }
107
108
    /**
109
     * Eloquent model.
110
     *
111
     * @return \Illuminate\Database\Eloquent\Model
112
     */
113
    abstract protected function model();
114
115
    /**
116
     * Transformer for the current model.
117
     *
118
     * @return \League\Fractal\TransformerAbstract
119
     */
120
    abstract protected function transformer();
121
122
    /**
123
     * Serializer for the current model.
124
     *
125
     * @return \League\Fractal\Serializer\SerializerAbstract
126
     */
127
    protected function serializer()
128
    {
129
        return new DataArraySerializer();
130
    }
131
132
    /**
133
     * Display a listing of the resource.
134
     * GET /api/{resource}.
135
     *
136
     * @return Response
137
     */
138
    public function index()
139
    {
140
        $with = $this->getEagerLoad();
141
        $skip = (int) $this->request->input('skip', 0);
142
        $limit = $this->calculateLimit();
143
144
        $items = $limit
145
            ? $this->model->with($with)->skip($skip)->limit($limit)->get()
146
            : $this->model->with($with)->get();
147
148
        return $this->respondWithCollection($items, $skip, $limit);
149
    }
150
151
    /**
152
     * Store a newly created resource in storage.
153
     * POST /api/{resource}.
154
     *
155
     * @return Response
156
     */
157
    public function store()
158
    {
159
        $data = $this->request->json()->get($this->resourceKeySingular);
160
161
        if (!$data) {
162
            return $this->errorWrongArgs('Empty data');
163
        }
164
165
        $validator = Validator::make($data, $this->rulesForCreate());
166
        if ($validator->fails()) {
167
            return $this->errorWrongArgs($validator->messages());
168
        }
169
170
        $this->unguardIfNeeded();
171
172
        $item = $this->model->create($data);
173
174
        return $this->respondWithItem($item);
175
    }
176
177
    /**
178
     * Display the specified resource.
179
     * GET /api/{resource}/{id}.
180
     *
181
     * @param int $id
182
     *
183
     * @return Response
184
     */
185
    public function show($id)
186
    {
187
        $with = $this->getEagerLoad();
188
189
        $item = $this->findItem($id, $with);
190
        if (!$item) {
191
            return $this->errorNotFound();
192
        }
193
194
        return $this->respondWithItem($item);
195
    }
196
197
    /**
198
     * Update the specified resource in storage.
199
     * PUT /api/{resource}/{id}.
200
     *
201
     * @param int $id
202
     *
203
     * @return Response
204
     */
205
    public function update($id)
206
    {
207
        $data = $this->request->json()->get($this->resourceKeySingular);
208
209
        if (!$data) {
210
            return $this->errorWrongArgs('Empty data');
211
        }
212
213
        $item = $this->findItem($id);
214
        if (!$item) {
215
            return $this->errorNotFound();
216
        }
217
218
        $validator = Validator::make($data, $this->rulesForUpdate($item->id));
219
        if ($validator->fails()) {
220
            return $this->errorWrongArgs($validator->messages());
221
        }
222
223
        $this->unguardIfNeeded();
224
225
        $item->fill($data);
226
        $item->save();
227
228
        return $this->respondWithItem($item);
229
    }
230
231
    /**
232
     * Remove the specified resource from storage.
233
     * DELETE /api/{resource}/{id}.
234
     *
235
     * @param int $id
236
     *
237
     * @return Response
238
     */
239
    public function destroy($id)
240
    {
241
        $item = $this->findItem($id);
242
243
        if (!$item) {
244
            return $this->errorNotFound();
245
        }
246
247
        $item->delete();
248
249
        return response()->json(['message' => 'Deleted']);
250
    }
251
252
    /**
253
     * Show the form for creating the specified resource.
254
     *
255
     * @return Response
256
     */
257
    public function create()
258
    {
259
        return $this->errorNotImplemented();
260
    }
261
262
    /**
263
     * Show the form for editing the specified resource.
264
     *
265
     * @param int $id
266
     *
267
     * @return Response
268
     */
269
    public function edit($id)
270
    {
271
        return $this->errorNotImplemented();
272
    }
273
274
    /**
275
     * Getter for statusCode.
276
     *
277
     * @return int
278
     */
279
    protected function getStatusCode()
280
    {
281
        return $this->statusCode;
282
    }
283
284
    /**
285
     * Setter for statusCode.
286
     *
287
     * @param int $statusCode Value to set
288
     *
289
     * @return self
290
     */
291
    protected function setStatusCode($statusCode)
292
    {
293
        $this->statusCode = $statusCode;
294
295
        return $this;
296
    }
297
298
    /**
299
     * Respond with a given item.
300
     *
301
     * @param $item
302
     *
303
     * @return mixed
304
     */
305
    protected function respondWithItem($item)
306
    {
307
        $resource = new Item($item, $this->transformer, $this->resourceKeySingular);
308
309
        $rootScope = $this->prepareRootScope($resource);
310
311
        return $this->respondWithArray($rootScope->toArray());
312
    }
313
314
    /**
315
     * Respond with a given collection.
316
     *
317
     * @param $collection
318
     * @param int $skip
319
     * @param int $limit
320
     *
321
     * @return mixed
322
     */
323
    protected function respondWithCollection($collection, $skip = 0, $limit = 0)
324
    {
325
        $resource = new Collection($collection, $this->transformer, $this->resourceKeyPlural);
326
327
        if ($limit) {
328
            $cursor = new Cursor($skip, $skip + $limit, $collection->count());
329
            $resource->setCursor($cursor);
330
        }
331
332
        $rootScope = $this->prepareRootScope($resource);
333
334
        return $this->respondWithArray($rootScope->toArray());
335
    }
336
337
    /**
338
     * Respond with a given array of items.
339
     *
340
     * @param array $array
341
     * @param array $headers
342
     *
343
     * @return mixed
344
     */
345
    protected function respondWithArray(array $array, array $headers = [])
346
    {
347
        return response()->json($array, $this->statusCode, $headers);
348
    }
349
350
    /**
351
     * Response with the current error.
352
     *
353
     * @param string $message
354
     *
355
     * @return mixed
356
     */
357
    protected function respondWithError($message)
358
    {
359
        return $this->respondWithArray([
360
            'error' => [
361
                'http_code' => $this->statusCode,
362
                'message'   => $message,
363
            ],
364
        ]);
365
    }
366
367
    /**
368
     * Prepare root scope and set some meta information.
369
     *
370
     * @param Item|Collection $resource
371
     *
372
     * @return \League\Fractal\Scope
373
     */
374
    protected function prepareRootScope($resource)
375
    {
376
        $resource->setMetaValue('available_includes', $this->transformer->getAvailableIncludes());
377
        $resource->setMetaValue('default_includes', $this->transformer->getDefaultIncludes());
378
379
        return $this->fractal->createData($resource);
380
    }
381
382
    /**
383
     * Get the validation rules for create.
384
     *
385
     * @return array
386
     */
387
    protected function rulesForCreate()
388
    {
389
        return [];
390
    }
391
392
    /**
393
     * Get the validation rules for update.
394
     *
395
     * @param int $id
396
     *
397
     * @return array
398
     */
399
    protected function rulesForUpdate($id)
400
    {
401
        return [];
402
    }
403
404
    /**
405
     * Generate a Response with a 403 HTTP header and a given message.
406
     *
407
     * @param $message
408
     *
409
     * @return Response
410
     */
411
    protected function errorForbidden($message = 'Forbidden')
412
    {
413
        return $this->setStatusCode(403)->respondWithError($message);
414
    }
415
416
    /**
417
     * Generate a Response with a 500 HTTP header and a given message.
418
     *
419
     * @param string $message
420
     *
421
     * @return Response
422
     */
423
    protected function errorInternalError($message = 'Internal Error')
424
    {
425
        return $this->setStatusCode(500)->respondWithError($message);
426
    }
427
428
    /**
429
     * Generate a Response with a 404 HTTP header and a given message.
430
     *
431
     * @param string $message
432
     *
433
     * @return Response
434
     */
435
    protected function errorNotFound($message = 'Resource Not Found')
436
    {
437
        return $this->setStatusCode(404)->respondWithError($message);
438
    }
439
440
    /**
441
     * Generate a Response with a 401 HTTP header and a given message.
442
     *
443
     * @param string $message
444
     *
445
     * @return Response
446
     */
447
    protected function errorUnauthorized($message = 'Unauthorized')
448
    {
449
        return $this->setStatusCode(401)->respondWithError($message);
450
    }
451
452
    /**
453
     * Generate a Response with a 400 HTTP header and a given message.
454
     *
455
     * @param string$message
456
     *
457
     * @return Response
458
     */
459
    protected function errorWrongArgs($message = 'Wrong Arguments')
460
    {
461
        return $this->setStatusCode(400)->respondWithError($message);
462
    }
463
464
    /**
465
     * Generate a Response with a 501 HTTP header and a given message.
466
     *
467
     * @param string $message
468
     *
469
     * @return Response
470
     */
471
    protected function errorNotImplemented($message = 'Not implemented')
472
    {
473
        return $this->setStatusCode(501)->respondWithError($message);
474
    }
475
476
    /**
477
     * Specify relations for eager loading.
478
     *
479
     * @return array
480
     */
481
    protected function getEagerLoad()
482
    {
483
        $include = camel_case($this->request->input('include', ''));
484
        $includes = explode(',', $include);
485
        $includes = array_filter($includes);
486
487
        return $includes ?: [];
488
    }
489
490
    /**
491
     * Get item according to mode.
492
     *
493
     * @param int   $id
494
     * @param array $with
495
     *
496
     * @return mixed
497
     */
498
    protected function findItem($id, array $with = [])
499
    {
500
        if ($this->request->has('use_as_id')) {
501
            return $this->model->with($with)->where($this->request->input('use_as_id'), '=', $id)->first();
502
        }
503
504
        return $this->model->with($with)->find($id);
505
    }
506
507
    /**
508
     * Unguard eloquent model if needed.
509
     */
510
    protected function unguardIfNeeded()
511
    {
512
        if ($this->unguard) {
513
            $this->model->unguard();
514
        }
515
    }
516
517
    /**
518
     * Calculates limit for a number of items displayed in list.
519
     *
520
     * @return int
521
     */
522
    protected function calculateLimit()
523
    {
524
        $limit = (int) $this->request->input('limit', $this->defaultLimit);
525
526
        return ($this->maximumLimit && $this->maximumLimit < $limit) ? $this->maximumLimit : $limit;
527
    }
528
}