0

I'm trying to setup a workflow that does the following (must work in CI, ie. nothing exists before it runs):

  • create a new DB with random name
  • install new instance of my Laravel app to new random database name
  • run my tests
  • still have the test DB around to investigate issue after tests complete
  • not have to juggle .env files or edit them in any way

EDIT: It's been suggested to let Laravel Test handle all this because it will wipe then migrate your DB but if I'm understanding correctly, this assumes that the DB already exists, ie. I have to manually create the DB, set it in .env THEN run my script to install the app and run tests. That is not the desired end goal/workflow.

I've got an Artisan command (gist here, cred to this post for a bunch of the code) that seemingly should do all of the above, except running the tests (can't get that far). You can see in the gist or below the numerous ways I've tried telling the command what database to connect to:

// $_ENV['DB_DATABASE'] = $dbname;
// $this->getLaravel()->environment(['DB_DATABASE' => $dbname]);
// $this->info('env: ' . $this->getLaravel()->environment('DB_DATABASE'));
// \Illuminate\Support\Env::enablePutenv();
// putenv('DB_DATABASE=' . $dbname);
// $this->info('env: ' . json_encode($_ENV));
// $this->call('config:clear');
config(['database.connections.' . $connection . '.database' => $dbname]);

What I'm finding is that every time I run the script, no matter how I try to tell the environment what DB to use, it will try to install overtop of my existing dev environment which is defined in my .env file. I want php artisan install --ready to install into the new randomly named DB and for the tests to subsequently run using that DB.

What I'm finding though is that there isn't a way to dynamically tell Laravel or PHPUnit what database to use so this seems to a pointless endeavor. But it's such a common use case. How do Laravel devs handle this normally? A Node script that sets up the DB then copies/edits the .env and phpunit.xml files? Do it all in a CI env? I feel like I'm missing something.

EDIT: I've updated the gist to include the recommended change from Yahya's answer below which gets things working as desired, running DB:reconnect($connection) after changing the config().

th3coop
  • 411
  • 4
  • 11
  • 1
    What are you trying to achieve? Why use a random database name? laravel already has all the features for [testing](https://laravel.com/docs/8.x/testing) and [database testing](https://laravel.com/docs/8.x/database-testing) that take care of database creation and stuff – brombeer Apr 16 '21 at 05:05
  • "*install new instance of my Laravel app to new random database name*" - do you mean install Laravel, and configure it to use your new random DB? Why not just have the script update your `.env` on disk? – Don't Panic Apr 16 '21 at 08:34
  • Do you want this to be a persistent change? e.g. Once you created your new database, it makes a change to your config. Or do you want it to dynamically change the database based on runtime. – Yahya Uddin Apr 16 '21 at 14:40
  • Thanks for all the quick respones! @brombeer, are you referring to the [Run Tests In Parallel](https://laravel.com/docs/8.x/testing#running-tests-in-parallel) section? That sounds like it copies an existing database to be use for each running instance of the tests which wouldn't work in my case. – th3coop Apr 16 '21 at 15:27
  • @Don'tPanic, added some clarifying details. I meant `php artisan install --ready` to a randomly created DB. I could copy my .env file, move the dev one aside, edit the test .env file to point at the new db, then run test, then cleanup but that seems like it will very quickly bite me. Is there no way to tell `php artisan` what environmental variables to use other than the *.env* file? – th3coop Apr 16 '21 at 15:29
  • @YahyaUddin, I added some hopefully clarifying details about what I'm trying to achieve. Added more bullet points to my use case and an additional paragraph below my code snippet. I think you second note is closest to what I want to do. What I ideally want is what `php artisan migrate` has with the `--database` option and the same for when I run PHPUnit. I need a way to, at the time that it gets run, tell the script which database to use when it runs, dynamically. – th3coop Apr 16 '21 at 15:42
  • Ps. that "Thank you" goes for all of you. Brombeer doesn't get all the glory :P – th3coop Apr 16 '21 at 15:48

1 Answers1

1

If you want to change a config during runtime, use:

config(["database.connections.{$connection}.database" => $dbname]);

However, as you are changing a pre-existing connection, the DatabaseManager may have already created a connection using the old config. Therefore you may need to run the following:

DB::reconnect($connection);

This would not be needed if it is a fresh new connection that was not used. i.e.

config(["database.connections.{$connection}' => $connectionConfig]);

If you want to choose which database connection to use just do:

DB::connection($connectionName)->where('what_ever', 20)->...->get()

Alternatively change your default connection like so:

DB::setDefaultConnection($connectionName);
DB::where('whaterver', 20)->...->get()

The reason the following does not work is:

// You shouldn't be changing env variables during runtime, especially if config is cached
$_ENV['DB_DATABASE'] = $dbname; 

// this function checks what environment your in. Correct usage $this->getLaravel()->environment('staging')
$this->getLaravel()->environment(['DB_DATABASE' => $dbname]); 
$this->info('env: ' . $this->getLaravel()->environment('DB_DATABASE'));

// This enables the the putenv adapter. You shouldn't be calling it for this.
\Illuminate\Support\Env::enablePutenv();

// putenv shouldn't be used in Laravel project
putenv('DB_DATABASE=' . $dbname);

// This should set the database for the config, but you need to make sure your using it. Note this change is not persistent.
config(['database.connections.' . $connection . '.database' => $dbname]);
Yahya Uddin
  • 26,997
  • 35
  • 140
  • 231
  • Doesn't this answer assume that 1. the database is already created and 2. I have a database connection defined in config/database.php? Neither of which is the case. This is a dynamic environment that I can't know about before running the tests. – th3coop Apr 16 '21 at 15:17
  • Thanks for the code notes. I understand many of those things you're just not supposed to do. I shared that to show all of the different things I have already tried to make this work. In a normal situation I would avoid making those changes. Can you clarify this comment: > but you need to make sure your using it What do you mean? What would "using it" look like in the context of my shared script? – th3coop Apr 16 '21 at 15:47
  • If this is just for testing, have you had a look at [Database Testing](https://laravel.com/docs/8.x/database-testing). I'm unsure as to why you need to dynamically create and set the database, when Laravel can reset the database. – Yahya Uddin Apr 16 '21 at 16:04
  • There are a couple of things that won't work there: 1. That assumes the DB exists. Imagine this is a CI environment where you have to spin up the entire app ecosystem. 2. It blows away old test DB which I will at some point want to reference 3. ...I thought there was a third but I can't remember it now... – th3coop Apr 16 '21 at 16:08
  • Ahhh i think I see. so `DB::reconnect($connection);` will cause the Database manager to reload the `config(['database.connections.' . $connection . '.database' => $dbname]);` change I make? – th3coop Apr 16 '21 at 16:27
  • 1
    BOOM! That did it, Yahya! Thank you so much! That was driving me CRAZY. – th3coop Apr 17 '21 at 20:01