diff --git a/app/src/GraphGenerator/ContextPreparator.php b/app/src/GraphGenerator/ContextPreparator.php
new file mode 100644
index 00000000..67e88e04
--- /dev/null
+++ b/app/src/GraphGenerator/ContextPreparator.php
@@ -0,0 +1,25 @@
+getContext();
+
+ if ('user' !== $context->getName()) {
+ return;
+ }
+
+ $event->getContext()
+ ->setIgnoreAll(true)
+ ->forEntityCluster(User::class)
+ ;
+ }
+}
diff --git a/composer.json b/composer.json
index b099e445..68a37087 100644
--- a/composer.json
+++ b/composer.json
@@ -296,7 +296,8 @@
],
"generate:artifact": [
"Composer\\Config::disableProcessTimeout",
- "bin/console draw:doctrine:generate-graph-schema | dot -Tsvg -o doc/databse.svg"
+ "bin/console draw:doctrine:generate-graph-schema | dot -Tsvg -o doc/database.svg",
+ "bin/console draw:doctrine:generate-graph-schema user | dot -Tsvg -o doc/user.svg"
]
},
"minimum-stability": "dev",
diff --git a/doc/databse.svg b/doc/database.svg
similarity index 100%
rename from doc/databse.svg
rename to doc/database.svg
diff --git a/doc/user.svg b/doc/user.svg
new file mode 100644
index 00000000..ab83d643
--- /dev/null
+++ b/doc/user.svg
@@ -0,0 +1,482 @@
+
+
+
+
+
diff --git a/packages/doctrine-extra/ORM/Command/GenerateGraphSchemaCommand.php b/packages/doctrine-extra/ORM/Command/GenerateGraphSchemaCommand.php
index 9c4ac595..6a89cd41 100644
--- a/packages/doctrine-extra/ORM/Command/GenerateGraphSchemaCommand.php
+++ b/packages/doctrine-extra/ORM/Command/GenerateGraphSchemaCommand.php
@@ -2,11 +2,11 @@
namespace Draw\DoctrineExtra\ORM\Command;
-use Doctrine\DBAL\Schema\Visitor\Graphviz;
use Doctrine\ORM\EntityManagerInterface;
-use Doctrine\ORM\Mapping\ClassMetadata;
-use Doctrine\ORM\Tools\SchemaTool;
+use Draw\DoctrineExtra\ORM\GraphSchema\Context;
+use Draw\DoctrineExtra\ORM\GraphSchema\GraphGenerator;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
@@ -14,8 +14,9 @@
class GenerateGraphSchemaCommand extends Command
{
public function __construct(
- private EntityManagerInterface $entityManager)
- {
+ private EntityManagerInterface $entityManager,
+ private GraphGenerator $graphGenerator,
+ ) {
parent::__construct();
}
@@ -23,6 +24,7 @@ protected function configure(): void
{
$this
->setName('draw:doctrine:generate-graph-schema')
+ ->addArgument('context-name', InputArgument::OPTIONAL, 'The context name to use.', 'default')
->setDescription('Get dot from database schema.')
->setHelp(\sprintf('Usage: bin/console %s | dot -Tsvg -o /tmp/databse.svg', $this->getName()))
;
@@ -31,45 +33,13 @@ protected function configure(): void
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
- $io->writeln($this->getDot());
+ $io->writeln(
+ $this->graphGenerator
+ ->generate(
+ new Context($this->entityManager, $input->getArgument('context-name'))
+ )
+ );
return Command::SUCCESS;
}
-
- /**
- * Get dot from database schema.
- */
- protected function getDot(): string
- {
- /** @var array> $metadata */
- $metadata = $this->entityManager->getMetadataFactory()->getAllMetadata();
-
- usort($metadata, static fn (ClassMetadata $a, ClassMetadata $b): int => $a->getTableName() <=> $b->getTableName());
-
- $tool = new SchemaTool($this->entityManager);
- $schema = $tool->getSchemaFromMetadata($metadata);
-
- $visitor = new Graphviz();
-
- $visitor->acceptSchema($schema);
-
- foreach ($schema->getTables() as $table) {
- $visitor->acceptTable($table);
- foreach ($table->getColumns() as $column) {
- $visitor->acceptColumn($table, $column);
- }
- foreach ($table->getIndexes() as $index) {
- $visitor->acceptIndex($table, $index);
- }
- foreach ($table->getForeignKeys() as $foreignKey) {
- $visitor->acceptForeignKey($table, $foreignKey);
- }
- }
-
- foreach ($schema->getSequences() as $sequence) {
- $visitor->acceptSequence($sequence);
- }
-
- return $visitor->getOutput();
- }
}
diff --git a/packages/doctrine-extra/ORM/GraphSchema/Context.php b/packages/doctrine-extra/ORM/GraphSchema/Context.php
new file mode 100644
index 00000000..65671e91
--- /dev/null
+++ b/packages/doctrine-extra/ORM/GraphSchema/Context.php
@@ -0,0 +1,93 @@
+entityManager;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function setIgnoreAll(bool $ignoreAll): self
+ {
+ $this->ignoreAll = $ignoreAll;
+
+ return $this;
+ }
+
+ public function getIgnoreAll(): bool
+ {
+ return $this->ignoreAll;
+ }
+
+ public function forEntity(string $entity): self
+ {
+ if (!\in_array($entity, $this->ignoreEntities, true)) {
+ $this->forEntities[] = $entity;
+ }
+
+ return $this;
+ }
+
+ public function getForEntities(): array
+ {
+ return $this->forEntities;
+ }
+
+ public function forEntityCluster(string $entity, bool $includeReverseRelation = true): self
+ {
+ $this->forEntities[] = $entity;
+
+ foreach ($this->entityManager->getClassMetadata($entity)->getAssociationMappings() as $associationMapping) {
+ $this->forEntity($associationMapping['targetEntity']);
+ }
+
+ if ($includeReverseRelation) {
+ foreach ($this->entityManager->getMetadataFactory()->getAllMetadata() as $metadata) {
+ foreach ($metadata->getAssociationMappings() as $associationMapping) {
+ if ($associationMapping['targetEntity'] !== $entity) {
+ continue;
+ }
+
+ $this->forEntity($associationMapping['sourceEntity']);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ public function ignoreEntity(string $entity): self
+ {
+ $this->ignoreEntities[] = $entity;
+
+ return $this;
+ }
+
+ public function getIgnoreEntities(): array
+ {
+ return $this->ignoreEntities;
+ }
+}
diff --git a/packages/doctrine-extra/ORM/GraphSchema/Event/PrepareContextEvent.php b/packages/doctrine-extra/ORM/GraphSchema/Event/PrepareContextEvent.php
new file mode 100644
index 00000000..0a5c7eb3
--- /dev/null
+++ b/packages/doctrine-extra/ORM/GraphSchema/Event/PrepareContextEvent.php
@@ -0,0 +1,20 @@
+context;
+ }
+}
diff --git a/packages/doctrine-extra/ORM/GraphSchema/GraphGenerator.php b/packages/doctrine-extra/ORM/GraphSchema/GraphGenerator.php
new file mode 100644
index 00000000..f871e98e
--- /dev/null
+++ b/packages/doctrine-extra/ORM/GraphSchema/GraphGenerator.php
@@ -0,0 +1,105 @@
+eventDispatcher->dispatch(new Event\PrepareContextEvent($context));
+
+ $entityManager = $context->getEntityManager();
+
+ /** @var array> $metadata */
+ $metadata = $entityManager->getMetadataFactory()->getAllMetadata();
+
+ usort($metadata, static fn (ClassMetadata $a, ClassMetadata $b): int => $a->getTableName() <=> $b->getTableName());
+
+ $tool = new SchemaTool($entityManager);
+ $schema = $tool->getSchemaFromMetadata($metadata);
+
+ $visitor = new Graphviz();
+
+ $visitor->acceptSchema($schema);
+
+ $ignoreTables = $this->buildIgnoreTables($context);
+
+ foreach ($schema->getTables() as $table) {
+ if (\in_array($table->getName(), $ignoreTables, true)) {
+ continue;
+ }
+
+ $visitor->acceptTable($table);
+ foreach ($table->getColumns() as $column) {
+ $visitor->acceptColumn($table, $column);
+ }
+ foreach ($table->getIndexes() as $index) {
+ $visitor->acceptIndex($table, $index);
+ }
+ foreach ($table->getForeignKeys() as $foreignKey) {
+ $visitor->acceptForeignKey($table, $foreignKey);
+ }
+ }
+
+ foreach ($schema->getSequences() as $sequence) {
+ $visitor->acceptSequence($sequence);
+ }
+
+ return $visitor->getOutput();
+ }
+
+ private function buildIgnoreTables(Context $context): array
+ {
+ $entityManager = $context->getEntityManager();
+ $ignoreTables = [];
+
+ if ($context->getIgnoreAll()) {
+ foreach ($entityManager->getMetadataFactory()->getAllMetadata() as $metadata) {
+ $ignoreTables[] = $metadata->getTableName();
+ foreach ($metadata->getAssociationMappings() as $associationMapping) {
+ if (!isset($associationMapping['joinTable'])) {
+ continue;
+ }
+
+ $ignoreTables[] = $associationMapping['joinTable']['name'];
+ }
+ }
+ }
+
+ $forEntities = $context->getForEntities();
+ foreach ($forEntities as $entity) {
+ $metadata = $entityManager->getClassMetadata($entity);
+ $ignoreTables = array_diff(
+ $ignoreTables,
+ [$metadata->getTableName()],
+ );
+
+ foreach ($metadata->getAssociationMappings() as $associationMapping) {
+ if (!isset($associationMapping['joinTable'])) {
+ continue;
+ }
+
+ if (!\in_array($associationMapping['targetEntity'], $forEntities, true)) {
+ continue;
+ }
+
+ $ignoreTables = array_diff(
+ $ignoreTables,
+ [$associationMapping['joinTable']['name']],
+ );
+ }
+ }
+
+ return array_values($ignoreTables);
+ }
+}
diff --git a/packages/doctrine-extra/Tests/DependencyInjection/DoctrineExtraIntegrationTest.php b/packages/doctrine-extra/Tests/DependencyInjection/DoctrineExtraIntegrationTest.php
index 10b271ff..222c7eea 100644
--- a/packages/doctrine-extra/Tests/DependencyInjection/DoctrineExtraIntegrationTest.php
+++ b/packages/doctrine-extra/Tests/DependencyInjection/DoctrineExtraIntegrationTest.php
@@ -12,6 +12,7 @@
use Draw\DoctrineExtra\ORM\Command\MysqlDumpCommand;
use Draw\DoctrineExtra\ORM\Command\MysqlImportFileCommand;
use Draw\DoctrineExtra\ORM\EntityHandler;
+use Draw\DoctrineExtra\ORM\GraphSchema\GraphGenerator;
use PHPUnit\Framework\Attributes\CoversClass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -83,6 +84,12 @@ public static function provideTestLoad(): iterable
GenerateGraphSchemaCommand::class,
]
),
+ new ServiceConfiguration(
+ 'draw.doctrine_extra.orm.graph_schema.graph_generator',
+ [
+ GraphGenerator::class,
+ ]
+ ),
],
[
'doctrine' => [
diff --git a/tests/AppKernelTest.php b/tests/AppKernelTest.php
index f397366d..856c6fec 100644
--- a/tests/AppKernelTest.php
+++ b/tests/AppKernelTest.php
@@ -12,10 +12,16 @@ class AppKernelTest extends KernelTestCase
{
use EventDispatcherTesterTrait;
+ private bool $resetFile = false;
+
public function testEventDispatcherConfiguration(): void
{
- $this->assertEventDispatcherConfiguration(
- __DIR__.'/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml'
- );
+ $path = __DIR__.'/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml';
+
+ if ($this->resetFile && file_exists($path)) {
+ unlink($path);
+ }
+
+ $this->assertEventDispatcherConfiguration($path);
}
}
diff --git a/tests/DoctrineExtra/ORM/Command/GenerateGraphSchemaCommandTest.php b/tests/DoctrineExtra/ORM/Command/GenerateGraphSchemaCommandTest.php
index 20b9befe..7437adee 100644
--- a/tests/DoctrineExtra/ORM/Command/GenerateGraphSchemaCommandTest.php
+++ b/tests/DoctrineExtra/ORM/Command/GenerateGraphSchemaCommandTest.php
@@ -9,6 +9,7 @@
use Draw\DoctrineExtra\ORM\Command\GenerateGraphSchemaCommand;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
/**
* @internal
@@ -27,7 +28,11 @@ public function getCommandName(): string
public static function provideTestArgument(): iterable
{
- return [];
+ yield [
+ 'context-name',
+ InputArgument::OPTIONAL,
+ 'default',
+ ];
}
public static function provideTestOption(): iterable
@@ -42,7 +47,7 @@ public static function provideTestOption(): iterable
*/
public function testExecute(): void
{
- $this->execute([])
+ $this->execute(['context-name' => 'user'])
->test(
CommandDataTester::create()
->setExpectedDisplay(null)
diff --git a/tests/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml b/tests/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml
index 3f94383e..bb8dc50e 100644
--- a/tests/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml
+++ b/tests/fixtures/AppKernelTest/testEventDispatcherConfiguration/event_dispatcher.xml
@@ -89,6 +89,9 @@
+
+
+