1: <?php
2:
3: namespace Baril\Bonsai\Console;
4:
5: use Baril\Bonsai\Console\Concerns\InteractsWithTree;
6: use Illuminate\Console\Command;
7:
8: class FixTreeCommand extends Command
9: {
10: use InteractsWithTree;
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:
19: $this->checkModel($model);
20:
21: $this->rebuildClosures($model);
22: }
23:
24: protected function rebuildClosures($model)
25: {
26: $instance = new $model();
27: $connection = $instance->getConnection();
28: $connection->transaction(function () use ($instance, $connection) {
29: $table = $instance->getTable();
30: $parentKey = $instance->getParentForeignKeyName();
31: $primaryKey = $instance->getKeyName();
32: $closureTable = $instance->getClosureTable();
33:
34: // Delete old closures:
35: $connection->table($closureTable)->delete();
36:
37: // Insert "self-closures":
38: $select = $connection->table($table)->select($primaryKey, $primaryKey, $connection->raw('0'));
39: $connection->table($closureTable)->insertUsing(['ancestor_id', 'descendant_id', 'depth'], $select);
40:
41: // Increment depth and insert closures until there's nothing left to insert:
42: $depth = 1;
43: $continue = true;
44: while ($continue) {
45: // INSERT INTO $closureTable (ancestor_id, descendant_id, depth)
46: // SELECT closure_table.ancestor_id, main_table.$primaryKey, $depth
47: // FROM $table AS main_table
48: // INNER JOIN $closureTable AS closure_table
49: // ON main_table.$parentKey = closure_table.descendant_id
50: // WHERE closure_table.depth = $depth - 1"
51: $select = $connection
52: ->table($table, 'main_table')
53: ->join(
54: "$closureTable as closure_table",
55: "main_table.$parentKey",
56: '=',
57: 'closure_table.descendant_id'
58: )
59: ->where('closure_table.depth', '=', $depth - 1)
60: ->select('closure_table.ancestor_id', "main_table.$primaryKey", $connection->raw((string) $depth));
61: $connection->table($closureTable)->insertUsing(['ancestor_id', 'descendant_id', 'depth'], $select);
62:
63: $continue = (bool) $connection->table($closureTable)->where('depth', '=', $depth)->exists();
64: $depth++;
65: }
66: });
67:
68: $this->line("<info>Rebuilt the closures for:</info> $model");
69: }
70: }
71: