2
0

DatabaseSyncCommand.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. namespace PhpDevCommunity\PaperORM\Command;
  3. use LogicException;
  4. use PhpDevCommunity\Console\Command\CommandInterface;
  5. use PhpDevCommunity\Console\InputInterface;
  6. use PhpDevCommunity\Console\Option\CommandOption;
  7. use PhpDevCommunity\Console\Output\ConsoleOutput;
  8. use PhpDevCommunity\Console\OutputInterface;
  9. use PhpDevCommunity\PaperORM\Collector\EntityDirCollector;
  10. use PhpDevCommunity\PaperORM\Migration\PaperMigration;
  11. use PhpDevCommunity\PaperORM\Tools\EntityExplorer;
  12. class DatabaseSyncCommand implements CommandInterface
  13. {
  14. private PaperMigration $paperMigration;
  15. private EntityDirCollector $entityDirCollector;
  16. private ?string $env;
  17. /**
  18. * @param PaperMigration $paperMigration
  19. * @param EntityDirCollector $entityDirCollector
  20. * @param string|null $env
  21. */
  22. public function __construct(PaperMigration $paperMigration, EntityDirCollector $entityDirCollector, ?string $env = null)
  23. {
  24. $this->paperMigration = $paperMigration;
  25. $this->entityDirCollector = $entityDirCollector;
  26. $this->env = $env;
  27. }
  28. public function getName(): string
  29. {
  30. return 'paper:database:sync';
  31. }
  32. public function getDescription(): string
  33. {
  34. return 'Update the SQL database structure so it matches the current ORM entities.';
  35. }
  36. public function getOptions(): array
  37. {
  38. return [
  39. new CommandOption('no-execute', 'n', 'Show the generated SQL statements without executing them.', true)
  40. ];
  41. }
  42. public function getArguments(): array
  43. {
  44. return [];
  45. }
  46. public function execute(InputInterface $input, OutputInterface $output): void
  47. {
  48. $io = ConsoleOutput::create($output);
  49. $verbose = $input->getOptionValue('verbose');
  50. if (!$this->isEnabled()) {
  51. throw new LogicException('This command is only available in `dev` environment.');
  52. }
  53. if ($this->entityDirCollector->count() === 0) {
  54. $suggested = getcwd() . '/src/Entity';
  55. throw new LogicException(sprintf(
  56. "No entity directories registered in %s.\n" .
  57. "You must register at least one directory when building the application.\n\n" .
  58. "Example:\n" .
  59. " \$collector = new EntityDirCollector(['%s']);\n" .
  60. " \$command = new %s(\$paperMigration, \$collector);",
  61. static::class,
  62. $suggested,
  63. static::class
  64. ));
  65. }
  66. $noExecute = $input->getOptionValue('no-execute');
  67. $platform = $this->paperMigration->getEntityManager()->getPlatform();
  68. $io->title('Starting database sync on ' . $platform->getDatabaseName());
  69. $io->list([
  70. 'Database : ' . $platform->getDatabaseName(),
  71. 'Entities directories : ' . count($this->entityDirCollector->all())
  72. ]);
  73. if ($verbose) {
  74. $io->listKeyValues($this->entityDirCollector->all());
  75. }
  76. $entities = EntityExplorer::getEntities($this->entityDirCollector->all());
  77. $normalEntities = $entities['normal'];
  78. $systemEntities = $entities['system'];
  79. $entities = array_merge($normalEntities, $systemEntities);
  80. $io->title('Detected entities');
  81. $io->list([
  82. 'Normal entities : ' . count($normalEntities),
  83. 'System entities : ' . count($systemEntities),
  84. ]);
  85. if ($verbose) {
  86. $io->listKeyValues($entities);
  87. }
  88. $updates = $this->paperMigration->getSqlDiffFromEntities($entities);
  89. if (empty($updates)) {
  90. $io->info('No differences detected — all entities are already in sync with the database schema.');
  91. return;
  92. }
  93. $count = count($updates);
  94. $io->writeln("📘 Database synchronization plan");
  95. $io->writeln("{$count} SQL statements will be executed:");
  96. $io->writeln("");
  97. $io->numberedList($updates);
  98. if ($noExecute) {
  99. $io->info('Preview mode only — SQL statements were displayed but NOT executed.');
  100. return;
  101. }
  102. $io->writeln("");
  103. $io->writeln("🚀 Applying changes to database...");
  104. $conn = $this->paperMigration->getEntityManager()->getConnection();
  105. foreach ($updates as $sql) {
  106. $conn->executeStatement($sql);
  107. $io->writeln("✔ Executed: {$sql}");
  108. }
  109. $io->success("Database successfully synchronized.");
  110. }
  111. private function isEnabled(): bool
  112. {
  113. return 'dev' === $this->env || 'test' === $this->env;
  114. }
  115. }