Completed
Pull Request — master (#1339)
by José
02:01
created

Plan::gatherUpdates()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
cc 5
eloc 15
nc 1
nop 1
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated * documentation files (the "Software"), to
9
 * deal in the Software without restriction, including without limitation the
10
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
 * sell copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
 * IN THE SOFTWARE.
24
 */
25
namespace Phinx\Db\Plan;
26
27
use Phinx\Db\Action\AddColumn;
28
use Phinx\Db\Action\AddForeignKey;
29
use Phinx\Db\Action\AddIndex;
30
use Phinx\Db\Action\ChangeColumn;
31
use Phinx\Db\Action\CreateTable;
32
use Phinx\Db\Action\DropForeignKey;
33
use Phinx\Db\Action\DropIndex;
34
use Phinx\Db\Action\DropTable;
35
use Phinx\Db\Action\RemoveColumn;
36
use Phinx\Db\Action\RenameColumn;
37
use Phinx\Db\Action\RenameTable;
38
use Phinx\Db\Adapter\AdapterInterface;
39
use Phinx\Db\Table\Table;
40
41
/**
42
 * A Plan takes an Intent and transforms int into a sequence of
43
 * instructions that can be correctly executed by an AdapterInterface.
44
 *
45
 * The main focus of Plan is to arrange the actions in the most efficient
46
 * way possible for the database.
47
 */
48
class Plan
49
{
50
51
    /**
52
     * List of tables to be created
53
     *
54
     * @var \Phinx\Db\Plan\NewTable[]
55
     */
56
    protected $tableCreates = [];
57
58
    /**
59
     * List of table updates
60
     *
61
     * @var \Phinx\Db\Plan\AlterTable[]
62
     */
63
    protected $tableUpdates = [];
64
65
    /**
66
     * List of table removals or renames
67
     *
68
     * @var \Phinx\Db\Plan\AlterTable[]
69
     */
70
    protected $tableMoves = [];
71
72
    /**
73
     * List of index additions or removals
74
     *
75
     * @var \Phinx\Db\Plan\AlterTable[]
76
     */
77
    protected $indexes = [];
78
79
    /**
80
     * List of constraint additions or removals
81
     *
82
     * @var \Phinx\Db\Plan\AlterTable[]
83
     */
84
    protected $constraints = [];
85
86
    /**
87
     * Constructor
88
     *
89
     * @param Intent $intent All the actions that should be executed
90
     */
91
    public function __construct(Intent $intent)
92
    {
93
        $this->createPlan($intent->getActions());
0 ignored issues
show
Documentation introduced by
$intent->getActions() is of type array<integer,object<Phinx\Db\Action\Action>>, but the function expects a object<Phinx\Db\Plan\Intent>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
94
    }
95
96
    /**
97
     * Parses the given Intent and creates the separate steps to execute
98
     *
99
     * @param Intent $actions The actions to use for the plan
100
     * @return void
101
     */
102
    protected function createPlan($actions)
103
    {
104
        $this->gatherCreates($actions);
0 ignored issues
show
Documentation introduced by
$actions is of type object<Phinx\Db\Plan\Intent>, but the function expects a array<integer,object<Phinx\Db\Action\Action>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
105
        $this->gatherUpdates($actions);
0 ignored issues
show
Documentation introduced by
$actions is of type object<Phinx\Db\Plan\Intent>, but the function expects a array<integer,object<Phinx\Db\Action\Action>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
106
        $this->gatherTableMoves($actions);
0 ignored issues
show
Documentation introduced by
$actions is of type object<Phinx\Db\Plan\Intent>, but the function expects a array<integer,object<Phinx\Db\Action\Action>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
107
        $this->gatherIndexes($actions);
0 ignored issues
show
Documentation introduced by
$actions is of type object<Phinx\Db\Plan\Intent>, but the function expects a array<integer,object<Phinx\Db\Action\Action>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
108
        $this->gatherConstraints($actions);
0 ignored issues
show
Documentation introduced by
$actions is of type object<Phinx\Db\Plan\Intent>, but the function expects a array<integer,object<Phinx\Db\Action\Action>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
109
        $this->resolveConflicts();
110
    }
111
112
    /**
113
     * Returns a nested list of all the steps to execute
114
     *
115
     * @return AlterTable[][]
116
     */
117
    protected function updatesSequence()
118
    {
119
        return [
120
            $this->tableUpdates,
121
            $this->constraints,
122
            $this->indexes,
123
            $this->tableMoves,
124
        ];
125
    }
126
127
    /**
128
     * Executes this plan using the given AdapterInterface
129
     *
130
     * @param AdapterInterface $executor The executor object for the plan
131
     * @return void
132
     */
133 View Code Duplication
    public function execute(AdapterInterface $executor)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134
    {
135
        foreach ($this->tableCreates as $newTable) {
136
            $executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes());
137
        }
138
139
        collection($this->updatesSequence())
140
            ->unfold()
141
            ->each(function ($updates) use ($executor) {
142
                $executor->executeActions($updates->getTable(), $updates->getActions());
143
            });
144
    }
145
146
    /**
147
     * Executes the inverse plan (rollback the actions) with the given AdapterInterface:w
148
     *
149
     * @param AdapterInterface $executor The executor object for the plan
150
     * @return void
151
     */
152 View Code Duplication
    public function executeInverse(AdapterInterface $executor)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
    {
154
        collection(array_reverse($this->updatesSequence()))
155
            ->unfold()
156
            ->each(function ($updates) use ($executor) {
157
                $executor->executeActions($updates->getTable(), $updates->getActions());
158
            });
159
160
        foreach ($this->tableCreates as $newTable) {
161
            $executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes());
162
        }
163
    }
