Completed
Pull Request — master (#2254)
by
unknown
12:23 queued 09:50
created

Model::setPerPageName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Grid;
4
5
use Encore\Admin\Middleware\Pjax;
6
use Illuminate\Database\Eloquent\Model as EloquentModel;
7
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
use Illuminate\Database\Eloquent\Relations\HasOne;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use Illuminate\Pagination\LengthAwarePaginator;
11
use Illuminate\Support\Collection;
12
use Illuminate\Support\Facades\Input;
13
use Illuminate\Support\Facades\Request;
14
15
class Model
16
{
17
    /**
18
     * Eloquent model instance of the grid model.
19
     *
20
     * @var EloquentModel
21
     */
22
    protected $model;
23
24
    /**
25
     * Array of queries of the eloquent model.
26
     *
27
     * @var \Illuminate\Support\Collection
28
     */
29
    protected $queries;
30
31
    /**
32
     * Sort parameters of the model.
33
     *
34
     * @var array
35
     */
36
    protected $sort;
37
38
    /**
39
     * @var array
40
     */
41
    protected $data = [];
42
43
    /*
44
     * 20 items per page as default.
45
     *
46
     * @var int
47
     */
48
    protected $perPage = 20;
49
50
    /**
51
     * If the model use pagination.
52
     *
53
     * @var bool
54
     */
55
    protected $usePaginate = true;
56
57
    /**
58
     * The query string variable used to store the per-page.
59
     *
60
     * @var string
61
     */
62
    protected $perPageName = 'per_page';
63
64
    /**
65
     * The query string variable used to store the sort.
66
     *
67
     * @var string
68
     */
69
    protected $sortName = '_sort';
70
71
    /**
72
     * Collection callback.
73
     *
74
     * @var \Closure
75
     */
76
    protected $collectionCallback;
77
78
    /**
79
     * Create a new grid model instance.
80
     *
81
     * @param EloquentModel $model
82
     */
83
    public function __construct(EloquentModel $model)
84
    {
85
        $this->model = $model;
86
87
        $this->queries = collect();
88
89
//        static::doNotSnakeAttributes($this->model);
90
    }
91
92
    /**
93
     * Don't snake case attributes.
94
     *
95
     * @param EloquentModel $model
96
     *
97
     * @return void
98
     */
99
    protected static function doNotSnakeAttributes(EloquentModel $model)
100
    {
101
        $class = get_class($model);
102
103
        $class::$snakeAttributes = false;
104
    }
105
106
    /**
107
     * Get the eloquent model of the grid model.
108
     *
109
     * @return EloquentModel
110
     */
111
    public function eloquent()
112
    {
113
        return $this->model;
114
    }
115
116
    /**
117
     * Enable or disable pagination.
118
     *
119
     * @param bool $use
120
     */
121
    public function usePaginate($use = true)
122
    {
123
        $this->usePaginate = $use;
124
    }
125
126
    /**
127
     * Get the query string variable used to store the per-page.
128
     *
129
     * @return string
130
     */
131
    public function getPerPageName()
132
    {
133
        return $this->perPageName;
134
    }
135
136
    /**
137
     * Set the query string variable used to store the per-page.
138
     *
139
     * @param string $name
140
     *
141
     * @return $this
142
     */
143
    public function setPerPageName($name)
144
    {
145
        $this->perPageName = $name;
146
147
        return $this;
148
    }
149
150
    /**
151
     * Get the query string variable used to store the sort.
152
     *
153
     * @return string
154
     */
155
    public function getSortName()
156
    {
157
        return $this->sortName;
158
    }
159
160
    /**
161
     * Set the query string variable used to store the sort.
162
     *
163
     * @param string $name
164
     *
165
     * @return $this
166
     */
167
    public function setSortName($name)
168
    {
169
        $this->sortName = $name;
170
171
        return $this;
172
    }
173
174
    /**
175
     * Set collection callback.
176
     *
177
     * @param \Closure $callback
178
     *
179
     * @return $this
180
     */
181
    public function collection(\Closure $callback = null)
182
    {
183
        $this->collectionCallback = $callback;
184
185
        return $this;
186
    }
187
188
    /**
189
     * Build.
190
     *
191
     * @param bool $toArray
192
     *
193
     * @return array|Collection|mixed
194
     */
195
    public function buildData($toArray = true)
196
    {
197
        if (empty($this->data)) {
198
            $collection = $this->get();
199
200
            if ($this->collectionCallback) {
201
                $collection = call_user_func($this->collectionCallback, $collection);
202
            }
203
204
            if ($toArray) {
205
                $this->data = $collection->toArray();
206
            } else {
207
                $this->data = $collection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $collection of type * is incompatible with the declared type array of property $data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
208
            }
209
        }
210
211
        return $this->data;
212
    }
213
214
    /**
215
     * @param callable $callback
216
     * @param int      $count
217
     *
218
     * @return bool
219
     */
220
    public function chunk($callback, $count = 100)
221
    {
222
        if ($this->usePaginate) {
223
            return $this->buildData(false)->chunk($count)->each($callback);
224
        }
225
226
        $this->setSort();
227
228
        $this->queries->reject(function ($query) {
229
            return $query['method'] == 'paginate';
230
        })->each(function ($query) {
231
            $this->model = $this->model->{$query['method']}(...$query['arguments']);
232
        });
233
234
        return $this->model->chunk($count, $callback);
235
    }
236
237
    /**
238
     * Add conditions to grid model.
239
     *
240
     * @param array $conditions
241
     *
242
     * @return $this
243
     */
244
    public function addConditions(array $conditions)
245
    {
246
        foreach ($conditions as $condition) {
247
            call_user_func_array([$this, key($condition)], current($condition));
248
        }
249
250
        return $this;
251
    }
252
253
    /**
254
     * Get table of the model.
255
     *
256
     * @return string
257
     */
258
    public function getTable()
259
    {
260
        return $this->model->getTable();
261
    }
262
263
    /**
264
     * @throws \Exception
265
     *
266
     * @return Collection
267
     */
268
    protected function get()
269
    {
270
        if ($this->model instanceof LengthAwarePaginator) {
271
            return $this->model;
272
        }
273
274
        $this->setSort();
275
        $this->setPaginate();
276
277
        $this->queries->unique()->each(function ($query) {
278
            $this->model = call_user_func_array([$this->model, $query['method']], $query['arguments']);
279
        });
280
281
        if ($this->model instanceof Collection) {
282
            return $this->model;
283
        }
284
285
        if ($this->model instanceof LengthAwarePaginator) {
286
            $this->handleInvalidPage($this->model);
287
288
            return $this->model->getCollection();
289
        }
290
291
        throw new \Exception('Grid query error');
292
    }
293
294
    /**
295
     * If current page is greater than last page, then redirect to last page.
296
     *
297
     * @param LengthAwarePaginator $paginator
298
     *
299
     * @return void
300
     */
301
    protected function handleInvalidPage(LengthAwarePaginator $paginator)
302
    {
303
        if ($paginator->lastPage() && $paginator->currentPage() > $paginator->lastPage()) {
304
            $lastPageUrl = Request::fullUrlWithQuery([
305
                $paginator->getPageName() => $paginator->lastPage(),
306
            ]);
307
308
            Pjax::respond(redirect($lastPageUrl));
309
        }
310
    }
311
312
    /**
313
     * Set the grid paginate.
314
     *
315
     * @return void
316
     */
317
    protected function setPaginate()
318
    {
319
        $paginate = $this->findQueryByMethod('paginate');
320
321
        $this->queries = $this->queries->reject(function ($query) {
322
            return $query['method'] == 'paginate';
323
        });
324
325
        if (!$this->usePaginate) {
326
            $query = [
327
                'method'    => 'get',
328
                'arguments' => [],
329
            ];
330
        } else {
331
            $query = [
332
                'method'    => 'paginate',
333
                'arguments' => $this->resolvePerPage($paginate),
0 ignored issues
show
Documentation introduced by
$paginate is of type this<Encore\Admin\Grid\Model>, but the function expects a array|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
334
            ];
335
        }
336
337
        $this->queries->push($query);
338
    }
339
340
    /**
341
     * Resolve perPage for pagination.
342
     *
343
     * @param array|null $paginate
344
     *
345
     * @return array
346
     */
347
    protected function resolvePerPage($paginate)
348
    {
349
        if ($perPage = app('request')->input($this->perPageName)) {
350
            if (is_array($paginate)) {
351
                $paginate['arguments'][0] = (int) $perPage;
352
353
                return $paginate['arguments'];
354
            }
355
356
            $this->perPage = (int) $perPage;
357
        }
358
359
        if (isset($paginate['arguments'][0])) {
360
            return $paginate['arguments'];
361
        }
362
363
        return [$this->perPage];
364
    }
365
366
    /**
367
     * Find query by method name.
368
     *
369
     * @param $method
370
     *
371
     * @return static
372
     */
373
    protected function findQueryByMethod($method)
374
    {
375
        return $this->queries->first(function ($query) use ($method) {
376
            return $query['method'] == $method;
377
        });
378
    }
379
380
    /**
381
     * Set the grid sort.
382
     *
383
     * @return void
384
     */
385
    protected function setSort()
386
    {
387
        $this->sort = Input::get($this->sortName, []);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Illuminate\Support\Faca...his->sortName, array()) of type * is incompatible with the declared type array of property $sort.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
388
        if (!is_array($this->sort)) {
389
            return;
390
        }
391
392
        if (empty($this->sort['column']) || empty($this->sort['type'])) {
393
            return;
394
        }
395
396
        if (str_contains($this->sort['column'], '.')) {
397
            $this->setRelationSort($this->sort['column']);
398
        } else {
399
            $this->resetOrderBy();
400
401
            $this->queries->push([
402
                'method'    => 'orderBy',
403
                'arguments' => [$this->sort['column'], $this->sort['type']],
404
            ]);
405
        }
406
    }
407
408
    /**
409
     * Set relation sort.
410
     *
411
     * @param string $column
412
     *
413
     * @return void
414
     */
415
    protected function setRelationSort($column)
416
    {
417
        list($relationName, $relationColumn) = explode('.', $column);
418
419
        if ($this->queries->contains(function ($query) use ($relationName) {
420
            return $query['method'] == 'with' && in_array($relationName, $query['arguments']);
421
        })) {
422
            $relation = $this->model->$relationName();
423
424
            $this->queries->push([
425
                'method'    => 'join',
426
                'arguments' => $this->joinParameters($relation),
427
            ]);
428
429
            $this->resetOrderBy();
430
431
            $this->queries->push([
432
                'method'    => 'orderBy',
433
                'arguments' => [
434
                    $relation->getRelated()->getTable().'.'.$relationColumn,
435
                    $this->sort['type'],
436
                ],
437
            ]);
438
        }
439
    }
440
441
    /**
442
     * Reset orderBy query.
443
     *
444
     * @return void
445
     */
446
    public function resetOrderBy()
447
    {
448
        $this->queries = $this->queries->reject(function ($query) {
449
            return $query['method'] == 'orderBy' || $query['method'] == 'orderByDesc';
450
        });
451
    }
452
453
    /**
454
     * Build join parameters for related model.
455
     *
456
     * `HasOne` and `BelongsTo` relation has different join parameters.
457
     *
458
     * @param Relation $relation
459
     *
460
     * @throws \Exception
461
     *
462
     * @return array
463
     */
464
    protected function joinParameters(Relation $relation)
465
    {
466
        $relatedTable = $relation->getRelated()->getTable();
467
468
        if ($relation instanceof BelongsTo) {
469
            return [
470
                $relatedTable,
471
                $relation->getForeignKey(),
472
                '=',
473
                $relatedTable.'.'.$relation->getRelated()->getKeyName(),
474
            ];
475
        }
476
477
        if ($relation instanceof HasOne) {
478
            return [
479
                $relatedTable,
480
                $relation->getQualifiedParentKeyName(),
481
                '=',
482
                $relation->getQualifiedForeignKeyName(),
483
            ];
484
        }
485
486
        throw new \Exception('Related sortable only support `HasOne` and `BelongsTo` relation.');
487
    }
488
489
    /**
490
     * @param string $method
491
     * @param array  $arguments
492
     *
493
     * @return $this
494
     */
495
    public function __call($method, $arguments)
496
    {
497
        $this->queries->push([
498
            'method'    => $method,
499
            'arguments' => $arguments,
500
        ]);
501
502
        return $this;
503
    }
504
505
    /**
506
     * @param $key
507
     *
508
     * @return mixed
509
     */
510
    public function __get($key)
511
    {
512
        $data = $this->buildData();
513
514
        if (array_key_exists($key, $data)) {
515
            return $data[$key];
516
        }
517
    }
518
}
519