Completed
Branch feature/pre-split (669609)
by Anton
03:30
created

SynchronizationPool::resetSchemas()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
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
            $this->dropForeigns($logger);
0 ignored issues
show
Bug introduced by
It seems like $logger defined by parameter $logger on line 79 can be null; however, Spiral\Database\Helpers\...ionPool::dropForeigns() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
99
100
            //Drop not-needed indexes
101
            $this->dropIndexes($logger);
0 ignored issues
show
Bug introduced by
It seems like $logger defined by parameter $logger on line 79 can be null; however, Spiral\Database\Helpers\...tionPool::dropIndexes() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
102
103
            //Other changes [NEW TABLES WILL BE CREATED HERE!]
104
            $this->runChanges($logger);
0 ignored issues
show
Bug introduced by
It seems like $logger defined by parameter $logger on line 79 can be null; however, Spiral\Database\Helpers\...ationPool::runChanges() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
105
106
            //Finishing with new foreign keys
107
            $this->resetSchemas($logger);
0 ignored issues
show
Bug introduced by
It seems like $logger defined by parameter $logger on line 79 can be null; however, Spiral\Database\Helpers\...ionPool::resetSchemas() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
108
        } catch (\Throwable $e) {
109
            $this->rollbackTransaction();
110
            throw $e;
111
        }
112
113
        $this->commitTransaction();
114
    }
115
116
    /**
117
     * Begin mass transaction.
118
     */
119
    protected function beginTransaction()
120
    {
121
        foreach ($this->drivers as $driver) {
122
            $driver->beginTransaction();
123
        }
124
    }
125
126
    /**
127
     * Commit mass transaction.
128
     */
129
    protected function commitTransaction()
130
    {
131
        foreach ($this->drivers as $driver) {
132
            $driver->commitTransaction();
133
        }
134
    }
135
136
    /**
137
     * Roll back mass transaction.
138
     */
139
    protected function rollbackTransaction()
140
    {
141
        foreach ($this->drivers as $driver) {
142
            $driver->rollbackTransaction();
143
        }
144
    }
145
146
    /**
147
     * Collecting all involved drivers.
148
     */
149
    private function collectDrivers()
150
    {
151
        foreach ($this->tables as $table) {
152
            if (!in_array($table->getDriver(), $this->drivers, true)) {
153
                $this->drivers[] = $table->getDriver();
154
            }
155
        }
156
    }
157
158
    /**
159
     * @param LoggerInterface $logger
160
     */
161
    private function dropForeigns(LoggerInterface $logger)
162
    {
163
        foreach ($this->sortedTables() as $table) {
164
            if ($table->exists()) {
165
                $table->save(Behaviour::DROP_FOREIGNS, $logger, false);
166
            }
167
        }
168
    }
169
170
    /**
171
     * @param LoggerInterface $logger
172
     */
173
    private function dropIndexes(LoggerInterface $logger)
174
    {
175
        foreach ($this->sortedTables() as $table) {
176
            if ($table->exists()) {
177
                $table->save(Behaviour::DROP_INDEXES, $logger, false);
178
            }
179
        }
180
181
    }
182
183
    /**
184
     * @param LoggerInterface $logger
185
     */
186
    private function runChanges(LoggerInterface $logger)
187
    {
188
        foreach ($this->sortedTables() as $table) {
189
            $table->save(
190
                Behaviour::DO_ALL ^ Behaviour::DROP_FOREIGNS ^ Behaviour::DROP_INDEXES ^ Behaviour::CREATE_FOREIGNS,
191
                $logger
192
            );
193
        }
194
    }
195
196
    /**
197
     * @param LoggerInterface $logger
198
     */
199
    private function resetSchemas(LoggerInterface $logger)
200
    {
201
        foreach ($this->sortedTables() as $table) {
202
            $table->save(Behaviour::CREATE_FOREIGNS, $logger, true);
203
        }
204
    }
205
}
206