164
165
    /**
166
     * Deletes certain actions from the plan if they are found to be conflicting or redundant.
167
     *
168
     * @return void
169
     */
170
    protected function resolveConflicts()
171
    {
172
        $actions = collection($this->tableMoves)
173
            ->unfold(function ($move) {
174
                return $move->getActions();
175
            });
176
177
        foreach ($actions as $action) {
178
            if ($action instanceof DropTable) {
179
                $this->tableUpdates = $this->forgetTable($action->getTable(), $this->tableUpdates);
180
                $this->constraints = $this->forgetTable($action->getTable(), $this->constraints);
181
                $this->indexes = $this->forgetTable($action->getTable(), $this->indexes);
182
            }
183
        }
184
    }
185
186
    /**
187
     * Deletes all actions related to the given table and keeps the
188
     * rest
189
     *
190
     * @param Table $table The table to find in the list of actions
191
     * @param AlterTable[] $actions The actions to transform
192
     * @return AlterTable[] The list of actions without actions for the given table
193
     */
194
    protected function forgetTable(Table $table, $actions)
195
    {
196
        $result = [];
197
        foreach ($actions as $action) {
198
            if ($action->getTable()->getName() === $table->getName()) {
199
                continue;
200
            }
201
            $result[] = $action;
202
        }
203
204
        return $result;
205
    }
206
207
    /**
208
     * Collects all table creation actions from the given intent
209
     *
210
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
211
     * @return void
212
     */
213
    protected function gatherCreates($actions)
214
    {
215
        collection($actions)
216
            ->filter(function ($action) {
217
                return $action instanceof CreateTable;
218
            })
219
            ->map(function ($action) {
220
                return [$table->getName(), new NewTable($action->getTable())];
0 ignored issues
show
Bug introduced by
The variable $table does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
221
            })
222
            ->each(function ($step) {
223
                $this->tableCreates[$step[0]] = $step[1];
224
            });
225
226
        collection($actions)
227
            ->filter(function ($action) {
228
                return $action instanceof AddColumn
229
                    || $action instanceof AddIndex;
230
            })
231
            ->filter(function ($action) {
232
                return isset($this->tableCreates[$action->getTable()->getName()]);
233
            })
234
            ->each(function ($action) {
235
                $table = $action->getTable();
236
237
                if ($action instanceof AddColumn) {
238
                    $this->tableCreates[$table->getName()]->addColumn($action->getColumn());
239
                }
240
241
                if ($action instanceof AddIndex) {
242
                    $this->tableCreates[$table->getName()]->addIndex($action->getIndex());
243
                }
244
            });
245
    }
246
247
    /**
248
     * Collects all alter table actions from the given intent
249
     *
250
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
251
     * @return void
252
     */
253
    protected function gatherUpdates($actions)
254
    {
255
        collection($actions)
256
            ->filter(function ($action) {
257
                return $action instanceof AddColumn
258
                    || $action instanceof ChangeColumn
259
                    || $action instanceof RemoveColumn
260
                    || $action instanceof RenameColumn;
261
            })
262
            // We are only concerned with table changes
263
            ->reject(function ($action) {
264
                return isset($this->tableCreates[$action->getTable()->getName()]);
265
            })
266
            ->each(function ($action) {
267
                $table = $action->getTable();
268
                $name = $table->getName();
269
270
                if (!isset($this->tableUpdates[$name])) {
271
                    $this->tableUpdates[$name] = new AlterTable($table);
272
                }
273
274
                $this->tableUpdates[$name]->addAction($action);
275
            });
276
    }
277
278
    /**
279
     * Collects all alter table drop and renames from the given intent
280
     *
281
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
282
     * @return void
283
     */
284 View Code Duplication
    protected function gatherTableMoves($actions)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
285
    {
286
        collection($actions)
287
            ->filter(function ($action) {
288
                return $action instanceof DropTable
289
                    || $action instanceof RenameTable;
290
            })
291
            ->each(function ($action) {
292
                $table = $action->getTable();
293
                $name = $table->getName();
294
295
                if (!isset($this->tableMoves[$name])) {
296
                    $this->tableMoves[$name] = new AlterTable($table);
297
                }
298
299
                $this->tableMoves[$name]->addAction($action);
300
            });
301
    }
302
303
    /**
304
     * Collects all index creation and drops from the given intent
305
     *
306
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
307
     * @return void
308
     */
309
    protected function gatherIndexes($actions)
310
    {
311
        collection($actions)
312
            ->filter(function ($action) {
313
                return $action instanceof AddIndex
314
                    || $action instanceof DropIndex;
315
            })
316
            ->reject(function ($action) {
317
                // Indexes for new tables are created inline
318
                // so we don't wan't them here too
319
                return isset($this->tableCreates[$action->getTable()->getName()]);
320
            })
321
            ->each(function ($action) {
322
                $table = $action->getTable();
323
                $name = $table->getName();
324
325
                if (!isset($this->indexes[$name])) {
326
                    $this->indexes[$name] = new AlterTable($table);
327
                }
328
329
                $this->indexes[$name]->addAction($action);
330
            });
331
    }
332
333
    /**
334
     * Collects all foreign key creation and drops from the given intent
335
     *
336
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
337
     * @return void
338
     */
339 View Code Duplication
    protected function gatherConstraints($actions)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
340
    {
341
        collection($actions)
342
            ->filter(function ($action) {
343
                return $action instanceof AddForeignKey
344
                    || $action instanceof DropForeignKey;
345
            })
346
            ->each(function ($action) {
347
                $table = $action->getTable();
348
                $name = $table->getName();
349
350
                if (!isset($this->constraints[$name])) {
351
                    $this->constraints[$name] = new AlterTable($table);
352
                }
353
354
                $this->constraints[$name]->addAction($action);
355
            });
356
    }
357
}
358