diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96518ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Jetbrains +.idea/* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..063e13d --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Check Migrations + +A Laravel command scans your application for pending migrations, providing a clear overview of what needs to be migrated. +You can then choose to run individual migrations or skip them as needed, short-cutting and optimizing your development workflow. + +## Support for Structured Migrations + +This package seamlessly handles migrations organized within subdirectories, ensuring that no pending migrations are overlooked regardless of the project's migration structure. + +```php +database +└── migrations +├── 2023_01_01 +│ ├── 20230101000001_create_table_one.php +│ └── 20230101000002_create_table_two.php +└── 2023_02_01 +├── 20230201000001_create_table_three.php +└── 20230201000002_create_table_four.php + +``` +## Installation + +```bash +composer require levizoesch/laravel-check-migrations +``` + +## Usage + +Run the following command in your terminal: + +#### This will display pending migrations and prompt for confirmation before running each one. +```bash +php artisan check-migrations +``` +#### This will skip confirmation and run pending migrations directly. +```bash +php artisan check-migrations --skip +``` +#### This will ignore the migration named 20230101000000_create_example_table from running and prompting for confirmation. +```bash +php artisan check-migrations --ignore=20230101000000_create_example_table +``` + +### Notes + +Ensure that your Laravel project is properly configured and migrations are set up correctly for this command to work effectively. + +Always review pending migrations before running them in production environments to prevent unintended consequences. \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..38d29fc --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "levizoesch/laravel-check-migrations", + "type": "project", + "license": "MIT", + "description": "A Laravel command scans your application for pending migrations, providing a clear overview of what needs to be migrated.\nYou can then choose to run individual migrations or skip them as needed, short-cutting and optimizing your development workflow.", + "keywords": [ + "Levi Zoesch", + "laravel", + "laravel commands", + "laravel helpers", + "Laravel Check Migrations" + ], + "require": { + "php": "^7.0|^8.0" + }, + "autoload": { + "psr-4" : { + "levizoesch\\checkmigrations\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "levizoesch\\checkmigrations\\CheckMigrationsServiceProvider" + ] + } + } +} diff --git a/src/CheckMigrations.php b/src/CheckMigrations.php new file mode 100644 index 0000000..d5d009f --- /dev/null +++ b/src/CheckMigrations.php @@ -0,0 +1,123 @@ +showPendingMigrations(); + } + + // Function to display pending migrations and optionally run them + private function showPendingMigrations(): void + { + $pendingMigrations = $this->getPendingMigrations(); + + foreach ($pendingMigrations as $migrationName) { + $this->info("Pending migration found: $migrationName"); + + if ($this->shouldRunMigration($migrationName)) { + $this->runMigration($migrationName); + } else { + $this->comment("The migration $migrationName was not run."); + $this->line(""); + } + } + } + + // Function to retrieve a list of pending migrations + private function getPendingMigrations(): array + { + Artisan::call('migrate:status'); + + $output = trim(Artisan::output()); + $lines = explode(PHP_EOL, $output); + $pendingMigrations = []; + + foreach ($lines as $line) { + if (str_contains($line, 'Pending')) { + preg_match('/(\d{4}_\d{2}_\d{2}_\d{6}_\w+)/', $line, $matches); + + if (!empty($matches[1])) { + $pendingMigrations[] = $matches[1]; + } else { + $this->error("Unable to extract migration name from line: $line"); + } + } + } + + return $pendingMigrations; + } + + // Function to determine whether to run a migration based on user confirmation or pipeline option + private function shouldRunMigration($migrationName): bool + { + $ignoreMigration = $this->option('ignore'); + + if ($ignoreMigration && $ignoreMigration === $migrationName) { + return false; + } + + if ($this->option('skip')) { + return false; + } + + return $this->confirm("Are you sure you want to run the migration $migrationName?"); + } + + // Function to run a migration + private function runMigration($migrationName): void + { + $migrationPath = $this->findMigrationPath($migrationName); + + if ($migrationPath) { + Artisan::call('migrate', ['--path' => $migrationPath]); + $this->line(Artisan::output()); + } else { + $this->error("Migration file not found for $migrationName"); + } + } + + // Function to find the path of a migration file + private function findMigrationPath($migrationName) + { + $migrationFiles = $this->getMigrationFiles('database/migrations'); + + foreach ($migrationFiles as $file) { + if (strpos($file, $migrationName) !== false) { + return $file; + } + } + + return null; + } + + // Function to retrieve migration files within a directory + private function getMigrationFiles($directory): array + { + $files = []; + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)); + + foreach ($iterator as $file) { + if ($file->isFile() && $file->getExtension() === 'php') { + $files[] = $file->getPathname(); + } + } + + return $files; + } +} \ No newline at end of file diff --git a/src/CheckMigrationsServiceProvider.php b/src/CheckMigrationsServiceProvider.php new file mode 100644 index 0000000..c61f45a --- /dev/null +++ b/src/CheckMigrationsServiceProvider.php @@ -0,0 +1,16 @@ +commands([ + CheckMigrations::class + ]); + } +}