Completed
Push — master ( 3f5a8a...dd81ae )
by Arjay
01:13
created

DataTablesEditor::getBuilder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
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
     * Process dataTables editor action request.
30
     *
31
     * @param Request $request
32
     * @return JsonResponse|mixed
33
     * @throws DataTablesEditorException
34
     */
35
    public function process(Request $request)
36
    {
37
        $action = $request->get('action');
38
39
        if (! in_array($action, $this->actions)) {
40
            throw new DataTablesEditorException('Requested action not supported!');
41
        }
42
43
        return $this->{$action}($request);
44
    }
45
46
    /**
47
     * Process create action request.
48
     *
49
     * @param Request $request
50
     * @return JsonResponse
51
     */
52
    public function create(Request $request)
53
    {
54
        $instance   = $this->resolveModel();
55
        $connection = $instance->getConnection();
56
        $affected   = [];
57
        $errors     = [];
58
59
        $connection->beginTransaction();
60
        foreach ($request->get('data') as $data) {
61
            $validator = $this->getValidationFactory()
62
                              ->make($data, $this->createRules(), $this->createMessages(), $this->attributes());
63
            if ($validator->fails()) {
64
                foreach ($this->formatErrors($validator) as $error) {
65
                    $errors[] = $error;
66
                }
67
68
                continue;
69
            }
70
71
            $instance->fill($data);
72
73
            if (method_exists($this, 'creating')) {
74
                $data = $this->creating($instance, $data);
75
            }
76
77
            if (method_exists($this, 'saving')) {
78
                $data = $this->saving($instance, $data);
79
            }
80
81
            $instance->save();
82
83
            if (method_exists($this, 'created')) {
84
                $instance = $this->created($instance, $data);
85
            }
86
87
            if (method_exists($this, 'saved')) {
88
                $instance = $this->saved($instance, $data);
89
            }
90
91
            $instance->setAttribute('DT_RowId', $instance->getKey());
92
            $affected[] = $instance;
93
        }
94
95
        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...
96
            $connection->commit();
97
        } else {
98
            $connection->rollBack();
99
        }
100
101
        return $this->toJson($affected, $errors);
102
    }
103
104
    /**
105
     * Resolve model to used.
106
     *
107
     * @return Model|\Illuminate\Database\Eloquent\SoftDeletes
108
     */
109
    protected function resolveModel()
110
    {
111
        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...
112
            $this->model = new $this->model;
113
        }
114
115
        return $this->model;
116
    }
117
118
    /**
119
     * Get create action validation rules.
120
     *
121
     * @return array
122
     */
123
    abstract public function createRules();
124
125
    /**
126
     * Get create validation messages.
127
     *
128
     * @return array
129
     */
130
    protected function createMessages()
131
    {
132
        return [];
133
    }
134
135
    /**
136
     * Get custom attributes for validator errors.
137
     *
138
     * @return array
139
     */
140
    public function attributes()
141
    {
142
        return [];
143
    }
144
145
    /**
146
     * @param Validator $validator
147
     * @return array
148
     */
149
    protected function formatErrors(Validator $validator)
150
    {
151
        $errors = [];
152
153
        collect($validator->errors())->each(function ($error, $key) use (&$errors) {
154
            $errors[] = [
155
                'name'   => $key,
156
                'status' => $error[0],
157
            ];
158
        });
159
160
        return $errors;
161
    }
162
163
    /**
164
     * Display success data in dataTables editor format.
165
     *
166
     * @param array $data
167
     * @param array $errors
168
     * @return JsonResponse
169
     */
170
    protected function toJson(array $data, array $errors = [])
171
    {
172
        $response = ['data' => $data];
173
        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...
174
            $response['fieldErrors'] = $errors;
175
        }
176
177
        return new JsonResponse($response, 200);
178
    }
179
180
    /**
181
     * Process edit action request.
182
     *
183
     * @param Request $request
184
     * @return JsonResponse
185
     */
186
    public function edit(Request $request)
187
    {
188
        $builder    = $this->getBuilder();
189
        $connection = $builder->getConnection();
190
        $affected   = [];
191
        $errors     = [];
192
193
        $connection->beginTransaction();
194
        foreach ($request->get('data') as $key => $data) {
195
            $model     = $builder->findOrFail($key);
196
            $validator = $this->getValidationFactory()
197
                              ->make($data, $this->editRules($model), $this->editMessages(), $this->attributes());
198
            if ($validator->fails()) {
199
                foreach ($this->formatErrors($validator) as $error) {
200
                    $errors[] = $error;
201
                }
202
203
                continue;
204
            }
205
206
            $model->fill($data);
207
208
            if (method_exists($this, 'updating')) {
209
                $data = $this->updating($model, $data);
210
            }
211
212
            if (method_exists($this, 'saving')) {
213
                $data = $this->saving($model, $data);
214
            }
215
216
            $model->save();
217
218
            if (method_exists($this, 'updated')) {
219
                $model = $this->updated($model, $data);
220
            }
221
222
            if (method_exists($this, 'saved')) {
223
                $model = $this->saved($model, $data);
224
            }
225
226
            $model->setAttribute('DT_RowId', $model->getKey());
227
            $affected[] = $model;
228
        }
229
230
        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...
231
            $connection->commit();
232
        } else {
233
            $connection->rollBack();
234
        }
