Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
94.29% |
33 / 35 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
| FixTreeCommand | |
94.29% |
33 / 35 |
|
50.00% |
1 / 2 |
6.01 | |
0.00% |
0 / 1 |
| handle | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
4.37 | |||
| rebuildClosures | |
100.00% |
28 / 28 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | // @todo make agnostic |
| 4 | |
| 5 | namespace Baril\Bonsai\Console; |
| 6 | |
| 7 | use Illuminate\Console\Command; |
| 8 | use Illuminate\Database\Eloquent\Model; |
| 9 | |
| 10 | class FixTreeCommand extends Command |
| 11 | { |
| 12 | protected $signature = 'bonsai:fix {model : The model class.}'; |
| 13 | protected $description = 'Rebuilds the closures for a given tree'; |
| 14 | |
| 15 | public function handle() |
| 16 | { |
| 17 | $model = $this->input->getArgument('model'); |
| 18 | if ( |
| 19 | !class_exists($model) |
| 20 | || !is_subclass_of($model, Model::class) |
| 21 | || !method_exists($model, 'getClosureTable') |
| 22 | ) { |
| 23 | $this->error('{model} must be a valid model class and use the BelongsToTree trait!'); |
| 24 | return; |
| 25 | } |
| 26 | |
| 27 | $this->rebuildClosures($model); |
| 28 | } |
| 29 | |
| 30 | protected function rebuildClosures($model) |
| 31 | { |
| 32 | $instance = new $model(); |
| 33 | $connection = $instance->getConnection(); |
| 34 | $connection->transaction(function () use ($instance, $connection) { |
| 35 | $table = $instance->getTable(); |
| 36 | $parentKey = $instance->getParentForeignKeyName(); |
| 37 | $primaryKey = $instance->getKeyName(); |
| 38 | $closureTable = $instance->getClosureTable(); |
| 39 | |
| 40 | // Delete old closures: |
| 41 | $connection->table($closureTable)->delete(); |
| 42 | |
| 43 | // Insert "self-closures": |
| 44 | $select = $connection->table($table)->select($primaryKey, $primaryKey, $connection->raw('0')); |
| 45 | $connection->table($closureTable)->insertUsing(['ancestor_id', 'descendant_id', 'depth'], $select); |
| 46 | |
| 47 | // Increment depth and insert closures until there's nothing left to insert: |
| 48 | $depth = 1; |
| 49 | $continue = true; |
| 50 | while ($continue) { |
| 51 | // INSERT INTO $closureTable (ancestor_id, descendant_id, depth) |
| 52 | // SELECT closure_table.ancestor_id, main_table.$primaryKey, $depth |
| 53 | // FROM $table AS main_table |
| 54 | // INNER JOIN $closureTable AS closure_table |
| 55 | // ON main_table.$parentKey = closure_table.descendant_id |
| 56 | // WHERE closure_table.depth = $depth - 1" |
| 57 | $select = $connection |
| 58 | ->table($table, 'main_table') |
| 59 | ->join( |
| 60 | "$closureTable as closure_table", |
| 61 | "main_table.$parentKey", |
| 62 | '=', |
| 63 | 'closure_table.descendant_id' |
| 64 | ) |
| 65 | ->where('closure_table.depth', '=', $depth - 1) |
| 66 | ->select('closure_table.ancestor_id', "main_table.$primaryKey", $connection->raw((string) $depth)); |
| 67 | $connection->table($closureTable)->insertUsing(['ancestor_id', 'descendant_id', 'depth'], $select); |
| 68 | |
| 69 | $continue = (bool) $connection->table($closureTable)->where('depth', '=', $depth)->exists(); |
| 70 | $depth++; |
| 71 | } |
| 72 | }); |
| 73 | |
| 74 | $this->line("<info>Rebuilt the closures for:</info> $model"); |
| 75 | } |
| 76 | } |