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,25 @@ 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+
79+ To use a specific entity manager (e.g., for multi-DB projects), use the <info>--em</info> option:
80+ <info>%command.full_name% My\Namespace\Entity\MyEntity --em=my_custom_entity_manager</info>
81+
6682EOT);
6783 }
6884
6985 protected function execute (InputInterface $ input , OutputInterface $ output ): int
7086 {
7187 $ ui = new SymfonyStyle ($ input , $ output );
7288
89+ $ format = MappingDescribeCommandFormat::from ($ input ->getOption ('format ' ));
90+
7391 $ entityManager = $ this ->getEntityManager ($ input );
7492
75- $ this ->displayEntity ($ input ->getArgument ('entityName ' ), $ entityManager , $ ui );
93+ $ this ->displayEntity ($ input ->getArgument ('entityName ' ), $ entityManager , $ ui, $ format );
7694
7795 return 0 ;
7896 }
@@ -89,6 +107,10 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
89107
90108 $ suggestions ->suggestValues (array_values ($ entities ));
91109 }
110+
111+ if ($ input ->mustSuggestOptionValuesFor ('format ' )) {
112+ $ suggestions ->suggestValues (array_map (static fn (MappingDescribeCommandFormat $ format ) => $ format ->value , MappingDescribeCommandFormat::cases ()));
113+ }
92114 }
93115
94116 /**
@@ -100,9 +122,47 @@ private function displayEntity(
100122 string $ entityName ,
101123 EntityManagerInterface $ entityManager ,
102124 SymfonyStyle $ ui ,
125+ MappingDescribeCommandFormat $ format ,
103126 ): void {
104127 $ metadata = $ this ->getClassMetadata ($ entityName , $ entityManager );
105128
129+ if ($ format === MappingDescribeCommandFormat::JSON ) {
130+ $ ui ->text (json_encode (
131+ [
132+ 'name ' => $ metadata ->name ,
133+ 'rootEntityName ' => $ metadata ->rootEntityName ,
134+ 'customGeneratorDefinition ' => $ this ->formatValueAsJson ($ metadata ->customGeneratorDefinition ),
135+ 'customRepositoryClassName ' => $ metadata ->customRepositoryClassName ,
136+ 'isMappedSuperclass ' => $ metadata ->isMappedSuperclass ,
137+ 'isEmbeddedClass ' => $ metadata ->isEmbeddedClass ,
138+ 'parentClasses ' => $ metadata ->parentClasses ,
139+ 'subClasses ' => $ metadata ->subClasses ,
140+ 'embeddedClasses ' => $ metadata ->embeddedClasses ,
141+ 'identifier ' => $ metadata ->identifier ,
142+ 'inheritanceType ' => $ metadata ->inheritanceType ,
143+ 'discriminatorColumn ' => $ this ->formatValueAsJson ($ metadata ->discriminatorColumn ),
144+ 'discriminatorValue ' => $ metadata ->discriminatorValue ,
145+ 'discriminatorMap ' => $ metadata ->discriminatorMap ,
146+ 'generatorType ' => $ metadata ->generatorType ,
147+ 'table ' => $ this ->formatValueAsJson ($ metadata ->table ),
148+ 'isIdentifierComposite ' => $ metadata ->isIdentifierComposite ,
149+ 'containsForeignIdentifier ' => $ metadata ->containsForeignIdentifier ,
150+ 'containsEnumIdentifier ' => $ metadata ->containsEnumIdentifier ,
151+ 'sequenceGeneratorDefinition ' => $ this ->formatValueAsJson ($ metadata ->sequenceGeneratorDefinition ),
152+ 'changeTrackingPolicy ' => $ metadata ->changeTrackingPolicy ,
153+ 'isVersioned ' => $ metadata ->isVersioned ,
154+ 'versionField ' => $ metadata ->versionField ,
155+ 'isReadOnly ' => $ metadata ->isReadOnly ,
156+ 'entityListeners ' => $ metadata ->entityListeners ,
157+ 'associationMappings ' => $ this ->formatMappingsAsJson ($ metadata ->associationMappings ),
158+ 'fieldMappings ' => $ this ->formatMappingsAsJson ($ metadata ->fieldMappings ),
159+ ],
160+ JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR ,
161+ ));
162+
163+ return ;
164+ }
165+
106166 $ ui ->table (
107167 ['Field ' , 'Value ' ],
108168 array_merge (
@@ -240,6 +300,22 @@ private function formatValue(mixed $value): string
240300 throw new InvalidArgumentException (sprintf ('Do not know how to format value "%s" ' , print_r ($ value , true )));
241301 }
242302
303+ /** @throws JsonException */
304+ private function formatValueAsJson (mixed $ value ): mixed
305+ {
306+ if (is_object ($ value )) {
307+ $ value = (array ) $ value ;
308+ }
309+
310+ if (is_array ($ value )) {
311+ foreach ($ value as $ k => $ v ) {
312+ $ value [$ k ] = $ this ->formatValueAsJson ($ v );
313+ }
314+ }
315+
316+ return $ value ;
317+ }
318+
243319 /**
244320 * Add the given label and value to the two column table output
245321 *
@@ -281,6 +357,22 @@ private function formatMappings(array $propertyMappings): array
281357 return $ output ;
282358 }
283359
360+ /**
361+ * @param array<string, FieldMapping|AssociationMapping> $propertyMappings
362+ *
363+ * @return array<string, mixed>
364+ */
365+ private function formatMappingsAsJson (array $ propertyMappings ): array
366+ {
367+ $ output = [];
368+
369+ foreach ($ propertyMappings as $ propertyName => $ mapping ) {
370+ $ output [$ propertyName ] = $ this ->formatValueAsJson ((array ) $ mapping );
371+ }
372+
373+ return $ output ;
374+ }
375+
284376 /**
285377 * Format the entity listeners
286378 *
0 commit comments