Completed
Push — master ( 66bffc...f1bba6 )
by Arjay
25s queued 11s
created

DataTablesEditor::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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