MapperVisitor::acceptColumn()   F
last analyzed

Complexity

Conditions 18
Paths 3072

Size

Total Lines 87
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 31.8915

Importance

Changes 0
Metric Value
eloc 47
dl 0
loc 87
ccs 26
cts 40
cp 0.65
rs 0.7
c 0
b 0
f 0
cc 18
nc 3072
nop 2
crap 31.8915

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
namespace Bdf\Prime\Schema\Visitor;
4
5
use Bdf\Prime\Mapper\NameResolver\ResolverInterface;
6
use Bdf\Prime\Mapper\NameResolver\SuffixResolver;
7
use Bdf\Prime\Schema\Inflector\InflectorInterface;
8
use Bdf\Prime\Schema\Inflector\SimpleInfector;
9
use Bdf\Prime\Types\TypeInterface;
10
use Doctrine\DBAL\Schema\Column;
11
use Doctrine\DBAL\Schema\Index;
12
use Doctrine\DBAL\Schema\Schema;
13
use Doctrine\DBAL\Schema\Sequence;
14
use Doctrine\DBAL\Schema\Table;
15
use Doctrine\DBAL\Schema\Visitor\AbstractVisitor;
16
17
/**
18
 * Create a mapper output from a Schema.
19
 */
20
class MapperVisitor extends AbstractVisitor
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\DBAL\Schema\Visitor\AbstractVisitor has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