235
236
        return $this->toJson($affected, $errors);
237
    }
238
239
    /**
240
     * Get elqouent builder of the model.
241
     *
242
     * @return \Illuminate\Database\Eloquent\Builder
243
     */
244
    protected function getBuilder()
245
    {
246
        $model = $this->resolveModel();
247
248
        if (in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses($model))) {
249
            return $model->newQuery()->withTrashed();
250
        }
251
252
        return $model->newQuery();
253
    }
254
255
    /**
256
     * Get edit action validation rules.
257
     *
258
     * @param Model $model
259
     * @return array
260
     */
261
    abstract public function editRules(Model $model);
262
263
    /**
264
     * Get edit validation messages.
265
     *
266
     * @return array
267
     */
268
    protected function editMessages()
269
    {
270
        return [];
271
    }
272
273
    /**
274
     * Process remove action request.
275
     *
276
     * @param Request $request
277
     * @return JsonResponse
278
     */
279
    public function remove(Request $request)
280
    {
281
        $builder    = $this->getBuilder();
282
        $connection = $builder->getConnection();
283
        $affected   = [];
284
        $errors     = [];
285
286
        $connection->beginTransaction();
287
        foreach ($request->get('data') as $key => $data) {
288
            $model     = $builder->findOrFail($key);
289
            $validator = $this->getValidationFactory()
290
                              ->make($data, $this->removeRules($model), $this->removeMessages(), $this->attributes());
291
            if ($validator->fails()) {
292
                foreach ($this->formatErrors($validator) as $error) {
293
                    $errors[] = $error['status'];
294
                }
295
296
                continue;
297
            }
298
299
            try {
300
                $deleted = clone $model;
301
                if (method_exists($this, 'deleting')) {
302
                    $this->deleting($model, $data);
303
                }
304
305
                $model->delete();
306
307
                if (method_exists($this, 'deleted')) {
308
                    $this->deleted($deleted, $data);
309
                }
310
            } 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...
311
                $error = config('app.debug')
312
                    ? $exception->errorInfo[2]
313
                    : $this->removeExceptionMessage($exception, $model);
314
315
                $errors[] = $error;
316
            }
317
318
            $affected[] = $deleted;
319
        }
320
321
        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...
322
            $connection->commit();
323
        } else {
324
            $connection->rollBack();
325
        }
326
327
        $response = ['data' => $affected];
328
        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...
329
            $response['error'] = implode("\n", $errors);
330
        }
331
332
        return new JsonResponse($response, 200);
333
    }
334
335
    /**
336
     * Get remove action validation rules.
337
     *
338
     * @param Model $model
339
     * @return array
340
     */
341
    abstract public function removeRules(Model $model);
342
343
    /**
344
     * Get remove validation messages.
345
     *
346
     * @return array
347
     */
348
    protected function removeMessages()
349
    {
350
        return [];
351
    }
352
353
    /**
354
     * Get remove query exception message.
355
     *
356
     * @param QueryException $exception
357
     * @param Model $model
358
     * @return string
359
     */
360
    protected function removeExceptionMessage(QueryException $exception, Model $model)
361
    {
362
        return "Record {$model->getKey()} is protected and cannot be deleted!";
363
    }
364
365
    /**
366
     * Get dataTables model.
367
     *
368
     * @return Model
369
     */
370
    public function getModel()
371
    {
372
        return $this->model;
373
    }
374
375
    /**
376
     * Set the dataTables model on runtime.
377
     *
378
     * @param Model|string $model
379
     * @return DataTablesEditor
380
     */
381
    public function setModel($model)
382
    {
383
        $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...
384
385
        return $this;
386
    }
387
388
    /**
389
     * Display dataTables editor validation errors.
390
     *
391
     * @param Validator $validator
392
     * @return JsonResponse
393
     */
394
    protected function displayValidationErrors(Validator $validator)
395
    {
396
        $errors = $this->formatErrors($validator);
397
398
        return new JsonResponse([
399
            'data'        => [],
400
            'fieldErrors' => $errors,
401
        ]);
402
    }
403
}
404