Failed Conditions
Push — master ( ee4e26...e98654 )
by Marco
13:06
created

MappingDescribeCommand::formatValue()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 9.1582

Importance

Changes 0
Metric Value
cc 9
eloc 15
nc 9
nop 1
dl 0
loc 31
rs 4.909
c 0
b 0
f 0
ccs 14
cts 16
cp 0.875
crap 9.1582
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Tools\Console\Command;
6
7
use Doctrine\Common\Persistence\Mapping\MappingException;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Doctrine\ORM\Mapping\AssociationMetadata;
10
use Doctrine\ORM\Mapping\ClassMetadata;
11
use Doctrine\ORM\Mapping\ColumnMetadata;
12
use Doctrine\ORM\Mapping\ComponentMetadata;
13
use Doctrine\ORM\Mapping\FieldMetadata;
14
use Doctrine\ORM\Mapping\Property;
15
use Doctrine\ORM\Mapping\TableMetadata;
16
use Symfony\Component\Console\Command\Command;
17
use Symfony\Component\Console\Input\InputArgument;
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Output\OutputInterface;
20
use Symfony\Component\Console\Style\SymfonyStyle;
21
22
/**
23
 * Show information about mapped entities.
24
 */
25
final class MappingDescribeCommand extends Command
26
{
27
    /**
28
     * {@inheritdoc}
29
     */
30 3
    protected function configure()
31
    {
32 3
        $this->setName('orm:mapping:describe')
33 3
             ->addArgument('entityName', InputArgument::REQUIRED, 'Full or partial name of entity')
34 3
             ->setDescription('Display information about mapped objects')
35 3
             ->setHelp(<<<'EOT'
36 3
The %command.full_name% command describes the metadata for the given full or partial entity class name.
37
38
    <info>%command.full_name%</info> My\Namespace\Entity\MyEntity
39
40
Or:
41
42
    <info>%command.full_name%</info> MyEntity
43
EOT
44
             );
45 3
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 3
    protected function execute(InputInterface $input, OutputInterface $output)
51
    {
52 3
        $ui = new SymfonyStyle($input, $output);
53
54
        /* @var $entityManager \Doctrine\ORM\EntityManagerInterface */
55 3
        $entityManager = $this->getHelper('em')->getEntityManager();
56
57 3
        $this->displayEntity($input->getArgument('entityName'), $entityManager, $ui);
58
59 1
        return 0;
60
    }
61
62
    /**
63
     * Display all the mapping information for a single Entity.
64
     *
65
     * @param string $entityName Full or partial entity class name
66
     */
67 3
    private function displayEntity($entityName, EntityManagerInterface $entityManager, SymfonyStyle $ui)
68
    {
69 3
        $metadata    = $this->getClassMetadata($entityName, $entityManager);
70 1
        $parentValue = $metadata->getParent() === null ? '<comment>None</comment>' : '';
71
72 1
        $ui->table(
73 1
            ['Field', 'Value'],
74 1
            array_merge(
75
                [
76 1
                    $this->formatField('Name', $metadata->getClassName()),
77 1
                    $this->formatField('Root entity name', $metadata->getRootClassName()),
78 1
                    $this->formatField('Custom repository class', $metadata->getCustomRepositoryClassName()),
79 1
                    $this->formatField('Mapped super class?', $metadata->isMappedSuperclass),
80 1
                    $this->formatField('Embedded class?', $metadata->isEmbeddedClass),
81 1
                    $this->formatField('Parent classes', $parentValue),
82
                ],
83 1
                $this->formatParentClasses($metadata),
84
                [
85 1
                    $this->formatField('Sub classes', $metadata->getSubClasses()),
86 1
                    $this->formatField('Embedded classes', $metadata->getSubClasses()),
87 1
                    $this->formatField('Named native queries', $metadata->getNamedNativeQueries()),
88 1
                    $this->formatField('SQL result set mappings', $metadata->getSqlResultSetMappings()),
89 1
                    $this->formatField('Identifier', $metadata->getIdentifier()),
90 1
                    $this->formatField('Inheritance type', $metadata->inheritanceType),
91 1
                    $this->formatField('Discriminator column', ''),
92
                ],
93 1
                $this->formatColumn($metadata->discriminatorColumn),
94
                [
95 1
                    $this->formatField('Discriminator value', $metadata->discriminatorValue),
96 1
                    $this->formatField('Discriminator map', $metadata->discriminatorMap),
97 1
                    $this->formatField('Table', ''),
98
                ],
99 1
                $this->formatTable($metadata->table),
100
                [
101 1
                    $this->formatField('Composite identifier?', $metadata->isIdentifierComposite()),
102 1
                    $this->formatField('Change tracking policy', $metadata->changeTrackingPolicy),
103 1
                    $this->formatField('Versioned?', $metadata->isVersioned()),
104 1
                    $this->formatField('Version field', ($metadata->isVersioned() ? $metadata->versionProperty->getName() : '')),
105 1
                    $this->formatField('Read only?', $metadata->isReadOnly()),
106
107 1
                    $this->formatEntityListeners($metadata->entityListeners),
108
                ],
109 1
                [$this->formatField('Property mappings:', '')],
110 1
                $this->formatPropertyMappings($metadata->getDeclaredPropertiesIterator())
111
            )
112
        );
113 1
    }
114
115
    /**
116
     * Return all mapped entity class names
117
     *
118
     * @return string[]
119
     */
120 3
    private function getMappedEntities(EntityManagerInterface $entityManager)
121
    {
122 3
        $entityClassNames = $entityManager->getConfiguration()
123 3
                                          ->getMetadataDriverImpl()
124 3
                                          ->getAllClassNames();
125
126 3
        if (! $entityClassNames) {
127
            throw new \InvalidArgumentException(
128
                'You do not have any mapped Doctrine ORM entities according to the current configuration. ' .
129
                'If you have entities or mapping files you should check your mapping configuration for errors.'
130
            );
131
        }
132
133 3
        return $entityClassNames;
134
    }
135
136
    /**
137
     * Return the class metadata for the given entity
138
     * name
139
     *
140
     * @param string $entityName Full or partial entity name
141
     *
142
     * @return \Doctrine\ORM\Mapping\ClassMetadata
143
     */
144 3
    private function getClassMetadata($entityName, EntityManagerInterface $entityManager)
145
    {
146
        try {
147 3
            return $entityManager->getClassMetadata($entityName);
148 3
        } catch (MappingException $e) {
149
        }
150
151 3
        $matches = array_filter(
152 3
            $this->getMappedEntities($entityManager),
153 3
            function ($mappedEntity) use ($entityName) {
154 3
                return preg_match('{' . preg_quote($entityName) . '}', $mappedEntity);
155 3
            }
156
        );
157
158 3
        if (! $matches) {
159 1
            throw new \InvalidArgumentException(sprintf(
160 1
                'Could not find any mapped Entity classes matching "%s"',
161 1
                $entityName
162
            ));
163
        }
164
165 2
        if (count($matches) > 1) {
166 1
            throw new \InvalidArgumentException(sprintf(
167 1
                'Entity name "%s" is ambiguous, possible matches: "%s"',
168 1
                $entityName,
169 1
                implode(', ', $matches)
170
            ));
171
        }
172
173 1
        return $entityManager->getClassMetadata(current($matches));
174
    }
175
176
    /**
177
     * @return string[]
178
     */
179 1
    private function formatParentClasses(ComponentMetadata $metadata)
180
    {
181 1
        $output      = [];
182 1
        $parentClass = $metadata;
183
184 1
        while (($parentClass = $parentClass->getParent()) !== null) {
185
            /** @var ClassMetadata $parentClass */
186
            $attributes = [];
187
188
            if ($parentClass->isEmbeddedClass) {
189
                $attributes[] = 'Embedded';
190
            }
191
192
            if ($parentClass->isMappedSuperclass) {
193
                $attributes[] = 'Mapped superclass';
194
            }
195
196
            if ($parentClass->inheritanceType) {
197
                $attributes[] = ucfirst(strtolower($parentClass->inheritanceType));
198
            }
199
200
            if ($parentClass->isReadOnly()) {
201
                $attributes[] = 'Read-only';
202
            }
203
204
            if ($parentClass->isVersioned()) {
205
                $attributes[] = 'Versioned';
206
            }
207
208
            $output[] = $this->formatField(
209
                sprintf('  %s', $parentClass->getParent()),
210
                ($parentClass->isRootEntity() ? '(Root) ' : '') . $this->formatValue($attributes)
211
            );
212
        }
213
214 1
        return $output;
215
    }
216
217
    /**
218
     * Format the given value for console output
219
     *
220
     * @param mixed $value
221
     *
222
     * @return string
223
     */
224 1
    private function formatValue($value)
225
    {
226 1
        if ($value === '') {
227 1
            return '';
228
        }
229
230 1
        if ($value === null) {
231 1
            return '<comment>Null</comment>';
232
        }
233
234 1
        if (is_bool($value)) {
235 1
            return '<comment>' . ($value ? 'True' : 'False') . '</comment>';
236
        }
237
238 1
        if (empty($value)) {
239 1
            return '<comment>Empty</comment>';
240
        }
241
242 1
        if (is_array($value)) {
243 1
            return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
244
        }
245
246 1
        if (is_object($value)) {
247
            return sprintf('<%s>', get_class($value));
248
        }
249
250 1
        if (is_scalar($value)) {
251 1
            return $value;
252
        }
253
254
        throw new \InvalidArgumentException(sprintf('Do not know how to format value "%s"', print_r($value, true)));
255
    }
256
257
    /**
258
     * Add the given label and value to the two column table output
259
     *
260
     * @param string $label Label for the value
261
     * @param mixed  $value A Value to show
262
     *
263
     * @return string[]
264
     */
265 1
    private function formatField($label, $value)
266
    {
267 1
        if ($value === null) {
268 1
            $value = '<comment>None</comment>';
269
        }
270
271 1
        return [sprintf('<info>%s</info>', $label), $this->formatValue($value)];
272
    }
273
274
    /**
275
     * Format the property mappings
276
     *
277
     * @param iterable|Property[] $propertyMappings
278
     *
279
     * @return string[]
280
     */
281 1
    private function formatPropertyMappings(iterable $propertyMappings)
282
    {
283 1
        $output = [];
284
285 1
        foreach ($propertyMappings as $propertyName => $property) {
286 1
            $output[] = $this->formatField(sprintf('  %s', $propertyName), '');
287
288 1
            if ($property instanceof FieldMetadata) {
289 1
                $output = array_merge($output, $this->formatColumn($property));
290 1
            } elseif ($property instanceof AssociationMetadata) {
291
                // @todo guilhermeblanco Fix me! We are trying to iterate through an AssociationMetadata instance
292 1
                foreach ($property as $field => $value) {
293 1
                    $output[] = $this->formatField(sprintf('    %s', $field), $this->formatValue($value));
294
                }
295
            }
296
        }
297
298 1
        return $output;
299
    }
300
301
    /**
302
     * @return string[]
303
     */
304 1
    private function formatColumn(?ColumnMetadata $columnMetadata = null)
305
    {
306 1
        $output = [];
307
308 1
        if ($columnMetadata === null) {
309
            $output[] = '<comment>Null</comment>';
310
311
            return $output;
312
        }
313
314 1
        $output[] = $this->formatField('    type', $this->formatValue($columnMetadata->getTypeName()));
315 1
        $output[] = $this->formatField('    tableName', $this->formatValue($columnMetadata->getTableName()));
316 1
        $output[] = $this->formatField('    columnName', $this->formatValue($columnMetadata->getColumnName()));
317 1
        $output[] = $this->formatField('    columnDefinition', $this->formatValue($columnMetadata->getColumnDefinition()));
318 1
        $output[] = $this->formatField('    isPrimaryKey', $this->formatValue($columnMetadata->isPrimaryKey()));
319 1
        $output[] = $this->formatField('    isNullable', $this->formatValue($columnMetadata->isNullable()));
320 1
        $output[] = $this->formatField('    isUnique', $this->formatValue($columnMetadata->isUnique()));
321 1
        $output[] = $this->formatField('    options', $this->formatValue($columnMetadata->getOptions()));
322
323 1
        if ($columnMetadata instanceof FieldMetadata) {
324 1
            $output[] = $this->formatField('    Generator type', $this->formatValue($columnMetadata->getValueGenerator()->getType()));
325 1
            $output[] = $this->formatField('    Generator definition', $this->formatValue($columnMetadata->getValueGenerator()->getDefinition()));
326
        }
327
328 1
        return $output;
329
    }
330
331
    /**
332
     * Format the entity listeners
333
     *
334
     * @param object[] $entityListeners
335
     *
336
     * @return string
337
     */
338 1
    private function formatEntityListeners(array $entityListeners)
339
    {
340 1
        return $this->formatField('Entity listeners', array_map('get_class', $entityListeners));
341
    }
342
343
    /**
344
     *
345
     * @return string[]
346
     */
347 1
    private function formatTable(?TableMetadata $tableMetadata = null)
348
    {
349 1
        $output = [];
350
351 1
        if ($tableMetadata === null) {
352
            $output[] = '<comment>Null</comment>';
353
354
            return $output;
355
        }
356
357 1
        $output[] = $this->formatField('    schema', $this->formatValue($tableMetadata->getSchema()));
358 1
        $output[] = $this->formatField('    name', $this->formatValue($tableMetadata->getName()));
359 1
        $output[] = $this->formatField('    indexes', $this->formatValue($tableMetadata->getIndexes()));
360 1
        $output[] = $this->formatField('    uniqueConstaints', $this->formatValue($tableMetadata->getUniqueConstraints()));
361 1
        $output[] = $this->formatField('    options', $this->formatValue($tableMetadata->getOptions()));
362
363 1
        return $output;
364
    }
365
}
366