Completed
Pull Request — master (#1339)
by José
05:14
created

Plan::gatherCreates()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 21
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
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
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
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
                $table = $action->getTable();
221
                return [$table->getName(), new NewTable($table)];
222
            })
223
            ->each(function ($step) {
224
                $this->tableCreates[$step[0]] = $step[1];
225
            });
226
227
        collection($actions)
228
            ->filter(function ($action) {
229
                return $action instanceof AddColumn
230
                    || $action instanceof AddIndex;
231
            })
232
            ->filter(function ($action) {
233
                return isset($this->tableCreates[$action->getTable()->getName()]);
234
            })
235
            ->each(function ($action) {
236
                $table = $action->getTable();
237
238
                if ($action instanceof AddColumn) {
239
                    $this->tableCreates[$table->getName()]->addColumn($action->getColumn());
240
                }
241
242
                if ($action instanceof AddIndex) {
243
                    $this->tableCreates[$table->getName()]->addIndex($action->getIndex());
244
                }
245
            });
246
    }
247
248
    /**
249
     * Collects all alter table actions from the given intent
250
     *
251
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
252
     * @return void
253
     */
254
    protected function gatherUpdates($actions)
255
    {
256
        collection($actions)
257
            ->filter(function ($action) {
258
                return $action instanceof AddColumn
259
                    || $action instanceof ChangeColumn
260
                    || $action instanceof RemoveColumn
261
                    || $action instanceof RenameColumn;
262
            })
263
            // We are only concerned with table changes
264
            ->reject(function ($action) {
265
                return isset($this->tableCreates[$action->getTable()->getName()]);
266
            })
267
            ->each(function ($action) {
268
                $table = $action->getTable();
269
                $name = $table->getName();
270
271
                if (!isset($this->tableUpdates[$name])) {
272
                    $this->tableUpdates[$name] = new AlterTable($table);
273
                }
274
275
                $this->tableUpdates[$name]->addAction($action);
276
            });
277
    }
278
279
    /**
280
     * Collects all alter table drop and renames from the given intent
281
     *
282
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
283
     * @return void
284
     */
285 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...
286
    {
287
        collection($actions)
288
            ->filter(function ($action) {
289
                return $action instanceof DropTable
290
                    || $action instanceof RenameTable;
291
            })
292
            ->each(function ($action) {
293
                $table = $action->getTable();
294
                $name = $table->getName();
295
296
                if (!isset($this->tableMoves[$name])) {
297
                    $this->tableMoves[$name] = new AlterTable($table);
298
                }
299
300
                $this->tableMoves[$name]->addAction($action);
301
            });
302
    }
303
304
    /**
305
     * Collects all index creation and drops from the given intent
306
     *
307
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
308
     * @return void
309
     */
310
    protected function gatherIndexes($actions)
311
    {
312
        collection($actions)
313
            ->filter(function ($action) {
314
                return $action instanceof AddIndex
315
                    || $action instanceof DropIndex;
316
            })
317
            ->reject(function ($action) {
318
                // Indexes for new tables are created inline
319
                // so we don't wan't them here too
320
                return isset($this->tableCreates[$action->getTable()->getName()]);
321
            })
322
            ->each(function ($action) {
323
                $table = $action->getTable();
324
                $name = $table->getName();
325
326
                if (!isset($this->indexes[$name])) {
327
                    $this->indexes[$name] = new AlterTable($table);
328
                }
329
330
                $this->indexes[$name]->addAction($action);
331
            });
332
    }
333
334
    /**
335
     * Collects all foreign key creation and drops from the given intent
336
     *
337
     * @param \Phinx\Db\Action\Action[] $actions The actions to parse
338
     * @return void
339
     */
340 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...
341
    {
342
        collection($actions)
343
            ->filter(function ($action) {
344
                return $action instanceof AddForeignKey
345
                    || $action instanceof DropForeignKey;
346
            })
347
            ->each(function ($action) {
348
                $table = $action->getTable();
349
                $name = $table->getName();
350
351
                if (!isset($this->constraints[$name])) {
352
                    $this->constraints[$name] = new AlterTable($table);
353
                }
354
355
                $this->constraints[$name]->addAction($action);
356
            });
357
    }
358
}
359