Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
57.14% covered (warning)
57.14%
32 / 56
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
FixPositionsCommand
57.14% covered (warning)
57.14%
32 / 56
40.00% covered (danger)
40.00%
2 / 5
36.15
0.00% covered (danger)
0.00%
0 / 1
 handle
68.75% covered (warning)
68.75%
11 / 16
0.00% covered (danger)
0.00%
0 / 1
7.10
 fixUngroupable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fixGroupable
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 fixRelation
82.61% covered (warning)
82.61%
19 / 23
0.00% covered (danger)
0.00%
0 / 1
4.08
 fixPositions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Baril\Orderly\Console;
4
5use Baril\Orderly\Relations\BelongsToManyOrderable;
6use Illuminate\Console\Command;
7use Illuminate\Database\Eloquent\Model;
8
9class FixPositionsCommand extends Command
10{
11    protected $signature = 'orderly:fix-positions
12        {model : The model class.}
13        {relationName? : The relationship to fix.}';
14    protected $description = 'Rebuild the position column for a given orderable model or relation';
15
16    protected $chunks = 200;
17
18    public function handle()
19    {
20        $model = $this->argument('model');
21        $relationName = $this->argument('relationName');
22        if (!class_exists($model) || !is_subclass_of($model, Model::class)) {
23            $this->error($model . ' is not a valid model class!');
24            return;
25        }
26        $instance = new $model();
27
28        // If the relation name is provided, then we're fixing an ordered relation:
29        if ($relationName) {
30            $this->fixRelation($instance, $relationName);
31            return;
32        }
33        // else, it's an orderable model.
34
35        // Let's check that the model uses the Orderable trait:
36        if (!method_exists($model, 'getOrderColumn')) {
37            $this->error('{model} must be a valid model class and use the Orderable trait!');
38            return;
39        }
40
41        // The treatment will be different whether the model is groupable or not:
42        if ($instance->getGroupColumn()) {
43            $this->fixGroupable($instance);
44        } else {
45            $this->fixUngroupable($instance);
46        }
47        $this->info('Done!');
48    }
49
50    protected function fixUngroupable($instance)
51    {
52        $this->fixPositions($instance->newQuery()->ordered(), $instance);
53    }
54
55    protected function fixGroupable($instance)
56    {
57        $column = (array) $instance->getGroupColumn();
58        $query = $instance->newQueryWithoutScopes()->getQuery()
59                ->distinct()
60                ->select($column);
61        foreach ($column as $col) {
62            $query->orderBy($col);
63        }
64        $query->chunk($this->chunks, function ($groups) use ($instance, $column) {
65            foreach ($groups as $item) {
66                $group = [];
67                foreach ($column as $col) {
68                    $group[] = $item->$col;
69                }
70                $query = $instance->newQuery()->whereGroup($group)->ordered();
71                $this->fixPositions($query, $instance);
72                $this->line('<info>Fixed for group:</info> ' . implode(',', $group));
73            }
74        });
75    }
76
77    protected function fixRelation($instance, $relationName)
78    {
79        try {
80            $relation = $instance->$relationName();
81            if (!$relation instanceof BelongsToManyOrderable) {
82                $this->error($relationName . ' is not a valid belongs-to-many-ordered relationship!');
83                return;
84            }
85        } catch (\Exception $e) {
86            $this->error($relationName . ' is not a valid relationship!');
87            return;
88        }
89
90        $query = $instance->newQuery()
91                ->select($instance->getKeyName())
92                ->orderBy($instance->getKeyName());
93        $query->chunk($this->chunks, function ($items) use ($relationName) {
94            foreach ($items as $item) {
95                $relation = $item->$relationName();
96                $related = $relation->ordered()->get();
97                $rownum = 0;
98                $related->each(function ($item) use ($relation, &$rownum) {
99                    $id = $item->{$relation->getPivotAccessor()}->{$relation->getRelatedPivotKeyName()};
100                    $relation->updateExistingPivot($id, [
101                        $relation->getOrderColumn() => ++$rownum,
102                    ], false);
103                });
104                $this->line("<info>Fixed for model:</info> {$item->getKey()}");
105            }
106        });
107    }
108
109    protected function fixPositions($query, $instance)
110    {
111        $query->updateColumnWithRowNumber($instance->getOrderColumn());
112    }
113}