Completed
Push — master ( 8b11c5...a17e81 )
by Arjay
54s
created

DataTablesEditor::unguard()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Yajra\DataTables;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Http\JsonResponse;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\QueryException;
9
use Illuminate\Contracts\Validation\Validator;
10
use Illuminate\Foundation\Validation\ValidatesRequests;
11
12
abstract class DataTablesEditor
13
{
14
    use ValidatesRequests;
15
16
    /**
17
     * Allowed dataTables editor actions.
18
     *
19
     * @var array
20
     */
21
    protected $actions = ['create', 'edit', 'remove'];
22
23
    /**
24
     * @var \Illuminate\Database\Eloquent\Model
25
     */
26
    protected $model = null;
27
28
    /**
29
     * Indicates if all mass assignment is enabled on model.
30
     *
31
     * @var bool
32
     */
33
    protected $unguarded = false;
34
35
    /**
36
     * Process dataTables editor action request.
37
     *
38
     * @param Request $request
39
     * @return JsonResponse|mixed
40
     * @throws DataTablesEditorException
41
     */
42
    public function process(Request $request)
43
    {
44
        $action = $request->get('action');
45
46
        if (! in_array($action, $this->actions)) {
47
            throw new DataTablesEditorException('Requested action not supported!');
48
        }
49
50
        return $this->{$action}($request);
51
    }
52
53
    /**
54
     * Process create action request.
55
     *
56
     * @param Request $request
57
     * @return JsonResponse
58
     */
59 View Code Duplication
    public function create(Request $request)
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...
60
    {
61
        $instance   = $this->resolveModel();
62
        $connection = $instance->getConnection();
63
        $affected   = [];
64
        $errors     = [];
65
66
        $connection->beginTransaction();
67
        foreach ($request->get('data') as $data) {
68
            $validator = $this->getValidationFactory()
69
                              ->make($data, $this->createRules(), $this->createMessages(), $this->attributes());
70
            if ($validator->fails()) {
71
                foreach ($this->formatErrors($validator) as $error) {
72
                    $errors[] = $error;
73
                }
74
75
                continue;
76
            }
77
78
            $instance->fill($data);
79
80
            if (method_exists($this, 'creating')) {
81
                $data = $this->creating($instance, $data);
82
            }
83
84
            if (method_exists($this, 'saving')) {
85
                $data = $this->saving($instance, $data);
86
            }
87
88
            $instance->save();
89
90
            if (method_exists($this, 'created')) {
91
                $instance = $this->created($instance, $data);
92
            }
93
94
            if (method_exists($this, 'saved')) {
95
                $instance = $this->saved($instance, $data);
96
            }
97
98
            $instance->setAttribute('DT_RowId', $instance->getKey());
99
            $affected[] = $instance;
100
        }
101
102
        if (! $errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
103
            $connection->commit();
104
        } else {
105
            $connection->rollBack();
106
        }
107
108
        return $this->toJson($affected, $errors);
109
    }
110
111
    /**
112
     * Resolve model to used.
113
     *
114
     * @return Model|\Illuminate\Database\Eloquent\SoftDeletes
115
     */
116
    protected function resolveModel()
117
    {
118
        if (! $this->model instanceof Model) {
0 ignored issues
show
Bug introduced by
The class Illuminate\Database\Eloquent\Model does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
119
            $this->model = new $this->model;
120
        }
121
122
        $this->model->unguard($this->unguarded);
123
124
        return $this->model;
125
    }
126
127
    /**
128
     * Get create action validation rules.
129
     *
130
     * @return array
131
     */
132
    abstract public function createRules();
133
134
    /**
135
     * Get create validation messages.
136
     *
137
     * @return array
138
     */
139
    protected function createMessages()
140
    {
141
        return [];
142
    }
143
144
    /**
145
     * Get custom attributes for validator errors.
146
     *
147
     * @return array
148
     */
149
    public function attributes()
150
    {
151
        return [];
152
    }
153
154
    /**
155
     * @param Validator $validator
156
     * @return array
157
     */
158
    protected function formatErrors(Validator $validator)
159
    {
160
        $errors = [];
161
162
        collect($validator->errors())->each(function ($error, $key) use (&$errors) {
163
            $errors[] = [
164
                'name'   => $key,
165
                'status' => $error[0],
166
            ];
167
        });
168
169
        return $errors;
170
    }
171
172
    /**
173
     * Display success data in dataTables editor format.
174
     *
175
     * @param array $data
176
     * @param array $errors
177
     * @return JsonResponse
178
     */
179
    protected function toJson(array $data, array $errors = [])
180
    {
181
        $response = ['data' => $data];
182
        if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
183
            $response['fieldErrors'] = $errors;
184
        }
185
186
        return new JsonResponse($response, 200);
187
    }
188
189
    /**
190
     * Process edit action request.
191
     *
192
     * @param Request $request
193
     * @return JsonResponse
194
     */
