diff --git a/README.md b/README.md index a1412e4..e92bbf9 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,5 @@ class AddPricingPage extends AbstractMigration { You can run specific migrations using the filename as an argument, eg. `wp dbi migrate AddCustomTable`. To rollback all migrations you can run `wp dbi migrate --rollback`, or just a specific migration `wp dbi migrate AddCustomTable --rollback`. + +To quickly scaffold a new migration you can run `wp scaffold migration `. For example, `wp scaffold migration MyMigration` will create a new class named `MyMigration` in the default migration files directory with the correct filename and all required boilerplate code. diff --git a/src/CLI/Command.php b/src/CLI/Migrate.php similarity index 96% rename from src/CLI/Command.php rename to src/CLI/Migrate.php index f3b3e4b..68da78b 100644 --- a/src/CLI/Command.php +++ b/src/CLI/Migrate.php @@ -2,7 +2,7 @@ namespace DeliciousBrains\WPMigrations\CLI; -class Command extends \WP_CLI_Command { +class Migrate extends \WP_CLI_Command { /** * Data migration command diff --git a/src/CLI/Scaffold.php b/src/CLI/Scaffold.php new file mode 100644 index 0000000..4b15831 --- /dev/null +++ b/src/CLI/Scaffold.php @@ -0,0 +1,50 @@ +] + * : Class name for the migration + * + * [--rollback] + * : If we are reverting a migration + * + * [--setup] + * : Set up the migrations table + * + * [--scaffold] + * : Scaffold a new migration class using the migration stub + * + * @param array $args + * @param array $assoc_args + * + * @throws \WP_CLI\ExitException + */ + public function __invoke( $args, $assoc_args ) { + $migrator = \DeliciousBrains\WPMigrations\Database\Migrator::instance(); + + $migration = null; + if ( ! empty( $args[0] ) ) { + $migration = $args[0]; + } + + + if ( ! $migration ) { + return \WP_CLI::error( 'Migration name must be specified when using --scaffold' ); + } + + $filename = $migrator->scaffold( $migration ); + + if ( is_wp_error( $filename ) ) { + return \WP_ClI::error( $filename->get_error_message() ); + } + + return \WP_CLI::success( "Created {$filename}" ); + } +} diff --git a/src/Database/Migrator.php b/src/Database/Migrator.php index a3a913d..0247bfc 100644 --- a/src/Database/Migrator.php +++ b/src/Database/Migrator.php @@ -2,7 +2,8 @@ namespace DeliciousBrains\WPMigrations\Database; -use DeliciousBrains\WPMigrations\CLI\Command; +use DeliciousBrains\WPMigrations\CLI\Migrate; +use DeliciousBrains\WPMigrations\CLI\Scaffold; class Migrator { @@ -34,7 +35,8 @@ public function init( $command_name ) { require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); if ( defined( 'WP_CLI' ) && WP_CLI ) { - \WP_CLI::add_command( $command_name . ' migrate', Command::class ); + \WP_CLI::add_command( $command_name . ' migrate', Migrate::class ); + \WP_CLI::add_command( 'scaffold migration', Scaffold::class ); } } @@ -79,12 +81,7 @@ public function setup() { protected function get_migrations( $exclude = array(), $migration = null, $rollback = false ) { $all_migrations = array(); - $base_path = __FILE__; - while ( basename( $base_path ) != 'vendor' ) { - $base_path = dirname( $base_path ); - } - - $path = apply_filters( 'dbi_wp_migrations_path', dirname( $base_path ) . '/app/migrations' ); + $path = $this->get_migrations_path(); $paths = apply_filters( 'dbi_wp_migrations_paths', array( $path ) ); $migrations = array(); @@ -119,6 +116,21 @@ protected function get_migrations( $exclude = array(), $migration = null, $rollb return $all_migrations; } + /** + * Get the default migrations folder path. + * + * @return string + */ + protected function get_migrations_path() { + $base_path = __FILE__; + + while ( basename( $base_path ) != 'vendor' ) { + $base_path = dirname( $base_path ); + } + + return apply_filters( 'dbi_wp_migrations_path', dirname( $base_path ) . '/app/migrations' ); + } + /** * Get all the migrations to be run * @@ -204,6 +216,53 @@ protected function camel_case( $string ) { return str_replace( ' ', '', $string ); } + /** + * Scaffold a new migration using the stub from the `stubs` directory. + * + * @param string $migration_name Camel cased migration name, e.g. myMigration. + * + * @return string|WP_Error Name of created migration file on success, WP_Error + * instance on failure. + */ + public function scaffold( $migration_name ) { + $migrations_path = $this->get_migrations_path(); + + // Create migrations dir if it doesn't exist already. + if ( ! is_dir( $migrations_path ) ) { + if ( ! mkdir( $migrations_path, 0755 ) ) { + return new \WP_Error( + 'migrations_folder_error', + "Unable to create migrations folder {$migrations_path}" + ); + } + } + + $stub_dir = dirname( dirname( __DIR__ ) ) . '/stubs'; + $stub_path = apply_filters( 'dbi_migration_stub_path', "{$stub_dir}/migration.stub" ); + $stub = file_get_contents( $stub_path ); + + if ( ! $stub ) { + return new \WP_Error( + 'stub_file_error', + "Unable to create migration file: Couldn't read from stub {$stub_path}." + ); + } + + $date = date( 'Y_m_d' ); + $filename = "{$date}_{$migration_name}.php"; + $file_path = "{$migrations_path}/{$filename}"; + $boilerplate = str_replace( '{{ class }}', $migration_name, $stub ); + + if ( ! file_put_contents( $file_path, $boilerplate ) ) { + return new \WP_Error( + 'file_creation_error', + "Unable to create migration file {$migration_path}." + ); + } + + return $filename; + } + /** * Protected constructor to prevent creating a new instance of the * class via the `new` operator from outside of this class. diff --git a/stubs/migration.stub b/stubs/migration.stub new file mode 100644 index 0000000..b7baed6 --- /dev/null +++ b/stubs/migration.stub @@ -0,0 +1,19 @@ +