-
Notifications
You must be signed in to change notification settings - Fork 11.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[10.x] Improvements for artisan migrate --pretend
command 🚀
#48768
Conversation
This is kind of complicated to solve a specific edge case. Would using another connection work for executing the not pretended queries? I haven‘t checked yet whether all connections are set to pretended mode or only the one for the migration. But I am all for solving problem 2! You‘re correct that the real query should be shown. |
You mean like this? Yes. public function up(): void
{
$foo1 = DB::table('SOME_TABLE')->get();
$firstConnection = DB::ignorePretendModeForCallback(function (){
return DB::connection('mysql')->table('SOME_TABLE')->get();
});
$otherConnection = DB::ignorePretendModeForCallback(function (){
return DB::connection('mysql2')->table('OTHER_TABLE')->get();
});
dump($foo1); // empty collection
dump($firstConnection); // populated collection we can work with
dump($otherConnection); // populated collection we can work with
} Edit 1: Edit 2: However, this is the exact same behaviour as already known from other contexts (e.g. laravel/docs#9059 and #36596). The pretend flag is, in fact, even ignored and statements will be executed if the connection is not the same as set in in the migration property. One should, in general, just not attempt to manipulate or create stuff for different connections in one migration. One migration writes to one connection. However, pulling data in from another connection works just fine. Here is an example: return new class extends Migration {
--> protected $connection = 'mysql2';
public function up(): void
{
--> Schema::connection('mysql2')->create('people', function (Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->timestamp('created_at')->nullable();
});
$otherConnection = DB::ignorePretendModeForCallback(function () {
--> return DB::connection('mysql')->table('websites')->get()->toArray();
});
foreach ($otherConnection as $item) {
// create stuff on the connection defined in this migration
// with data from another connection.
--> Schema::connection('mysql2')->create("websites_{$item->id}_backup", function (Blueprint $table) use ($item)
{
$table->increments('id');
$table->string('name');
$table->string('ref')->default($item->id);
$table->timestamp('created_at')->nullable();
});
}
}
// output
// ⇂ create table `people` (`id` int unsigned not null auto_increment primary key, `name` varchar(255) not null, `created_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci'
// ⇂ create table `websites_12_backup` (`id` int unsigned not null auto_increment primary key, `name` varchar(255) not null, `ref` varchar(255) not null default '12', `created_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci' Not sure if things like this should be tested with additional tests here. Personally, I tend to say it is out of scope of this PR and feature. Edit 3:
Interestingly, you don't need to wrap a query to a different connection into |
artisan migrate --prepend
command 🚀artisan migrate --pretend
command 🚀
With this PR, I propose to add value to the
php artisan migrate --pretend
command byDB::ignorePretendModeForCallback(Closure $callback)
to allow us to execute a given database query closure in migrations, while in pretend mode.Benefits
In the same spirit as my recently accepted PR this PR seeks to allow us to see as clearly as possible what we're dealing with, instead of doing guess work.
Usage
Status Quo
Problem 1
When running
php artisan migrate --pretend
no database query will be executed. As the mode says, they are pretended. This also means that statements that depend on a previous query to the database will not be shown in the output. I just do not know what I am really dealing with because only a part of the actual statements is visible to me. Here is a real life example:Click to expand code
Note: Code simplified
Click to expand output
Note: Output simplified, and already using real values instead of placeholders
The current output in my specific scenario is missing >170 statements like these:
Only with the full context I am able to decide whether I considered all edge cases for every table and column. This, currently, is not possible.
Problem 2
The current output of the migrate pretend command is something like this:
insert into "blogs" ("url") values (?), (?)
.This does not help me to confirm that the data I expect really will be used when running the command in "hot mode". What I expect is to see the exact query that will be run:
insert into "blogs" ("url") values ('www.janedoe.com'), ('www.johndoe.com')
Since @tpetry already did the hard work (🚀❤️) in #47507 we should leverage this functionality in the pretend command as well.
When merged
Before
After
Considerations
1. Bindings
In DB::logQuery() which is responsible for creating the output, I decided to substitute the bindings only while in pretend mode. I would appreciate feedback here.
2. Misuse
Admittedly, people can misuse this and break things. When I started to work on this PR, I tried to allow only read queries. Got complicated, and I skipped. People can also break things with
stevebauman/unfinalize
. I think that is acceptable and everyone should be able to decide for themselves. No nanny needed, though.3. Naming
I first named the method
DB::doNotPretend()
. It's shorter, but I feel likeDB::ignorePretendModeForCallback()
is more clear.4. BC break
Not sure if the binding change in whatever way could be a BC break for people. If that is the case, I would propose to add a config option in 10.x and make it default in 11.