195 View Code Duplication
    public function edit(Request $request)
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...
196
    {
197
        $connection = $this->getBuilder()->getConnection();
198
        $affected   = [];
199
        $errors     = [];
200
201
202
        $connection->beginTransaction();
203
        foreach ($request->get('data') as $key => $data) {
204
            $model     = $this->getBuilder()->findOrFail($key);
205
            $validator = $this->getValidationFactory()
206
                              ->make($data, $this->editRules($model), $this->editMessages(), $this->attributes());
207
            if ($validator->fails()) {
208
                foreach ($this->formatErrors($validator) as $error) {
209
                    $errors[] = $error;
210
                }
211
212
                continue;
213
            }
214
215
            $model->fill($data);
216
217
            if (method_exists($this, 'updating')) {
218
                $data = $this->updating($model, $data);
219
            }
220
221
            if (method_exists($this, 'saving')) {
222
                $data = $this->saving($model, $data);
223
            }
224
225
            $model->save();
226
227
            if (method_exists($this, 'updated')) {
228
                $model = $this->updated($model, $data);
229
            }
230
231
            if (method_exists($this, 'saved')) {
232
                $model = $this->saved($model, $data);
233
            }
234
235
            $model->setAttribute('DT_RowId', $model->getKey());
236
            $affected[] = $model;
237
        }
238
239
        if (! $errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
240
            $connection->commit();
241
        } else {
242
            $connection->rollBack();
243
        }
244
245
        return $this->toJson($affected, $errors);
246
    }
247
248
    /**
249
     * Get elqouent builder of the model.
250
     *
251
     * @return \Illuminate\Database\Eloquent\Builder
252
     */
253
    protected function getBuilder()
254
    {
255
        $model = $this->resolveModel();
256
257
        if (in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses($model))) {
258
            return $model->newQuery()->withTrashed();
259
        }
260
261
        return $model->newQuery();
262
    }
263
264
    /**
265
     * Get edit action validation rules.
266
     *
267
     * @param Model $model
268
     * @return array
269
     */
270
    abstract public function editRules(Model $model);
271
272
    /**
273
     * Get edit validation messages.
274
     *
275
     * @return array
276
     */
277
    protected function editMessages()
278
    {
279
        return [];
280
    }
281
282
    /**
283
     * Process remove action request.
284
     *
285
     * @param Request $request
286
     * @return JsonResponse
287
     */
288
    public function remove(Request $request)
289
    {
290
        $connection = $this->getBuilder()->getConnection();
291
        $affected   = [];
292
        $errors     = [];
293
294
        $connection->beginTransaction();
295
        foreach ($request->get('data') as $key => $data) {
296
            $model     = $this->getBuilder()->findOrFail($key);
297
            $validator = $this->getValidationFactory()
298
                              ->make($data, $this->removeRules($model), $this->removeMessages(), $this->attributes());
299
            if ($validator->fails()) {
300
                foreach ($this->formatErrors($validator) as $error) {
301
                    $errors[] = $error['status'];
302
                }
303
304
                continue;
305
            }
306
307
            try {
308
                $deleted = clone $model;
309
                if (method_exists($this, 'deleting')) {
310
                    $this->deleting($model, $data);
311
                }
312
313
                $model->delete();
314
315
                if (method_exists($this, 'deleted')) {
316
                    $this->deleted($deleted, $data);
317
                }
318
            } catch (QueryException $exception) {
0 ignored issues
show
Bug introduced by
The class Illuminate\Database\QueryException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
319
                $error = config('app.debug')
320
                    ? $exception->errorInfo[2]
321
                    : $this->removeExceptionMessage($exception, $model);
322
323
                $errors[] = $error;
324
            }
325
326
            $affected[] = $deleted;
327
        }
328
329
        if (! $errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
330
            $connection->commit();
331
        } else {
332
            $connection->rollBack();
333
        }
334
335
        $response = ['data' => $affected];
336
        if ($errors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
337
            $response['error'] = implode("\n", $errors);
338
        }
339
340
        return new JsonResponse($response, 200);
341
    }
342
343
    /**
344
     * Get remove action validation rules.
345
     *
346
     * @param Model $model
347
     * @return array
348
     */
349
    abstract public function removeRules(Model $model);
350
351
    /**
352
     * Get remove validation messages.
353
     *
354
     * @return array
355
     */
356
    protected function removeMessages()
357
    {
358
        return [];
359
    }
360
361
    /**
362
     * Get remove query exception message.
363
     *
364
     * @param QueryException $exception
365
     * @param Model $model
366
     * @return string
367
     */
368
    protected function removeExceptionMessage(QueryException $exception, Model $model)
369
    {
370
        return "Record {$model->getKey()} is protected and cannot be deleted!";
371
    }
372
373
    /**
374
     * Get dataTables model.
375
     *
376
     * @return Model
377
     */
378
    public function getModel()
379
    {
380
        return $this->model;
381
    }
382
383
    /**
384
     * Set the dataTables model on runtime.
385
     *
386
     * @param Model|string $model
387
     * @return DataTablesEditor
388
     */
389
    public function setModel($model)
390
    {
391
        $this->model = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model can also be of type string. However, the property $model is declared as type object<Illuminate\Database\Eloquent\Model>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
392
393
        return $this;
394
    }
395
396
    /**
397
     * Set model unguard state.
398
     *
399
     * @param bool $state
400
     * @return $this
401
     */
402
    public function unguard($state = true)
403
    {
404
        $this->unguarded = $state;
405
406
        return $this;
407
    }
408
409
    /**
410
     * Display dataTables editor validation errors.
411
     *
412
     * @param Validator $validator
413
     * @return JsonResponse
414
     */
415
    protected function displayValidationErrors(Validator $validator)
416
    {
417
        $errors = $this->formatErrors($validator);
418
419
        return new JsonResponse([
420
            'data'        => [],
421
            'fieldErrors' => $errors,
422
        ]);
423
    }
424
}
425