20
class MapperVisitor extends /** @scrutinizer ignore-deprecated */ AbstractVisitor
Loading history...
21
{
22
    /**
23
     * The connection name
24
     *
25
     * @var string|null
26
     */
27
    private $connectionName;
28
29
    /**
30
     * The mappers string representation
31
     *
32
     * @var array<string, array{
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, array{ at position 6 could not be parsed: the token is null at position 6.
Loading history...
33
     *    class:class-string,
34
     *    table:string,
35
     *    primaries:array<string,'autoincrement'|'sequence'|'primary'>,
36
     *    sequence:string|null,
37
     *    properties:array,
38
     *    indexes:array
39
     * }>
40
     */
41
    private $mappers = [];
42
43
    /**
44
     * The doctrine schema
45
     *
46
     * @var Schema
47
     */
48
    private $schema;
49
50
    /**
51
     * The stubs content
52
     *
53
     * @var string
54
     */
55
    private $prototype;
56
57
    /**
58
     * The mapper name resolver
59
     *
60
     * @var ResolverInterface
61
     */
62
    private $nameResolver;
63
64
    /**
65
     * The stubs content
66
     *
67
     * @var InflectorInterface
68
     */
69
    private $inflector;
70
71
    /**
72
     * The mapping between db types and builder methods
73
     *
74
     * @var array
75
     */
76
    private $typeAlias = [
77
        TypeInterface::STRING => 'string',
78
        TypeInterface::TEXT => 'text',
79
        TypeInterface::INTEGER => 'integer',
80
        TypeInterface::BIGINT => 'bigint',
81
        TypeInterface::SMALLINT => 'smallint',
82
        TypeInterface::TINYINT => 'tinyint',
83
        TypeInterface::FLOAT => 'float',
84
        TypeInterface::DOUBLE => 'double',
85
        TypeInterface::DECIMAL => 'decimal',
86
        TypeInterface::BOOLEAN => 'boolean',
87
        TypeInterface::DATE => 'date',
88
        TypeInterface::DATETIME => 'dateTime',
89
        TypeInterface::DATETIMETZ => 'dateTimeTz',
90
        TypeInterface::TIME => 'time',
91
        TypeInterface::TIMESTAMP => 'timestamp',
92
        TypeInterface::BINARY => 'binary',
93
        TypeInterface::BLOB => 'blob',
94
        TypeInterface::GUID => 'guid',
95
        TypeInterface::JSON => 'json',
96
        TypeInterface::TARRAY => 'simpleArray',
97
        TypeInterface::ARRAY_OBJECT => 'arrayObject',
98
        TypeInterface::OBJECT => 'object',
99
    ];
100
101
    /**
102
     * MapperVisitor constructor.
103
     *
104
     * @param string|null $connectionName
105
     * @param ResolverInterface|null $nameResolver
106
     * @param InflectorInterface|null $inflector
107
     */
108 1
    public function __construct($connectionName = null, ResolverInterface $nameResolver = null, InflectorInterface $inflector = null)
109
    {
110 1
        $this->connectionName = $connectionName;
111 1
        $this->nameResolver = $nameResolver ?: new SuffixResolver();
112 1
        $this->inflector = $inflector ?: new SimpleInfector();
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1
    public function acceptSchema(Schema $schema)
119
    {
120 1
        $this->schema = $schema;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 1
    public function acceptTable(Table $table)
127
    {
128 1
        $primaries = [];
129 1
        $sequence = null;
130 1
        $tableName = $table->getName();
131
132
        // Evaluate metadata for primary keys
133 1
        if ($table->hasPrimaryKey()) {
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\Table::hasPrimaryKey() has been deprecated: Use {@see getPrimaryKey()} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

133
        if (/** @scrutinizer ignore-deprecated */ $table->hasPrimaryKey()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
134
            // prepare sequence info for the method Mapper::sequence() and the metadata primary
135 1
            $sequence = $this->inflector->getSequenceName($tableName);
136 1
            if (!$this->schema->hasTable($sequence)) {
137 1
                $sequence = null;
138
            }
139
140
            // get the type of primary
141 1
            foreach ($table->getPrimaryKeyColumns() as $primary) {
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\Table::getPrimaryKeyColumns() has been deprecated: Use {@see getPrimaryKey()} and {@see Index::getColumns()} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

141
            foreach (/** @scrutinizer ignore-deprecated */ $table->getPrimaryKeyColumns() as $primary) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
142 1
                $primary = $primary->getName();
143 1
                $column = $table->getColumn($primary);
144
145 1
                if ($column->getAutoincrement()) {
146 1
                    $primaries[$primary] = 'autoincrement';
147
                } elseif ($sequence !== null && empty($primaries)) {
148
                    $primaries[$primary] = 'sequence';
149
                } else {
150
                    $primaries[$primary] = 'primary';
151
                }
152
            }
153
        }
154
155 1
        $this->mappers[$tableName] = [
156 1
            'class'      => $this->nameResolver->resolve($this->inflector->getClassName($tableName)),
157 1
            'table'      => $tableName,
158 1
            'primaries'  => $primaries,
159 1
            'sequence'   => $sequence,
160 1
            'properties' => [],
161 1
            'indexes'    => [],
162 1
        ];
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 1
    public function acceptColumn(Table $table, Column $column)
169
    {
170 1
        $field = $column->getName();
171 1
        $type = $column->getType()->getName();
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Types\Type::getName() has been deprecated: this method will be removed in Doctrine DBAL 4.0. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

171
        $type = /** @scrutinizer ignore-deprecated */ $column->getType()->getName();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
172 1
        $default = $column->getDefault();
173 1
        $length = $column->getLength();
174 1
        $tableName = $table->getName();
175
176 1
        $property = $this->inflector->getPropertyName($tableName, $field);
177 1
        $primaries = $this->mappers[$tableName]['primaries'];
178
179
        // default
180 1
        if ($default !== null) {
181
            switch ($type) {
182
                case TypeInterface::BOOLEAN:
183
                    $default = (bool)$default;
184
                    break;
185
186
                case TypeInterface::TINYINT:
187
                case TypeInterface::SMALLINT:
188
                case TypeInterface::INTEGER:
189
                    $default = (int)$default;
190
                    break;
191
192
                case TypeInterface::FLOAT:
193
                    $default = (float)$default;
194
                    break;
195
196
                case TypeInterface::DOUBLE:
197
                    $default = (float)$default;
198
                    break;
199
            }
200
201
            $default = ', '.var_export($default, true);
202
        }
203
204
        // type
205 1
        if ($type === TypeInterface::STRING) {
206 1
            $builder = "\$builder->string('$property', {$length}$default)";
207
            // string method has a length property.
208
            // We set the length to null to avoid the call of length() method
209 1
            $length = null;
210 1
        } elseif (isset($this->typeAlias[$type])) {
211 1
            $type = $this->typeAlias[$type];
212 1
            $builder = "\$builder->$type('$property'$default)";
213
        } else {
214
            $builder = "\$builder->add('$property', '$type'$default)";
215
        }
216
217
        // primary
218 1
        if (isset($primaries[$field])) {
219 1
            $builder .= "->{$primaries[$field]}()";
220
        }
221
222
        // TODO unique
223
224
        // length
225 1
        if ($length !== null) {
226
            $builder .= "->length($length)";
227
        }
228
229
        // nillable
230 1
        if (!$column->getNotnull()) {
231 1
            $builder .= "->nillable()";
232
        }
233
234
        // unsigned
235 1
        if ($column->getUnsigned()) {
236
            $builder .= "->unsigned()";
237
        }
238
239
        // precision and scale
240 1
        if ($column->getPrecision() !== 10 || $column->getScale() !== 0) {
241
            $builder .= "->precision({$column->getPrecision()}, {$column->getScale()})";
242
        }
243
244
        // fixed
245 1
        if ($column->getFixed()) {
246
            $builder .= "->fixed()";
247
        }
248
249
        // alias
250 1
        if ($property !== $field) {
251 1
            $builder .= "->alias('$field')";
252
        }
253
254 1
        $this->mappers[$tableName]['properties'][] = $builder.';';
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260 1
    public function acceptIndex(Table $table, Index $index)
261
    {
262 1
        if (!$index->isSimpleIndex()) {
263 1
            return;
264
        }
265
266
        $columns = [];
267
        $tableName = $table->getName();
268
269
        foreach ($index->getColumns() as $column) {
270
            $columns[] = $this->inflector->getPropertyName($tableName, $column);
271
        }
272
273
        $name = $index->getName();
274
        $indexes = implode("', '", $columns);
275
276
        $this->mappers[$tableName]['indexes'][] = "'$name' => ['$indexes'],";
277
    }
278
279
    /**
280
     * {@inheritdoc}
281
     */
282
    public function acceptSequence(Sequence $sequence)
283
    {
284
    }
285
286
    /**
287
     * Get mappers output
288
     *
289
     * @return string
290
     */
291 1
    public function getOutput()
292
    {
293 1
        $output = '';
294
295 1
        foreach ($this->mappers as $metadata) {
296 1
            $output .= $this->createOutput($metadata).PHP_EOL;
297
        }
298
299 1
        return $output;
300
    }
301
302
    /**
303
     * Writes mappers files in path directory
304
     *
305
     * @param string $path
306
     *
307
     * @return void
308
     */
309
    public function write($path): void
310
    {
311
        $path = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
312
313
        foreach ($this->mappers as $metadata) {
314
            file_put_contents($path.$metadata['class'].'.php', $this->createOutput($metadata));
315
        }
316
    }
317
318
    /**
319
     * Create output from table metadata
320
     *
321
     * @param array $metadata
322
     *
323
     * @return string
324
     */
325 1
    private function createOutput($metadata)
326
    {
327 1
        if ($this->prototype === null) {
328 1
            $this->prototype = file_get_contents(__DIR__.'/stubs/mapper.stub');
329
        }
330
331 1
        return strtr($this->prototype, [
332 1
            '<className>'  => $metadata['class'],
333 1
            '<connection>' => $this->connectionName,
334 1
            '<database>'   => $this->connectionName,
335 1
            '<tableName>'  => $metadata['table'],
336 1
            '<fields>'     => implode("\n        ", $metadata['properties']),
337 1
            '<sequence>'   => $this->getSequenceOutput($metadata['sequence']),
338 1
            '<indexes>'    => $this->getIndexOutput($metadata['indexes']),
339 1
        ]);
340
    }
341
342
    /**
343
     * Get the sequence output
344
     *
345
     * @param string $table
346
     *
347
     * @return string
348
     */
349 1
    private function getSequenceOutput($table)
350
    {
351 1
        if ($table === null) {
0 ignored issues
show
introduced by
The condition $table === null is always false.
Loading history...
352 1
            return '';
353
        }
354
355
        return <<<EOF
356
        
357
        
358
    /**
359
     * {@inheritdoc}
360
     */
361
    public function sequence(): array
362
    {
363
        return [
364
            'table' => {$table},
365
        ];
366
    }
367
EOF;
368
    }
369
370
    /**
371
     * Get the index output
372
     *
373
     * @param array $indexes
374
     *
375
     * @return string
376
     */
377 1
    private function getIndexOutput(array $indexes)
378
    {
379 1
        if (empty($indexes)) {
380 1
            return '';
381
        }
382
383
        $indexes = implode("\n            ", $indexes);
384
        return <<<EOF
385
        
386
        
387
    /**
388
     * {@inheritdoc}
389
     */
390
    public function indexes(): array
391
    {
392
        return [
393
            $indexes
394
        ];
395
    }
396
EOF;
397
    }
398
}
399