Completed
Branch feature/pre-split (ecea15)
by Anton
03:28
created

SynchronizationPool   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 145
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

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

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getTables() 0 4 1
A sortedTables() 0 12 2
D run() 0 38 8
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 Psr\Log\LoggerInterface;
12
use Spiral\Core\Component;
13
use Spiral\Database\Entities\AbstractHandler as Behaviour;
14
use Spiral\Database\Entities\Driver;
15
use Spiral\Database\Schemas\Prototypes\AbstractTable;
16
use Spiral\Debug\Traits\LoggerTrait;
17
use Spiral\Support\DFSSorter;
18
19
/**
20
 * Saves multiple linked tables at once but treating their cross dependency.
21
 */
22
class SynchronizationPool extends Component
23
{
24
    use LoggerTrait;
25
26
    /**
27
     * @var AbstractTable[]
28
     */
29
    private $tables = [];
30
31
    /**
32
     * @var Driver[]
33
     */
34
    private $drivers = [];
35
36
    /**
37
     * @param AbstractTable[] $tables
38
     */
39
    public function __construct(array $tables)
40
    {
41
        $this->tables = $tables;
42
43
        $this->collectDrivers();
44
    }
45
46
    /**
47
     * @return AbstractTable[]
48
     */
49
    public function getTables()
50
    {
51
        return $this->tables;
52
    }
53
54
    /**
55
     * List of tables sorted in order of cross dependency.
56
     *
57
     * @return AbstractTable[]
58
     */
59
    public function sortedTables(): array
60
    {
61
        /*
62
         * Tables has to be sorted using topological graph to execute operations in a valid order.
63
         */
64
        $sorter = new DFSSorter();
65
        foreach ($this->tables as $table) {
66
            $sorter->addItem($table->getName(), $table, $table->getDependencies());
67
        }
68
69
        return $sorter->sort();
70
    }
71
72
    /**
73
     * Synchronize tables.
74
     *
75
     * @param LoggerInterface|null $logger
76
     *
77
     * @throws \Exception
78
     * @throws \Throwable
79
     */
80
    public function run(LoggerInterface $logger = null)
81
    {
82
        $this->beginTransaction();
83
84
        try {
85
            //Drop not-needed foreign keys and alter everything else
86
            foreach ($this->sortedTables() as $table) {
87
                if ($table->exists()) {
88
                    $table->save(Behaviour::DROP_FOREIGNS, $logger, false);
89
                }
90
            }
91
92
            //Drop not-needed indexes
93
            foreach ($this->sortedTables() as $table) {
94
                if ($table->exists()) {
95
                    $table->save(Behaviour::DROP_INDEXES, $logger, false);
96
                }
97
            }
98
99
            //Other changes [NEW TABLES WILL BE CREATED HERE!]
100
            foreach ($this->sortedTables() as $table) {
101
                $table->save(
102
                    Behaviour::DO_ALL ^ Behaviour::DROP_FOREIGNS ^ Behaviour::DROP_INDEXES ^ Behaviour::CREATE_FOREIGNS,
103
                    $logger
104
                );
105
            }
106
107
            //Finishing with new foreign keys
108
            foreach ($this->sortedTables() as $table) {
109
                $table->save(Behaviour::CREATE_FOREIGNS, $logger, true);
110
            }
111
        } catch (\Throwable $e) {
112
            $this->rollbackTransaction();
113
            throw $e;
114
        }
115
116
        $this->commitTransaction();
117
    }
118
119
    /**
120
     * Begin mass transaction.
121
     */
122
    protected function beginTransaction()
123
    {
124
        $this->logger()->debug('Begin transaction');
125
126
        foreach ($this->drivers as $driver) {
127
            $driver->beginTransaction();
128
        }
129
    }
130
131
    /**
132
     * Commit mass transaction.
133
     */
134
    protected function commitTransaction()
135
    {
136
        $this->logger()->debug('Commit transaction');
137
138
        foreach ($this->drivers as $driver) {
139
            $driver->commitTransaction();
140
        }
141
    }
142
143
    /**
144
     * Roll back mass transaction.
145
     */
146
    protected function rollbackTransaction()
147
    {
148
        $this->logger()->warning('Roll back transaction');
149
150
        foreach ($this->drivers as $driver) {
151
            $driver->rollbackTransaction();
152
        }
153
    }
154
155
    /**
156
     * Collecting all involved drivers.
157
     */
158
    private function collectDrivers()
159
    {
160
        foreach ($this->tables as $table) {
161
            if (!in_array($table->getDriver(), $this->drivers, true)) {
162
                $this->drivers[] = $table->getDriver();
163
            }
164
        }
165
    }
166
}
167