How to run laravel migration and DB seeder except one
Laravel doesn't give you a default method to do it. However, you can create your own console commands and seeder to achieve it.
Let's say you have this default DatabaseSeeder
class:
class DatabaseSeeder extends Seeder{ public function run() { $this->call(ExampleTableSeeder::class); $this->call(UserSamplesTableSeeder::class); }}
the goal is to create a new command overriding "db:seed" and pass a new parameter, an "except" parameter, to the DatabaseSeeder
class.
This is the final code, I created on my Laravel 5.2 instance and tried:
Command, put in app/Console/Commands, don't forget to update your Kernel.php:
namespace App\Console\Commands;use Illuminate\Console\Command;class SeedExcept extends Command{ protected $signature = 'db:seed-except {--except=class name to jump}'; protected $description = 'Seed all except one'; public function handle() { $except = $this->option('except'); $seeder = new \DatabaseSeeder($except); $seeder->run(); }}
DatabaseSeeder
use Illuminate\Database\Seeder;class DatabaseSeeder extends Seeder{ protected $except; public function __construct($except = null) { $this->except = $except; } public function call($class) { if ($class != $this->except) { echo "calling $class \n"; //parent::call($class); // uncomment this to execute after tests } } public function run() { $this->call(ExampleTableSeeder::class); $this->call(UserSamplesTableSeeder::class); }}
It the code, you'll find that I commented the line that calls the seed and added an echo for testing purposes.
Executing this command:
php artisan db:seed-except
will give you:
calling ExampleTableSeeder
calling UserSamplesTableSeeder
However, adding "except":
php artisan db:seed-except --except=ExampleTableSeeder
will give you
calling UserSamplesTableSeeder
This works overriding the default call
method of your DatabaseSeeder
class and calling the parent only if the name of the class is not in the $except variable. The variable is populated by the SeedExcept
custom command.
Regarding migrations, the thing is similar but a little bit more difficult.
I can't give you tested code for this by now, but the thing is:
- you create a
migrate-except
command that overrides theMigrateCommand
class (namespace Illuminate\Database\Console\Migrations, located in vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php). - the
MigrateCommand
takes aMigrator
object (namespace Illuminate\Database\Migrations, path vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php) in the constructor (injected via IoC). TheMigrator
class owns the logic that reads all the migrations inside the folder and execute it. This logic is inside therun()
method - create a subclass of
Migrator
, for exampleMyMigrator
, and override therun()
method to skip the files passed with the special option - override the
__construct()
method of yourMigrateExceptCommand
and pass yourMyMigrator
:public function __construct(MyMigrator $migrator)
If I have time I'll add the code for an example before the bounty ends
EDITas promised, here's an example for migrations:
MyMigrator class, extends Migrator and contains the logic to skip files:
namespace App\Helpers;use Illuminate\Database\Migrations\Migrator;class MyMigrator extends Migrator{ public $except = null; // run() method copied from it's superclass adding the skip logic public function run($path, array $options = []) { $this->notes = []; $files = $this->getMigrationFiles($path); // skip logic // remove file from array if (isset($this->except)) { $index = array_search($this->except,$files); if($index !== FALSE){ unset($files[$index]); } } var_dump($files); // debug $ran = $this->repository->getRan(); $migrations = array_diff($files, $ran); $this->requireFiles($path, $migrations); //$this->runMigrationList($migrations, $options); // commented for debugging purposes }}
The MigrateExcept custom command
namespace App\Console\Commands;use Illuminate\Console\Command;use Illuminate\Database\Console\Migrations\MigrateCommand;use App\Helpers\MyMigrator;use Illuminate\Database\Migrations\Migrator;use Symfony\Component\Console\Input\InputOption;class MigrateExcept extends MigrateCommand{ protected $name = 'migrate-except'; public function __construct(MyMigrator $migrator) { parent::__construct($migrator); } public function fire() { // set the "except" param, containing the name of the file to skip, on our custom migrator $this->migrator->except = $this->option('except'); parent::fire(); } // add the 'except' option to the command protected function getOptions() { return [ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'], ['path', null, InputOption::VALUE_OPTIONAL, 'The path of migrations files to be executed.'], ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'], ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run.'], ['step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually.'], ['except', null, InputOption::VALUE_OPTIONAL, 'Files to jump'], ]; }}
Last, you need to add this to a service provider to permit the Laravel IoC resolve the dependencies
namespace App\Providers;use App\Helpers\MyMigrator;use App\Console\Commands\MigrateExcept;class CustomServiceProvider extends ServiceProvider{ public function boot() { parent::boot($events); $this->app->bind('Illuminate\Database\Migrations\MigrationRepositoryInterface', 'migration.repository'); $this->app->bind('Illuminate\Database\ConnectionResolverInterface', 'Illuminate\Database\DatabaseManager'); $this->app->singleton('MyMigrator', function ($app) { $repository = $app['migration.repository']; return new MyMigrator($repository, $app['db'], $app['files']); }); }}
Don't forget to add Commands\MigrateExcept::class
in the Kernel.php
Now, if you execute
php artisan migrate-except
you have:
array(70) { [0] => string(43) "2014_04_24_110151_create_oauth_scopes_table" [1] => string(43) "2014_04_24_110304_create_oauth_grants_table" [2] => string(49) "2014_04_24_110403_create_oauth_grant_scopes_table" ...
but adding the except param:
php artisan migrate-except --except=2014_04_24_110151_create_oauth_scopes_table
array(69) { [1] => string(43) "2014_04_24_110304_create_oauth_grants_table" [2] => string(49) "2014_04_24_110403_create_oauth_grant_scopes_table"
So, recap:
- we create a custom migrate-except command,
MigrateExcept
class, extending MigrateCommand - we create a custom migrator class,
MyMigrator
, extending the behavior of the standardMigrator
- when MigrateExcept is fire(), pass the name of the file to skip to our
MyMigrator
class MyMigrator
overrides therun()
method ofMigrator
and skip the passed migration- More: since we need to instruct Laravel IoC about the new created classes, so it can inject them correctly, we create a Service Provider
The code is tested so it should work correctly on Laravel 5.2 (hoping that cut&paste worked correctly :-) ...if anyone has any doubt leave a comment
Skipping seeds are very simple, migrations not so much. To skip a seed, remove the following from your DatabaseSeeder class.
$this->call(TableYouDontWantToSeed::class);
For migrations, There are three ways you can do it:
- Put the class you don't want to migrate into a different folder.
- Insert your migrations into the database manually (Bindesh Pandya's answer elaborated).
- Rename the file that you don't want to migrate to something like
UsersTableMigration.dud
.
Hope this helps
I also faced the same problem in my project but after long time wasting in R & D i have found that Laravel does not provide any way to do this with migration and seeding but you have 2 ways to do this.
1) you'll save a lot of time just putting them into different folders. You could theoretically make your own artisan command that does what you want, or spoofs its by making directories, moving files, and running php artisan migrate.
For the seeders, just make a seeder and call the others seeders you want to run from with in it. Then just be explicit about what seeder you want to run. Try php artisan db:seed --help
for more details there.
2) you can create a table Manually (which has same name as migration table is creating in you db) and insert the values of migration like this
insert into migrations(migration, batch) values('2015_12_08_134409_create_tables_script',1);
so migrate command will not create table which is already exist in migration table.