@@ -213,8 +213,17 @@ public function migrate(int $limit = 0): array
213213 $ newMigrations = array_slice ($ newMigrations , 0 , $ limit );
214214 }
215215
216- // In dry-run or pretend mode, just return the list without executing
217- if ($ this ->dryRun || $ this ->pretend ) {
216+ // In dry-run mode, execute migrations in transaction to collect SQL
217+ if ($ this ->dryRun ) {
218+ $ this ->clearCollectedQueries ();
219+ foreach ($ newMigrations as $ version ) {
220+ $ this ->migrateUp ($ version );
221+ }
222+ return $ newMigrations ;
223+ }
224+
225+ // In pretend mode, just return the list without executing
226+ if ($ this ->pretend ) {
218227 $ this ->clearCollectedQueries ();
219228 $ batch = $ this ->getNextBatchNumber ();
220229
@@ -413,8 +422,32 @@ public function migrateUp(string $version): void
413422 return ; // Already applied
414423 }
415424
416- // In dry-run or pretend mode, just collect info without executing
417- if ($ this ->dryRun || $ this ->pretend ) {
425+ // In dry-run mode, execute migration in transaction and collect SQL
426+ if ($ this ->dryRun ) {
427+ $ this ->collectedQueries [] = "-- Migration: {$ version }" ;
428+ $ this ->collectedQueries [] = '' ;
429+
430+ try {
431+ $ this ->db ->startTransaction ();
432+ $ migration = $ this ->loadMigration ($ version );
433+
434+ // Collect SQL queries during migration execution
435+ $ this ->collectMigrationSql ($ migration , $ version );
436+
437+ $ this ->db ->rollback (); // Always rollback in dry-run mode
438+ } catch (\Throwable $ e ) {
439+ // Rollback if transaction is still active
440+ if ($ this ->db ->connection ->inTransaction ()) {
441+ $ this ->db ->rollback ();
442+ }
443+ // Still collect the error as a comment
444+ $ this ->collectedQueries [] = '-- Error: ' . $ e ->getMessage ();
445+ }
446+ return ;
447+ }
448+
449+ // In pretend mode, just collect info without executing
450+ if ($ this ->pretend ) {
418451 $ batch = $ this ->getNextBatchNumber ();
419452 $ this ->collectedQueries [] = "-- Migration: {$ version }" ;
420453 $ this ->collectedQueries [] = '-- Would execute migration.up() ' ;
@@ -441,6 +474,125 @@ public function migrateUp(string $version): void
441474 }
442475 }
443476
477+ /**
478+ * Collect SQL queries from migration execution.
479+ *
480+ * @param Migration $migration Migration instance
481+ * @param string $version Migration version
482+ */
483+ protected function collectMigrationSql (Migration $ migration , string $ version ): void
484+ {
485+ // Create SQL collector
486+ $ sqlCollector = new SqlQueryCollector ();
487+
488+ // Set up event dispatcher with SQL collector if available
489+ $ originalDispatcher = $ this ->db ->getEventDispatcher ();
490+ $ collectorDispatcher = null ;
491+ $ connection = $ this ->db ->connection ;
492+ $ originalConnectionDispatcher = $ connection ->getEventDispatcher ();
493+
494+ if (interface_exists (\Psr \EventDispatcher \EventDispatcherInterface::class)) {
495+ // Create a simple event dispatcher that collects SQL queries
496+ $ collectorDispatcher = new class ($ sqlCollector ) implements \Psr \EventDispatcher \EventDispatcherInterface {
497+ public function __construct (
498+ private SqlQueryCollector $ collector
499+ ) {
500+ }
501+
502+ public function dispatch (object $ event ): object
503+ {
504+ if ($ event instanceof \tommyknocker \pdodb \events \QueryExecutedEvent) {
505+ $ this ->collector ->handleQueryExecuted ($ event );
506+ }
507+ return $ event ;
508+ }
509+ };
510+
511+ // Temporarily set the collector dispatcher
512+ $ this ->db ->setEventDispatcher ($ collectorDispatcher );
513+ $ connection ->setEventDispatcher ($ collectorDispatcher );
514+ }
515+
516+ try {
517+ // Execute migration - SQL will be collected via event dispatcher
518+ $ migration ->up ();
519+
520+ // Collect SQL from the collector
521+ $ collectedSql = $ sqlCollector ->getQueries ();
522+ if (!empty ($ collectedSql )) {
523+ $ this ->collectedQueries = array_merge ($ this ->collectedQueries , $ collectedSql );
524+ } else {
525+ // Fallback: try to get SQL from connection's lastQuery
526+ $ lastQuery = $ this ->db ->lastQuery ;
527+ if ($ lastQuery !== null && $ lastQuery !== '' ) {
528+ $ this ->collectedQueries [] = $ lastQuery ;
529+ }
530+ }
531+ } catch (\Throwable $ e ) {
532+ // Migration failed, but we still want to show what SQL was attempted
533+ $ this ->collectedQueries [] = '-- Error during migration execution: ' . $ e ->getMessage ();
534+
535+ // Still try to collect any SQL that was executed before the error
536+ $ collectedSql = $ sqlCollector ->getQueries ();
537+ if (!empty ($ collectedSql )) {
538+ $ this ->collectedQueries = array_merge ($ this ->collectedQueries , $ collectedSql );
539+ }
540+ } finally {
541+ // Restore original event dispatcher
542+ if ($ collectorDispatcher !== null ) {
543+ $ this ->db ->setEventDispatcher ($ originalDispatcher );
544+ $ connection ->setEventDispatcher ($ originalConnectionDispatcher );
545+ }
546+ }
547+
548+ // Add migration record SQL
549+ $ batch = $ this ->getNextBatchNumber ();
550+ $ schema = $ this ->db ->schema ();
551+ $ dialect = $ schema ->getDialect ();
552+ [$ insertSql , $ insertParams ] = $ dialect ->buildMigrationInsertSql ($ this ->migrationTable , $ version , $ batch );
553+
554+ // Format SQL with parameters
555+ $ formattedSql = $ this ->formatSqlWithParams ($ insertSql , $ insertParams );
556+ $ this ->collectedQueries [] = '' ;
557+ $ this ->collectedQueries [] = "-- Would record migration in batch {$ batch }" ;
558+ $ this ->collectedQueries [] = $ formattedSql ;
559+ }
560+
561+ /**
562+ * Format SQL query with parameters.
563+ *
564+ * @param string $sql SQL query
565+ * @param array<int|string, mixed> $params Query parameters
566+ *
567+ * @return string Formatted SQL
568+ */
569+ protected function formatSqlWithParams (string $ sql , array $ params ): string
570+ {
571+ if (empty ($ params )) {
572+ return $ sql ;
573+ }
574+
575+ // Simple parameter replacement for display
576+ $ formatted = $ sql ;
577+ foreach ($ params as $ key => $ value ) {
578+ $ placeholder = is_int ($ key ) ? '? ' : ': ' . $ key ;
579+ if (is_string ($ value )) {
580+ $ formattedValue = "' " . addslashes ($ value ) . "' " ;
581+ } elseif (is_int ($ value ) || is_float ($ value )) {
582+ $ formattedValue = (string )$ value ;
583+ } elseif (is_bool ($ value )) {
584+ $ formattedValue = $ value ? '1 ' : '0 ' ;
585+ } elseif ($ value === null ) {
586+ $ formattedValue = 'NULL ' ;
587+ } else {
588+ $ formattedValue = "' " . addslashes ((string )$ value ) . "' " ;
589+ }
590+ $ formatted = str_replace ($ placeholder , $ formattedValue , $ formatted );
591+ }
592+
593+ return $ formatted ;
594+ }
595+
444596 /**
445597 * Load migration class instance.
446598 *
0 commit comments