Completed
Branch feature/pre-split (4c50c1)
by Anton
03:17
created

SynchronizationPool::run()   F

Complexity

Conditions 11
Paths 594

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 26
nc 594
nop 1
dl 0
loc 51
rs 3.6791
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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