Schema   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 305
Duplicated Lines 11.15 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 19
Bugs 6 Features 1
Metric Value
wmc 32
c 19
b 6
f 1
lcom 1
cbo 10
dl 34
loc 305
rs 9.6

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A createCurrentSchema() 0 4 1
A connection() 0 4 1
A create() 0 6 1
A alter() 0 6 1
A drop() 0 6 1
A retrieveModels() 0 13 3
A buildCreate() 0 17 3
A buildAlter() 17 17 3
C createTable() 0 44 7
A quoteIdentifier() 0 13 3
A buildDrop() 17 17 3
A execute() 0 14 2
A queryString() 0 4 1
A reset() 0 7 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/*
4
 * This file is part of the Storage package
5
 *
6
 * (c) Michal Wachowski <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Moss\Storage\Schema;
13
14
use Doctrine\DBAL\Connection;
15
use Doctrine\DBAL\Schema\Schema as SchemaAsset;
16
use Moss\Storage\Model\ModelBag;
17
use Moss\Storage\Model\ModelInterface;
18
19
/**
20
 * Schema used to create and execute table related operations (create, alter, drop)
21
 *
22
 * @author  Michal Wachowski <[email protected]>
23
 * @package Moss\Storage
24
 */
25
class Schema implements SchemaInterface
26
{
27
    const OPERATION_CREATE = 'create';
28
    const OPERATION_ALTER = 'alter';
29
    const OPERATION_DROP = 'drop';
30
31
    /**
32
     * @var Connection
33
     */
34
    protected $connection;
35
36
    /**
37
     * @var ModelBag
38
     */
39
    protected $models;
40
41
    /**
42
     * @var SchemaAsset;
43
     */
44
    protected $schema;
45
46
    protected $queries = [];
47
48
    /**
49
     * Constructor
50
     *
51
     * @param Connection $connection
52
     * @param ModelBag   $models
53
     */
54
    public function __construct(Connection $connection, ModelBag $models)
55
    {
56
        $this->connection = $connection;
57
        $this->models = $models;
58
59
        $this->createCurrentSchema();
60
    }
61
62
    /**
63
     * Creates instance with current schema
64
     */
65
    protected function createCurrentSchema()
66
    {
67
        $this->schema = $this->connection->getSchemaManager()->createSchema();
68
    }
69
70
    /**
71
     * Returns connection
72
     *
73
     * @return Connection
74
     */
75
    public function connection()
76
    {
77
        return $this->connection;
78
    }
79
80
    /**
81
     * Sets create operation
82
     *
83
     * @param array $entityName
84
     *
85
     * @return $this
86
     */
87
    public function create(array $entityName = [])
88
    {
89
        $this->buildCreate($this->retrieveModels($entityName));
0 ignored issues
show
Documentation introduced by
$this->retrieveModels($entityName) is of type object<Moss\Storage\Model\ModelInterface>|array, but the function expects a array<integer,object<Mos...\Model\ModelInterface>>.

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...
90
91
        return $this;
92
    }
93
94
    /**
95
     * Sets alter operation
96
     *
97
     * @param array $entityName
98
     *
99
     * @return $this
100
     */
101
    public function alter(array $entityName = [])
102
    {
103
        $this->buildAlter($this->retrieveModels($entityName));
0 ignored issues
show
Documentation introduced by
$this->retrieveModels($entityName) is of type object<Moss\Storage\Model\ModelInterface>|array, but the function expects a array<integer,object<Mos...\Model\ModelInterface>>.

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...
104
105
        return $this;
106
    }
107
108
    /**
109
     * Sets drop operation
110
     *
111
     * @param array $entityName
112
     *
113
     * @return $this
114
     */
115
    public function drop(array $entityName = [])
116
    {
117
        $this->buildDrop($this->retrieveModels($entityName));
0 ignored issues
show
Documentation introduced by
$this->retrieveModels($entityName) is of type object<Moss\Storage\Model\ModelInterface>|array, but the function expects a array<integer,object<Mos...\Model\ModelInterface>>.

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...
118
119
        return $this;
120
    }
121
122
    /**
123
     * Returns array with models for operation
124
     *
125
     * @param array $entity
126
     *
127
     * @return ModelInterface[]
128
     */
129
    protected function retrieveModels(array $entity = [])
130
    {
131
        $models = [];
132
        foreach ((array) $entity as $node) {
133
            $models[] = $this->models->get($node);
134
        }
135
136
        if (empty($models)) {
137
            $models = $this->models->all();
138
        }
139
140
        return $models;
141
    }
142
143
    /**
144
     * Builds create table queries
145
     *
146
     * @param ModelInterface[] $models
147
     *
148
     * @throws SchemaException
149
     */
150
    protected function buildCreate(array $models)
151
    {
152
        $schemaManager = $this->connection->getSchemaManager();
153
154
        foreach ($models as $model) {
155
            if ($schemaManager->tablesExist([$model->table()])) {
156
                throw new SchemaException(sprintf('Unable to create table, table "%s" already exists', $model->table()));
157
            }
158
159
            $this->createTable($this->schema, $model);
160
        }
161
162
        $this->queries = array_merge(
163
            $this->queries,
164
            $this->schema->toSql($this->connection->getDatabasePlatform())
165
        );
166
    }
167
168
    /**
169
     * Builds table alteration queries
170
     *
171
     * @param ModelInterface[] $models
172
     */
173 View Code Duplication
    protected function buildAlter(array $models)
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...
174
    {
175
        $fromSchema = $this->connection->getSchemaManager()->createSchema();
176
        $toSchema = clone $fromSchema;
177
178
        foreach ($models as $model) {
179
            if ($toSchema->hasTable($model->table())) {
180
                $toSchema->dropTable($model->table());
181
            }
182
183
            $this->createTable($toSchema, $model);
184
        }
185
186
        $sql = $fromSchema->getMigrateToSql($toSchema, $this->connection->getDatabasePlatform());
187
188
        $this->queries = array_merge($this->queries, $sql);
189
    }
190
191
    /**
192
     * Creates table from model into schema
193
     *
194
     * @param SchemaAsset    $schema
195
     * @param ModelInterface $model
196
     */
197
    protected function createTable(SchemaAsset $schema, ModelInterface $model)
198
    {
199
        $table = $schema->createTable($this->quoteIdentifier($model->table()));
0 ignored issues
show
Bug introduced by
It seems like $this->quoteIdentifier($model->table()) targeting Moss\Storage\Schema\Schema::quoteIdentifier() can also be of type array; however, Doctrine\DBAL\Schema\Schema::createTable() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
200
201
        foreach ($model->fields() as $field) {
202
            $table->addColumn(
203
                $this->quoteIdentifier($field->mappedName()),
204
                $field->type(),
205
                $field->attributes()
206
            );
207
        }
208
209
        foreach ($model->indexes() as $index) {
210
            switch ($index->type()) {
211
                case 'primary':
212
                    $table->setPrimaryKey(
213
                        $this->quoteIdentifier($index->fields()),
214
                        $this->quoteIdentifier($index->name())
215
                    );
216
                    break;
217
                case 'unique':
218
                    $table->addUniqueIndex(
219
                        $this->quoteIdentifier($index->fields()),
220
                        $this->quoteIdentifier($index->name())
221
                    );
222
                    break;
223
                case 'foreign':
224
                    $table->addForeignKeyConstraint(
225
                        $index->table(),
226
                        $this->quoteIdentifier(array_keys($index->fields())),
227
                        $this->quoteIdentifier(array_values($index->fields())),
228
                        ['onUpdate' => 'CASCADE', 'onDelete' => 'RESTRICT'],
229
                        $this->quoteIdentifier($index->name())
230
                    );
231
                    break;
232
                case 'index':
233
                default:
234
                    $table->addIndex(
235
                        $this->quoteIdentifier($index->fields()),
236
                        $this->quoteIdentifier($index->name())
237
                    );
238
            }
239
        }
240
    }
241
242
    /**
243
     * Quotes SQL identifier or array of identifiers
244
     *
245
     * @param string|array $identifier
246
     *
247
     * @return string|array
248
     */
249
    protected function quoteIdentifier($identifier)
250
    {
251
        if (!is_array($identifier)) {
252
            return $this->connection->quoteIdentifier($identifier);
253
        }
254
255
        foreach ($identifier as &$value) {
256
            $value = $this->connection->quoteIdentifier($value);
257
            unset($value);
258
        }
259
260
        return $identifier;
261
    }
262
263
    /**
264
     * Builds drop table query
265
     *
266
     * @param ModelInterface[] $models
267
     */
268 View Code Duplication
    protected function buildDrop(array $models)
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...
269
    {
270
        $fromSchema = $this->connection->getSchemaManager()->createSchema();
271
        $toSchema = clone $fromSchema;
272
273
        foreach ($models as $model) {
274
            if (!$toSchema->hasTable($model->table())) {
275
                continue;
276
            }
277
278
            $toSchema->dropTable($model->table());
279
        }
280
281
        $sql = $fromSchema->getMigrateToSql($toSchema, $this->connection->getDatabasePlatform());
282
283
        $this->queries = array_merge($this->queries, $sql);
284
    }
285
286
    /**
287
     * Executes query
288
     * After execution query is reset
289
     *
290
     * @return mixed|null|void
291
     */
292
    public function execute()
293
    {
294
        $result = [];
295
        foreach ($this->queryString() as $query) {
296
            $stmt = $this->connection->prepare($query);
297
            $stmt->execute();
298
299
            $result[] = $query;
300
        }
301
302
        $this->reset();
303
304
        return $result;
305
    }
306
307
    /**
308
     * Returns array of queries that will be executed
309
     *
310
     * @return array
311
     */
312
    public function queryString()
313
    {
314
        return $this->queries;
315
    }
316
317
    /**
318
     * Resets adapter
319
     *
320
     * @return $this
321
     */
322
    public function reset()
323
    {
324
        $this->queries = [];
325
        $this->createCurrentSchema();
326
327
        return $this;
328
    }
329
}
330