This question already has an answer here:
-
How to use multiple databases in Laravel
3 answers
I am developing a application with multiple database access and I want to have PHPUnit tests with this. My current approache is to have in the config\databases.php
multiple connections (mysql, mysql2, mysql3) so I can have in the env file a different access for all of them. Because of this, the models have the $connection
variable defined. In my first feature test I want to access a page and just see the data that I am providing in my factory, so just to get things started. In my phpunit.xml
file I have specified the DB_CONNECTION
to be sqlite
and for each of the MySql setting to have the value=":memory:"
.
LATER EDIT
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE_1" value=":memory:"/>
<env name="DB_DATABASE_2" value=":memory:"/>
<env name="DB_DATABASE_3" value=":memory:"/>
</php>
So above you can find the relevant code from PHPUnit.
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db1
DB_USERNAME=xxx
DB_PASSWORD=xxx
DB_HOST_2=127.0.0.1
DB_PORT_2=3306
DB_DATABASE_2=db2
DB_USERNAME_2=xxx
DB_PASSWORD_2=xxx
DB_HOST_2=127.0.0.1
DB_PORT_2=3306
DB_DATABASE_3=db3
DB_USERNAME_3=xxx
DB_PASSWORD_3=xxx
The problem that I have is the fact that when I run the tests, i have this error -> PDOException: SQLSTATE[HY000] [1049] Unknown database ':memory:'
.
So somehow Laravel is not parsing the memory value. Any suggestion will be mush appreciated.
Thank you
I was having the same issue, but I got things working with some help from Adam Wathan on Twitter.
Here's what I did:
phpunit.xml
:
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="DB_CONNECTION_ACTIVITY_LOG" value="sqlite"/>
<env name="DB_DATABASE_ACTIVITY_LOG" value=":memory:"/>
config/database.php
:
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
],
'mysql' => [
'driver' => env('DB_CONNECTION', 'mysql'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
'mysql-activity-log' => [
'driver' => env('DB_CONNECTION_ACTIVITY_LOG', 'mysql'),
'host' => env('DB_HOST_ACTIVITY_LOG', '127.0.0.1'),
'port' => env('DB_PORT_ACTIVITY_LOG', '3306'),
'database' => env('DB_DATABASE_ACTIVITY_LOG', 'forge'),
'username' => env('DB_USERNAME_ACTIVITY_LOG', 'forge'),
'password' => env('DB_PASSWORD_ACTIVITY_LOG', ''),
'unix_socket' => env('DB_SOCKET_ACTIVITY_LOG', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
.env
:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my-app
DB_USERNAME=root
DB_PASSWORD=
DB_CONNECTION_ACTIVITY_LOG=mysql-activity-log
DB_HOST_ACTIVITY_LOG=127.0.0.1
DB_PORT_ACTIVITY_LOG=3306
DB_DATABASE_ACTIVITY_LOG=my-app
DB_USERNAME_ACTIVITY_LOG=root
DB_PASSWORD_ACTIVITY_LOG=
Also, for anyone not up to the point of the PDOException, make sure to set the connections in your migrations/models, too.
database/migrations/my_migration.php
:
Schema::connection(env('DB_CONNECTION_ACTIVITY_LOG', 'mysql'))->create(...);
app/MyModel.php
:
class MyModel extends Model
{
public function __construct($attributes = [])
{
parent::__construct($attributes);
$this->connection = config('app.env') === 'testing' ? 'sqlite' : 'mysql-activity-log';
}
...
}
Hard to decode where you actually put the :memory:
value.
In the phpunit.xml <php>
section, this should be sufficient (assuming you haven't modified the sqlite connection):
<php>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
</php>
To resolve a similar problem, I used a trait on the Model classes.
In my phpunit.xml I have this code
<env name="DB_CONNECTION" value="sqlite_testing"/>
<env name="DB_DATABASE" value=":memory:"/>```
In my config/database.php file I have connections set up for each of the databases, and a sqlite_testing connection set up for testing
'sqlite_testing' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'mysql_connection_a' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
'mysql_connection_b' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_B', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
'mysql_connection_c' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_C', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
I then create a trait for each of my connections to set the connection and include them in the relevant models. e.g. if the User model needed to use mysql_connection_a I would use ConnectionATrait in the model
use App\Traits\ConnectionATrait;
class User extends Authenticatable
{
use Notifiable, ConnectionATrait;
The trait would then look like this
trait ConnectionATrait
{
/**
* The database table used by the model.
*
* @var string
*/
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
if (env('APP_ENV') != 'testing') {
$this->connection = 'mysql_connection_a';
}else{
$this->connection = 'sqlite_testing';
}
}
}
If you use migrations in your tests I also had to do a similar approach in the migration files and use a trait for each connection.
For the mysql_connection_a I create a trait that looks likes below that overrides the getConnection method:
trait ConnectionAConnectionTrait
{
/**
* Get the migration connection name.
*
* @return string
*/
public function getConnection()
{
if (env('APP_ENV') != 'testing') {
return 'mysql_connection_a';
}
return 'sqlite_testing';
}
}
Then in the migration it would look like this
use Database\migrations\traits\ConnectionAConnectionTrait;
class CreateUsersTable extends Migration {
use ConnectionAConnectionTrait;
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::connection($this->getConnection())
->create('users', function(Blueprint $table)
{