1010use Doctrine \ORM \Mapping \FieldMapping ;
1111use Doctrine \Persistence \Mapping \MappingException ;
1212use InvalidArgumentException ;
13+ use JsonException ;
1314use Symfony \Component \Console \Completion \CompletionInput ;
1415use Symfony \Component \Console \Completion \CompletionSuggestions ;
1516use Symfony \Component \Console \Input \InputArgument ;
@@ -52,9 +53,17 @@ final class MappingDescribeCommand extends AbstractEntityManagerCommand
5253 protected function configure (): void
5354 {
5455 $ this ->setName ('orm:mapping:describe ' )
55- ->addArgument ('entityName ' , InputArgument::REQUIRED , 'Full or partial name of entity ' )
56- ->setDescription ('Display information about mapped objects ' )
57- ->addOption ('em ' , null , InputOption::VALUE_REQUIRED , 'Name of the entity manager to operate on ' )
56+ ->addArgument ('entityName ' , InputArgument::REQUIRED , 'Full or partial name of entity ' )
57+ ->setDescription ('Display information about mapped objects ' )
58+ ->addOption ('em ' , null , InputOption::VALUE_REQUIRED , 'Name of the entity manager to operate on ' )
59+ ->addOption (
60+ 'format ' ,
61+ null ,
62+ InputOption::VALUE_REQUIRED ,
63+ 'Output format (text, json) ' ,
64+ MappingDescribeCommandFormat::TEXT ->value ,
65+ array_map (static fn (MappingDescribeCommandFormat $ format ) => $ format ->value , MappingDescribeCommandFormat::cases ()),
66+ )
5867 ->setHelp (<<<'EOT'
5968The %command.full_name% command describes the metadata for the given full or partial entity class name.
6069
@@ -63,16 +72,22 @@ protected function configure(): void
6372Or:
6473
6574 <info>%command.full_name%</info> MyEntity
75+
76+ To output the metadata in JSON format, use the <info>--format</info> option:
77+ <info>%command.full_name% My\Namespace\Entity\MyEntity --format=json</info>
78+
6679EOT);
6780 }
6881
6982 protected function execute (InputInterface $ input , OutputInterface $ output ): int
7083 {
7184 $ ui = new SymfonyStyle ($ input , $ output );
7285
86+ $ format = MappingDescribeCommandFormat::from ($ input ->getOption ('format ' ));
87+
7388 $ entityManager = $ this ->getEntityManager ($ input );
7489
75- $ this ->displayEntity ($ input ->getArgument ('entityName ' ), $ entityManager , $ ui );
90+ $ this ->displayEntity ($ input ->getArgument ('entityName ' ), $ entityManager , $ ui, $ format );
7691
7792 return 0 ;
7893 }
@@ -89,6 +104,10 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
89104
90105 $ suggestions ->suggestValues (array_values ($ entities ));
91106 }
107+
108+ if ($ input ->mustSuggestOptionValuesFor ('format ' )) {
109+ $ suggestions ->suggestValues (array_map (static fn (MappingDescribeCommandFormat $ format ) => $ format ->value , MappingDescribeCommandFormat::cases ()));
110+ }
92111 }
93112
94113 /**
@@ -100,9 +119,47 @@ private function displayEntity(
100119 string $ entityName ,
101120 EntityManagerInterface $ entityManager ,
102121 SymfonyStyle $ ui ,
122+ MappingDescribeCommandFormat $ format ,
103123 ): void {
104124 $ metadata = $ this ->getClassMetadata ($ entityName , $ entityManager );
105125
126+ if ($ format === MappingDescribeCommandFormat::JSON ) {
127+ $ ui ->text (json_encode (
128+ [
129+ 'name ' => $ metadata ->name ,
130+ 'rootEntityName ' => $ metadata ->rootEntityName ,
131+ 'customGeneratorDefinition ' => $ this ->formatValueAsJson ($ metadata ->customGeneratorDefinition ),
132+ 'customRepositoryClassName ' => $ metadata ->customRepositoryClassName ,
133+ 'isMappedSuperclass ' => $ metadata ->isMappedSuperclass ,
134+ 'isEmbeddedClass ' => $ metadata ->isEmbeddedClass ,
135+ 'parentClasses ' => $ metadata ->parentClasses ,
136+ 'subClasses ' => $ metadata ->subClasses ,
137+ 'embeddedClasses ' => $ metadata ->embeddedClasses ,
138+ 'identifier ' => $ metadata ->identifier ,
139+ 'inheritanceType ' => $ metadata ->inheritanceType ,
140+ 'discriminatorColumn ' => $ this ->formatValueAsJson ($ metadata ->discriminatorColumn ),
141+ 'discriminatorValue ' => $ metadata ->discriminatorValue ,
142+ 'discriminatorMap ' => $ metadata ->discriminatorMap ,
143+ 'generatorType ' => $ metadata ->generatorType ,
144+ 'table ' => $ this ->formatValueAsJson ($ metadata ->table ),
145+ 'isIdentifierComposite ' => $ metadata ->isIdentifierComposite ,
146+ 'containsForeignIdentifier ' => $ metadata ->containsForeignIdentifier ,
147+ 'containsEnumIdentifier ' => $ metadata ->containsEnumIdentifier ,
148+ 'sequenceGeneratorDefinition ' => $ this ->formatValueAsJson ($ metadata ->sequenceGeneratorDefinition ),
149+ 'changeTrackingPolicy ' => $ metadata ->changeTrackingPolicy ,
150+ 'isVersioned ' => $ metadata ->isVersioned ,
151+ 'versionField ' => $ metadata ->versionField ,
152+ 'isReadOnly ' => $ metadata ->isReadOnly ,
153+ 'entityListeners ' => $ metadata ->entityListeners ,
154+ 'associationMappings ' => $ this ->formatMappingsAsJson ($ metadata ->associationMappings ),
155+ 'fieldMappings ' => $ this ->formatMappingsAsJson ($ metadata ->fieldMappings ),
156+ ],
157+ JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR ,
158+ ));
159+
160+ return ;
161+ }
162+
106163 $ ui ->table (
107164 ['Field ' , 'Value ' ],
108165 array_merge (
@@ -240,6 +297,22 @@ private function formatValue(mixed $value): string
240297 throw new InvalidArgumentException (sprintf ('Do not know how to format value "%s" ' , print_r ($ value , true )));
241298 }
242299
300+ /** @throws JsonException */
301+ private function formatValueAsJson (mixed $ value ): mixed
302+ {
303+ if (is_object ($ value )) {
304+ $ value = (array ) $ value ;
305+ }
306+
307+ if (is_array ($ value )) {
308+ foreach ($ value as $ k => $ v ) {
309+ $ value [$ k ] = $ this ->formatValueAsJson ($ v );
310+ }
311+ }
312+
313+ return $ value ;
314+ }
315+
243316 /**
244317 * Add the given label and value to the two column table output
245318 *
@@ -281,6 +354,22 @@ private function formatMappings(array $propertyMappings): array
281354 return $ output ;
282355 }
283356
357+ /**
358+ * @param array<string, FieldMapping|AssociationMapping> $propertyMappings
359+ *
360+ * @return array<string, mixed>
361+ */
362+ private function formatMappingsAsJson (array $ propertyMappings ): array
363+ {
364+ $ output = [];
365+
366+ foreach ($ propertyMappings as $ propertyName => $ mapping ) {
367+ $ output [$ propertyName ] = $ this ->formatValueAsJson ((array ) $ mapping );
368+ }
369+
370+ return $ output ;
371+ }
372+
284373 /**
285374 * Format the entity listeners
286375 *
0 commit comments