From 29f299a3aa94536022608fbb3260a11f8a1c04a7 Mon Sep 17 00:00:00 2001 From: mscherer Date: Tue, 26 Nov 2024 12:31:29 +0100 Subject: [PATCH 01/10] Add Migrations middleware --- docs/en/index.rst | 14 +++++ src/Middleware/MigrationsMiddleware.php | 79 +++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/Middleware/MigrationsMiddleware.php diff --git a/docs/en/index.rst b/docs/en/index.rst index 1a92b87d..290d90d3 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1177,6 +1177,20 @@ for instance when deploying on your production environment, by using the bin/cake bake migration_snapshot MyMigration --no-lock +Alert of missing migrations +--------------------------- + +You can use the ``Migrations.Migrations`` middleware in local development +to alert developers about new migrations that are not yet being applied:: + + use Migrations\Middleware\MigrationsMiddleware; + + $middlewareQueue + ... // ErrorHandler middleware + ->add(new MigrationsMiddleware($yourConfig)) + ... // rest + + IDE autocomplete support ------------------------ diff --git a/src/Middleware/MigrationsMiddleware.php b/src/Middleware/MigrationsMiddleware.php new file mode 100644 index 00000000..57d85fd6 --- /dev/null +++ b/src/Middleware/MigrationsMiddleware.php @@ -0,0 +1,79 @@ + [ + 'migrations' => ROOT . DS . 'config' . DS . 'Migrations' . DS, + ], + 'environment' => [ + 'adapter' => null, + 'connection' => 'default', + 'database' => null, + 'migration_table' => 'phinxlog', + ], + ]; + + /** + * @param array $config + */ + public function __construct(array $config = []) + { + $this->setConfig($config); + } + + /** + * Process method. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request. + * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler. + * @throws \Cake\Core\Exception\CakeException + * @return \Psr\Http\Message\ResponseInterface A response. + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + if (!Configure::read('debug')) { + return $handler->handle($request); + } + + if (!$this->hasPendingMigrations()) { + return $handler->handle($request); + } + + throw new CakeException('Pending migrations need to be run: `bin/cake migrations migrate`.', 503); + } + + /** + * @return bool + */ + protected function hasPendingMigrations(): bool + { + $config = new Config($this->_config); + $manager = new Manager($config, new ConsoleIo()); + + $migrations = $manager->getMigrations(); + foreach ($migrations as $migration) { + if (!$manager->isMigrated($migration->getVersion())) { + return true; + } + } + + return false; + } +} From cbc0eab3ae2dca842858df6503b64c17d1a7b7ad Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 29 Nov 2024 16:15:14 +0100 Subject: [PATCH 02/10] Rename and include plugins. --- docs/en/index.rst | 6 +- src/Middleware/MigrationsMiddleware.php | 79 --------- .../PendingMigrationsMiddleware.php | 152 ++++++++++++++++++ 3 files changed, 155 insertions(+), 82 deletions(-) delete mode 100644 src/Middleware/MigrationsMiddleware.php create mode 100644 src/Middleware/PendingMigrationsMiddleware.php diff --git a/docs/en/index.rst b/docs/en/index.rst index 290d90d3..ef2d5dea 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1180,14 +1180,14 @@ for instance when deploying on your production environment, by using the Alert of missing migrations --------------------------- -You can use the ``Migrations.Migrations`` middleware in local development +You can use the ``Migrations.PendingMigrations`` middleware in local development to alert developers about new migrations that are not yet being applied:: - use Migrations\Middleware\MigrationsMiddleware; + use Migrations\Middleware\PendingMigrationsMiddleware; $middlewareQueue ... // ErrorHandler middleware - ->add(new MigrationsMiddleware($yourConfig)) + ->add(new PendingMigrationsMiddleware($yourConfig)) ... // rest diff --git a/src/Middleware/MigrationsMiddleware.php b/src/Middleware/MigrationsMiddleware.php deleted file mode 100644 index 57d85fd6..00000000 --- a/src/Middleware/MigrationsMiddleware.php +++ /dev/null @@ -1,79 +0,0 @@ - [ - 'migrations' => ROOT . DS . 'config' . DS . 'Migrations' . DS, - ], - 'environment' => [ - 'adapter' => null, - 'connection' => 'default', - 'database' => null, - 'migration_table' => 'phinxlog', - ], - ]; - - /** - * @param array $config - */ - public function __construct(array $config = []) - { - $this->setConfig($config); - } - - /** - * Process method. - * - * @param \Psr\Http\Message\ServerRequestInterface $request The request. - * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler. - * @throws \Cake\Core\Exception\CakeException - * @return \Psr\Http\Message\ResponseInterface A response. - */ - public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface - { - if (!Configure::read('debug')) { - return $handler->handle($request); - } - - if (!$this->hasPendingMigrations()) { - return $handler->handle($request); - } - - throw new CakeException('Pending migrations need to be run: `bin/cake migrations migrate`.', 503); - } - - /** - * @return bool - */ - protected function hasPendingMigrations(): bool - { - $config = new Config($this->_config); - $manager = new Manager($config, new ConsoleIo()); - - $migrations = $manager->getMigrations(); - foreach ($migrations as $migration) { - if (!$manager->isMigrated($migration->getVersion())) { - return true; - } - } - - return false; - } -} diff --git a/src/Middleware/PendingMigrationsMiddleware.php b/src/Middleware/PendingMigrationsMiddleware.php new file mode 100644 index 00000000..43350a78 --- /dev/null +++ b/src/Middleware/PendingMigrationsMiddleware.php @@ -0,0 +1,152 @@ + [ + 'migrations' => ROOT . DS . 'config' . DS . 'Migrations' . DS, + ], + 'environment' => [ + 'connection' => 'default', + 'migration_table' => 'phinxlog', + ], + 'plugins' => null, + ]; + + /** + * @param array $config + */ + public function __construct(array $config = []) + { + if (!empty($config['plugins']) && $config['plugins'] === true) { + $config['plugins'] = Plugin::loaded(); + } + + $this->setConfig($config); + } + + /** + * Process method. + * + * @param \Psr\Http\Message\ServerRequestInterface $request The request. + * @param \Psr\Http\Server\RequestHandlerInterface $handler The request handler. + * @throws \Cake\Core\Exception\CakeException + * @return \Psr\Http\Message\ResponseInterface A response. + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + if (!Configure::read('debug')) { + return $handler->handle($request); + } + + $pendingMigrations = $this->pendingMigrations(); + if (!$pendingMigrations) { + return $handler->handle($request); + } + + $message = sprintf('Pending migrations need to be run for %s:', implode(', ', array_keys($pendingMigrations))) . PHP_EOL; + $message .= '`' . implode('`,' . PHP_EOL . '`', $pendingMigrations) . '`'; + + throw new CakeException($message, 503); + } + + /** + * @return array + */ + protected function pendingMigrations(): array + { + $pending = []; + if (!$this->checkAppMigrations()) { + $pending['app'] = 'bin/cake migrations migrate'; + } + + /** @var array $plugins */ + $plugins = (array)$this->_config['plugins']; + foreach ($plugins as $plugin) { + if (!$this->checkPluginMigrations($plugin)) { + $pending[$plugin] = 'bin/cake migrations migrate -p ' . $plugin; + } + } + + return $pending; + } + + /** + * @return bool + */ + protected function checkAppMigrations(): bool + { + $connection = ConnectionManager::get($this->_config['environment']['connection']); + $database = $connection->config()['database']; + $this->_config['environment']['database'] = $database; + + $config = new Config($this->_config); + $manager = new Manager($config, new ConsoleIo()); + + $migrations = $manager->getMigrations(); + foreach ($migrations as $migration) { + if (!$manager->isMigrated($migration->getVersion())) { + return false; + } + } + + return true; + } + + /** + * @param string $plugin + * @return bool + */ + protected function checkPluginMigrations(string $plugin): bool + { + $pluginPath = Plugin::path($plugin); + if (!is_dir($pluginPath . 'config' . DS . 'Migrations' . DS)) { + return true; + } + + $config = [ + 'paths' => [ + 'migrations' => $pluginPath . 'config' . DS . 'Migrations' . DS, + ], + ] + $this->_config; + + //TODO: reuse refactored own method in ManagerFactory etc + $table = 'phinxlog'; + $prefix = Inflector::underscore($plugin) . '_'; + $prefix = str_replace(['\\', '/', '.'], '_', $prefix); + $table = $prefix . $table; + + $config['environment']['migration_table'] = $table; + + $managerConfig = new Config($config); + $manager = new Manager($managerConfig, new ConsoleIo()); + + $migrations = $manager->getMigrations(); + foreach ($migrations as $migration) { + if (!$manager->isMigrated($migration->getVersion())) { + return false; + } + } + + return true; + } +} From 232c1b5c4e9d9b62ba80c41d39e03cb2f2b6cb65 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 2 Dec 2024 04:38:34 +0100 Subject: [PATCH 03/10] Docs --- docs/en/index.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index ef2d5dea..3f5771b4 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1185,9 +1185,15 @@ to alert developers about new migrations that are not yet being applied:: use Migrations\Middleware\PendingMigrationsMiddleware; + $config = [ + 'plugins' => [ + ... // Optionally include a list of plugins with migrations to check. + ], + ]; + $middlewareQueue ... // ErrorHandler middleware - ->add(new PendingMigrationsMiddleware($yourConfig)) + ->add(new PendingMigrationsMiddleware($config)) ... // rest From 35bad47057d2c5d2850f4c184a6f5100973a6ee1 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 2 Dec 2024 04:51:19 +0100 Subject: [PATCH 04/10] Refactor table name code. --- .../PendingMigrationsMiddleware.php | 8 +- src/Migration/ManagerFactory.php | 13 +-- src/Util/Util.php | 83 ++++--------------- 3 files changed, 22 insertions(+), 82 deletions(-) diff --git a/src/Middleware/PendingMigrationsMiddleware.php b/src/Middleware/PendingMigrationsMiddleware.php index 43350a78..9f99ebff 100644 --- a/src/Middleware/PendingMigrationsMiddleware.php +++ b/src/Middleware/PendingMigrationsMiddleware.php @@ -9,9 +9,9 @@ use Cake\Core\InstanceConfigTrait; use Cake\Core\Plugin; use Cake\Datasource\ConnectionManager; -use Cake\Utility\Inflector; use Migrations\Config\Config; use Migrations\Migration\Manager; +use Migrations\Util\Util; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -129,11 +129,7 @@ protected function checkPluginMigrations(string $plugin): bool ], ] + $this->_config; - //TODO: reuse refactored own method in ManagerFactory etc - $table = 'phinxlog'; - $prefix = Inflector::underscore($plugin) . '_'; - $prefix = str_replace(['\\', '/', '.'], '_', $prefix); - $table = $prefix . $table; + $table = Util::tableName($plugin); $config['environment']['migration_table'] = $table; diff --git a/src/Migration/ManagerFactory.php b/src/Migration/ManagerFactory.php index a86c1a2a..b5a42920 100644 --- a/src/Migration/ManagerFactory.php +++ b/src/Migration/ManagerFactory.php @@ -17,9 +17,9 @@ use Cake\Core\Configure; use Cake\Core\Plugin; use Cake\Datasource\ConnectionManager; -use Cake\Utility\Inflector; use Migrations\Config\Config; use Migrations\Config\ConfigInterface; +use Migrations\Util\Util; use RuntimeException; /** @@ -77,19 +77,14 @@ public function createConfig(): ConfigInterface if (defined('CONFIG')) { $dir = CONFIG . $folder; } - $plugin = $this->getOption('plugin'); - if ($plugin && is_string($plugin)) { + $plugin = (string)$this->getOption('plugin') ?: null; + if ($plugin) { $dir = Plugin::path($plugin) . 'config' . DS . $folder; } // Get the phinxlog table name. Plugins have separate migration history. // The names and separate table history is something we could change in the future. - $table = 'phinxlog'; - if ($plugin && is_string($plugin)) { - $prefix = Inflector::underscore($plugin) . '_'; - $prefix = str_replace(['\\', '/', '.'], '_', $prefix); - $table = $prefix . $table; - } + $table = Util::tableName($plugin); $templatePath = dirname(__DIR__) . DS . 'templates' . DS; $connectionName = (string)$this->getOption('connection'); diff --git a/src/Util/Util.php b/src/Util/Util.php index 32612b09..c8a48785 100644 --- a/src/Util/Util.php +++ b/src/Util/Util.php @@ -11,10 +11,7 @@ use Cake\Utility\Inflector; use DateTime; use DateTimeZone; -use Exception; use RuntimeException; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; /** * Temporary compatibility shim that can be refactored away. @@ -46,12 +43,6 @@ class Util */ protected const SEED_FILE_NAME_PATTERN = '/^([a-z][a-z\d]*)\.php$/i'; - /** - * @var string - * @psalm-var non-empty-string - */ - protected const CLASS_NAME_PATTERN = '/^(?:[A-Z][a-z\d]*)+$/'; - /** * Gets the current timestamp string, in UTC. * @@ -153,27 +144,6 @@ public static function mapFileNameToClassName(string $fileName): string return Inflector::camelize($fileName); } - /** - * Check if a migration class name is unique regardless of the - * timestamp. - * - * This method takes a class name and a path to a migrations directory. - * - * Migration class names must be in PascalCase format but consecutive - * capitals are allowed. - * e.g: AddIndexToPostsTable or CustomHTMLTitle. - * - * @param string $className Class Name - * @param string $path Path - * @return bool - */ - public static function isUniqueMigrationClassName(string $className, string $path): bool - { - $existingClassNames = static::getExistingMigrationClassNames($path); - - return !in_array($className, $existingClassNames, true); - } - /** * Check if a migration file name is valid. * @@ -230,43 +200,6 @@ public static function glob(string $path): array return []; } - /** - * Takes the path to a php file and attempts to include it if readable - * - * @param string $filename Filename - * @param \Symfony\Component\Console\Input\InputInterface|null $input Input - * @param \Symfony\Component\Console\Output\OutputInterface|null $output Output - * @param \Phinx\Console\Command\AbstractCommand|mixed|null $context Context - * @throws \Exception - * @return string - */ - public static function loadPhpFile(string $filename, ?InputInterface $input = null, ?OutputInterface $output = null, mixed $context = null): string - { - $filePath = realpath($filename); - if (!$filePath || !file_exists($filePath)) { - throw new Exception(sprintf("File does not exist: %s \n", $filename)); - } - - /** - * I lifed this from phpunits FileLoader class - * - * @see https://github.com/sebastianbergmann/phpunit/pull/2751 - */ - $isReadable = @fopen($filePath, 'r') !== false; - - if (!$isReadable) { - throw new Exception(sprintf("Cannot open file %s \n", $filename)); - } - - // TODO remove $input, $output, and $context from scope - // prevent this to be propagated to the included file - unset($isReadable); - - include_once $filePath; - - return $filePath; - } - /** * Given an array of paths, return all unique PHP files that are in them * @@ -286,4 +219,20 @@ public static function getFiles(string|array $paths): array return $files; } + + /** + * @param string|null $plugin + * @return string + */ + public static function tableName(?string $plugin): string + { + $table = 'phinxlog'; + if ($plugin) { + $prefix = Inflector::underscore($plugin) . '_'; + $prefix = str_replace(['\\', '/', '.'], '_', $prefix); + $table = $prefix . $table; + } + + return $table; + } } From 721089d190ecf9a81670e85b7c1790baa46f3ef3 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 2 Dec 2024 14:45:00 +0100 Subject: [PATCH 05/10] Add test case. --- .../PendingMigrationsMiddlewareTest.php | 148 ++++++++++++++++++ .../test_app/App/Http/TestRequestHandler.php | 26 +++ 2 files changed, 174 insertions(+) create mode 100644 tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php create mode 100644 tests/test_app/App/Http/TestRequestHandler.php diff --git a/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php b/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php new file mode 100644 index 00000000..2cff94df --- /dev/null +++ b/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php @@ -0,0 +1,148 @@ +expectException(CakeException::class); + $this->expectExceptionCode(503); + $this->expectExceptionMessage('Pending migrations need to be run for app:'); + + $middleware->process($request, $handler); + } + + /** + * @doesNotPerformAssertions + * @return void + */ + public function testAppMigrationsSuccess(): void + { + $middleware = new PendingMigrationsMiddleware(); + + $config = [ + 'paths' => [ + 'migrations' => ROOT . DS . 'config' . DS . 'Migrations' . DS, + ], + 'environment' => [ + 'connection' => 'default', + 'migration_table' => 'phinxlog', + ], + ]; + $config = new Config($config); + $manager = new Manager($config, new ConsoleIo()); + $manager->migrate(null, true); + + $request = new ServerRequest(); + $handler = new TestRequestHandler(function ($req) { + return new Response(); + }); + $middleware->process($request, $handler); + } + + /** + * @return void + */ + public function testAppAndPluginsMigrationsFail(): void + { + $this->loadPlugins(['Migrator']); + + $middleware = new PendingMigrationsMiddleware([ + 'plugins' => true, + ]); + + $request = new ServerRequest(); + $handler = new TestRequestHandler(function ($req) { + return new Response(); + }); + + $this->expectException(CakeException::class); + $this->expectExceptionCode(503); + $this->expectExceptionMessage('Pending migrations need to be run for Migrator:'); + + $middleware->process($request, $handler); + } + + /** + * @doesNotPerformAssertions + * @return void + */ + public function testAppAndPluginsMigrationsSuccess(): void + { + $this->loadPlugins(['Migrator']); + + $middleware = new PendingMigrationsMiddleware([ + 'plugins' => true, + ]); + + $config = [ + 'paths' => [ + 'migrations' => ROOT . DS . 'Plugin' . DS . 'Migrator' . DS . 'config' . DS . 'Migrations' . DS, + ], + 'environment' => [ + 'connection' => 'default', + 'migration_table' => 'migrator_phinxlog', + ], + ]; + $config = new Config($config); + $manager = new Manager($config, new ConsoleIo()); + $manager->migrate(null, true); + + $request = new ServerRequest(); + $handler = new TestRequestHandler(function ($req) { + return new Response(); + }); + + $middleware->process($request, $handler); + } +} diff --git a/tests/test_app/App/Http/TestRequestHandler.php b/tests/test_app/App/Http/TestRequestHandler.php new file mode 100644 index 00000000..2b376a4f --- /dev/null +++ b/tests/test_app/App/Http/TestRequestHandler.php @@ -0,0 +1,26 @@ +callable = $callable ?: function ($request) { + return new Response(); + }; + } + + public function handle(ServerRequestInterface $request): ResponseInterface + { + return ($this->callable)($request); + } +} From 60452d3f0af3d2cbcb5f14ba3e06a1f92d765ce9 Mon Sep 17 00:00:00 2001 From: mscherer Date: Wed, 4 Dec 2024 15:59:18 +0100 Subject: [PATCH 06/10] Add test case. --- src/Middleware/PendingMigrationsMiddleware.php | 4 ++++ tests/bootstrap.php | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Middleware/PendingMigrationsMiddleware.php b/src/Middleware/PendingMigrationsMiddleware.php index 9f99ebff..9c2a518b 100644 --- a/src/Middleware/PendingMigrationsMiddleware.php +++ b/src/Middleware/PendingMigrationsMiddleware.php @@ -118,6 +118,10 @@ protected function checkAppMigrations(): bool */ protected function checkPluginMigrations(string $plugin): bool { + $connection = ConnectionManager::get($this->_config['environment']['connection']); + $database = $connection->config()['database']; + $this->_config['environment']['database'] = $database; + $pluginPath = Plugin::path($plugin); if (!is_dir($pluginPath . 'config' . DS . 'Migrations' . DS)) { return true; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a6b64b40..93630647 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -104,19 +104,20 @@ } putenv('DB=' . $db); } + ConnectionManager::setConfig('test', [ 'cacheMetadata' => false, - 'url' => getenv('DB_URL'), + 'url' => getenv('DB_URL') ?: null, ]); ConnectionManager::setConfig('test_snapshot', [ 'cacheMetadata' => false, - 'url' => getenv('DB_URL_SNAPSHOT'), + 'url' => getenv('DB_URL_SNAPSHOT') ?: null, ]); if (getenv('DB_URL_COMPARE') !== false) { ConnectionManager::setConfig('test_comparisons', [ 'cacheMetadata' => false, - 'url' => getenv('DB_URL_COMPARE'), + 'url' => getenv('DB_URL_COMPARE') ?: null, ]); } From 53b969d96bf995aee3b20da8d6e3f12648001da3 Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 6 Dec 2024 02:33:41 +0100 Subject: [PATCH 07/10] Add test case. --- tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php b/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php index 2cff94df..b5e67ee7 100644 --- a/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php +++ b/tests/TestCase/Middleware/PendingMigrationsMiddlewareTest.php @@ -75,6 +75,7 @@ public function testAppMigrationsSuccess(): void 'migrations' => ROOT . DS . 'config' . DS . 'Migrations' . DS, ], 'environment' => [ + 'database' => 'cakephp_test', 'connection' => 'default', 'migration_table' => 'phinxlog', ], @@ -131,6 +132,7 @@ public function testAppAndPluginsMigrationsSuccess(): void ], 'environment' => [ 'connection' => 'default', + 'database' => 'cakephp_test', 'migration_table' => 'migrator_phinxlog', ], ]; From 2f060a445a1f0e710aab8ccfdf5fd8889b9a9f9f Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 6 Dec 2024 14:45:23 +0100 Subject: [PATCH 08/10] Allow skip of app. --- .../PendingMigrationsMiddleware.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Middleware/PendingMigrationsMiddleware.php b/src/Middleware/PendingMigrationsMiddleware.php index 9c2a518b..abdcbc00 100644 --- a/src/Middleware/PendingMigrationsMiddleware.php +++ b/src/Middleware/PendingMigrationsMiddleware.php @@ -9,6 +9,7 @@ use Cake\Core\InstanceConfigTrait; use Cake\Core\Plugin; use Cake\Datasource\ConnectionManager; +use Cake\Utility\Hash; use Migrations\Config\Config; use Migrations\Migration\Manager; use Migrations\Util\Util; @@ -21,6 +22,8 @@ class PendingMigrationsMiddleware implements MiddlewareInterface { use InstanceConfigTrait; + protected const SKIP_QUERY_KEY = 'skip-middleware-check'; + protected array $_defaultConfig = [ 'paths' => [ 'migrations' => ROOT . DS . 'config' . DS . 'Migrations' . DS, @@ -29,6 +32,7 @@ class PendingMigrationsMiddleware implements MiddlewareInterface 'connection' => 'default', 'migration_table' => 'phinxlog', ], + 'app' => null, 'plugins' => null, ]; @@ -54,7 +58,7 @@ public function __construct(array $config = []) */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - if (!Configure::read('debug')) { + if (!Configure::read('debug') || $this->isSkipped($request)) { return $handler->handle($request); } @@ -95,6 +99,10 @@ protected function pendingMigrations(): array */ protected function checkAppMigrations(): bool { + if ($this->_config['app'] === false) { + return true; + } + $connection = ConnectionManager::get($this->_config['environment']['connection']); $database = $connection->config()['database']; $this->_config['environment']['database'] = $database; @@ -149,4 +157,13 @@ protected function checkPluginMigrations(string $plugin): bool return true; } + + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return bool + */ + protected function isSkipped(ServerRequestInterface $request): bool + { + return (bool)Hash::get($request->getQueryParams(), static::SKIP_QUERY_KEY); + } } From f4f381c54b661fb84e7ff114c1aae55017c3940d Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 6 Dec 2024 14:47:39 +0100 Subject: [PATCH 09/10] Docs. --- docs/en/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/index.rst b/docs/en/index.rst index 3f5771b4..4bf31a2a 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1196,6 +1196,10 @@ to alert developers about new migrations that are not yet being applied:: ->add(new PendingMigrationsMiddleware($config)) ... // rest +You can set `app` config to false if you are only interested in plugin migrations to be checked. + +In case you run into the exception and need to skip it for a moment, you can temporarily disable +it using the query string `...?skip-migration-check=1`. IDE autocomplete support ------------------------ From 4f203c2602f5bdec19d9d69de045e91e4510bc88 Mon Sep 17 00:00:00 2001 From: mscherer Date: Fri, 6 Dec 2024 18:13:26 +0100 Subject: [PATCH 10/10] Docs. --- docs/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index 4bf31a2a..c7025475 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1196,7 +1196,7 @@ to alert developers about new migrations that are not yet being applied:: ->add(new PendingMigrationsMiddleware($config)) ... // rest -You can set `app` config to false if you are only interested in plugin migrations to be checked. +You can add `'app'` config key set to `false` if you are only interested in plugin migrations to be checked. In case you run into the exception and need to skip it for a moment, you can temporarily disable it using the query string `...?skip-migration-check=1`.