Completed
Branch feature/pre-split (7b42f5)
by Anton
03:44
created

SynchronizationBus   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 139
rs 10
c 0
b 0
f 0
wmc 18
lcom 1
cbo 6

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getTables() 0 4 1
A sortedTables() 0 12 2
A synchronize() 0 23 3
A saveTables() 0 6 2
A beginTransaction() 0 8 2
A commitTransaction() 0 8 2
A rollbackTransaction() 0 8 2
A collectDrivers() 0 8 3
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Database\Helpers;
10
11
use Spiral\Core\Component;
12
use Spiral\Database\Entities\Driver;
13
use Spiral\Database\Schemas\AbstractTable;
14
use Spiral\Debug\Traits\LoggerTrait;
15
use Spiral\Support\DFSSorter;
16
17
/**
18
 * Saves multiple linked tables at once but treating their cross dependency.
19
 */
20
class SynchronizationBus extends Component
21
{
22
    use LoggerTrait;
23
24
    /**
25
     * @var AbstractTable[]
26
     */
27
    private $tables = [];
28
29
    /**
30
     * @var Driver[]
31
     */
32
    private $drivers = [];
33
34
    /**
35
     * @param AbstractTable[] $tables
36
     */
37
    public function __construct(array $tables)
38
    {
39
        $this->tables = $tables;
40
41
        $this->collectDrivers();
42
    }
43
44
    /**
45
     * @return AbstractTable[]
46
     */
47
    public function getTables()
48
    {
49
        return $this->tables;
50
    }
51
52
    /**
53
     * List of tables sorted in order of cross dependency.
54
     *
55
     * @return AbstractTable[]
56
     */
57
    public function sortedTables(): array
58
    {
59
        /*
60
         * Tables has to be sorted using topological graph to execute operations in a valid order.
61
         */
62
        $sorter = new DFSSorter();
63
        foreach ($this->tables as $table) {
64
            $sorter->addItem($table->getName(), $table, $table->getDependencies());
65
        }
66
67
        return $sorter->sort();
68
    }
69
70
    /**
71
     * Syncronize table schemas.
72
     *
73
     * @throws \Exception
74
     */
75
    public function synchronize()
76
    {
77
        $this->beginTransaction();
78
79
        try {
80
            //Dropping non declared foreign keys
81
            $this->saveTables(false, false, true);
82
83
            //Dropping non declared indexes
84
            $this->saveTables(false, true, true);
85
86
            //Dropping non declared columns
87
            $this->saveTables(true, true, true);
88
        } catch (\Exception $e) {
89
            $this->rollbackTransaction();
90
            throw $e;
91
        } catch (\Throwable $e) {
92
            $this->rollbackTransaction();
93
            throw $e;
94
        }
95
96
        $this->commitTransaction();
97
    }
98
99
    /**
100
     * @param bool $forgetColumns
101
     * @param bool $forgetIndexes
102
     * @param bool $forgetForeigns
103
     */
104
    protected function saveTables(bool $forgetColumns, bool $forgetIndexes, bool $forgetForeigns)
105
    {
106
        foreach ($this->sortedTables() as $table) {
107
            $table->save($forgetColumns, $forgetIndexes, $forgetForeigns);
108
        }
109
    }
110
111
    /**
112
     * Begin mass transaction.
113
     */
114
    protected function beginTransaction()
115
    {
116
        $this->logger()->debug('Begin transaction');
117
118
        foreach ($this->drivers as $driver) {
119
            $driver->beginTransaction();
120
        }
121
    }
122
123
    /**
124
     * Commit mass transaction.
125
     */
126
    protected function commitTransaction()
127
    {
128
        $this->logger()->debug('Commit transaction');
129
130
        foreach ($this->drivers as $driver) {
131
            $driver->commitTransaction();
132
        }
133
    }
134
135
    /**
136
     * Roll back mass transaction.
137
     */
138
    protected function rollbackTransaction()
139
    {
140
        $this->logger()->warning('Roll back transaction');
141
142
        foreach ($this->drivers as $driver) {
143
            $driver->rollbackTransaction();
144
        }
145
    }
146
147
    /**
148
     * Collecting all involved drivers.
149
     */
150
    private function collectDrivers()
151
    {
152
        foreach ($this->tables as $table) {
153
            if (!in_array($table->driver(), $this->drivers, true)) {
154
                $this->drivers[] = $table->driver();
155
            }
156
        }
157
    }
158
}
159