Before I get into the story, I gotta be real with you. This is not a framework issue. It’s just a human error, like most things are.
We were using Laravel for a project and three developers were working on the backend Including me. One of them is a junior. I pushed some code and informed my teammate to pull the changes and run migrations in his local system.
I was unaware at that point that he directly connected to the staging environment database from his local system to test one of the issues. I’m not sure why this happened instead of just taking a dump of the DB and using it locally.
But as I asked him to run migration the build on staging is done and migrations have already been completed on staging DB. When he tried to run migration the command line output showed that there’s no new migration to run. He pinged me on Slack saying that he couldn’t run the migrations and that he was going to try running the migration rollback command. Before I could ping him and ask him to check the DB for changes before doing that, He ran the command and we lost the test data!
We were only a few weeks into building this project, and we didn’t have a lot of test data, we could’ve just created all of them from scratch if we had to. But thankfully, we have database backups and we restored the data immediately thus saving us time.
Now, I know this could’ve been prevented if we were a little careful. And avoided this completely if we had proper restrictions for the database. But even if this is your local development environment, It is quite easy to shoot your leg accidentally.
This is not a framework issue. I have seen this issue when I was working with another team as well. The migration is set up in a way that it’s easy to wipe out all your test data in your system by running the wrong command.
That’s when I started to look for options to save migrations from being executed accidentally. I set up a listener in laravel to block the migration and get confirmation from the user by showing the database host the command about to be executed and the consequences of the command before executing the command. This is how I set this up.
Let’s start with creating a listener to intercept the artisan commands
php artisan make:listener ArtisanListener
Then inside the handler function, I’m just displaying the database host and getting confirmation from the user. You can make this as comprehensive as you want.
if ($event->command !== "migrate:status" && str_contains($event->command, "migrate:")) {
$host = config('database.connections.pgsql.host');
$name = config('database.connections.pgsql.database');
echo "\nYour Database host is: " . $host . "\n";
echo "\nYour Database name is: " . $name . "\n\n";
if (!$this->confirmBeforeMigration()) {
echo "\nMigrations rollback cancelled... \n";
exit;
}
}
This is just an additional step to make sure you don’t accidentally shoot yourself. But if you really really want to, you can.