Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/EntityManagerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace LaravelDoctrine\ORM;

use Doctrine\DBAL\Connections\MasterSlaveConnection;
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\Cache\DefaultCacheFactory;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
Expand All @@ -18,6 +20,7 @@
use LaravelDoctrine\ORM\Configuration\MetaData\MetaDataManager;
use LaravelDoctrine\ORM\Extensions\MappingDriverChain;
use LaravelDoctrine\ORM\Resolvers\EntityListenerResolver;
use LaravelDoctrine\ORM\Utilities\MasterSlaveConfigParser;
use ReflectionException;

class EntityManagerFactory
Expand Down Expand Up @@ -100,12 +103,32 @@ public function create(array $settings = [])
$this->setMetadataDriver($settings, $configuration);

$driver = $this->getConnectionDriver($settings);
$slaveConfig = MasterSlaveConfigParser::hasValidConfig($driver)
? MasterSlaveConfigParser::parseConfig($driver)
: [];

$connection = $this->connection->driver(
$driver['driver'],
$driver
);

if (empty($slaveConfig)) {
$conn = DriverManager::getConnection($connection, $configuration);
} else {
$conn = DriverManager::getConnection([
'wrapperClass' => MasterSlaveConnection::class,
'driver' => $connection['driver'],
'master' => [
'user' => array_get($slaveConfig, 'write.user', ''),
'password' => array_get($slaveConfig, 'write.password', ''),
'host' => array_get($slaveConfig, 'write.host', ''),
'dbname' => array_get($slaveConfig, 'write.dbname', ''),
'port' => array_get($slaveConfig, 'write.port', ''),
],
'slaves' => $slaveConfig['read'],
]);
}

$this->setNamingStrategy($settings, $configuration);
$this->setCustomFunctions($configuration);
$this->setCacheSettings($configuration);
Expand All @@ -121,7 +144,7 @@ public function create(array $settings = [])
$configuration->setEntityListenerResolver($this->resolver);

$manager = EntityManager::create(
$connection,
$conn,
$configuration
);

Expand Down
76 changes: 76 additions & 0 deletions src/Utilities/MasterSlaveConfigParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace LaravelDoctrine\ORM\Utilities;

use UnexpectedValueException;

class MasterSlaveConfigParser
Copy link
Contributor

@guiwoda guiwoda Sep 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fureszpeter I don't think this should be extracted to a whole class.
This could live inside the EntityManagerFactory, as no other code will need to call it. Plus, a big, big, BIG 👎 to utility (static) classes.

{
/**
* @param array $config
*
* @throws UnexpectedValueException If config not contains valid read / write configuration.
*
* @return array If config is not valid for parse.
*
*/
public static function parseConfig(array $config)
{
if ( ! self::hasValidConfig($config)) {
throw new UnexpectedValueException('Config not contains configuration for master/slave setup.');
}

$masterSlave = [
'write' => [],
'read' => [],
];

$masterSlave['write'] = [
'user' => array_get($config, 'write.username', $config['username']),
'password' => array_get($config, 'write.password', $config['password']),
'host' => array_get($config, 'write.host', $config['host']),
'dbname' => array_get($config, 'write.database', $config['database']),
'port' => array_get($config, 'write.port', $config['port']),
];

foreach (array_get($config, 'read', []) as $slaveConfig) {
$config = [
'user' => array_get($slaveConfig, 'username', $config['username']),
'password' => array_get($slaveConfig, 'password', $config['password']),
'host' => array_get($slaveConfig, 'host', $config['host']),
'dbname' => array_get($slaveConfig, 'database', $config['database']),
'port' => array_get($config, 'write.port', $config['port']),
];

$masterSlave['read'][] = $config;
}

return $masterSlave;
}

/**
* @param array $config
*
* @return bool
*/
public static function hasValidConfig(array $config)
{
if (
array_key_exists('read', $config)
&& ! empty($config['read'])
&& is_array($config['read'])
//All value in $config['read'] should be an array
&& ! in_array(false, array_map(function ($readConfigValue) {
return is_array($readConfigValue);
}, $config['read']))
//All value in $config['read'] should have at least one config difference
&& ! in_array(false, array_map(function (array $readConfig) {
return count($readConfig) > 0;
}, $config['read']))
) {
return true;
}

return false;
}
}
174 changes: 174 additions & 0 deletions tests/Utilities/MasterSlaveConfigParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

use LaravelDoctrine\ORM\Utilities\MasterSlaveConfigParser;

class MasterSlaveConfigParserTest extends PHPUnit_Framework_TestCase
{

/**
* @dataProvider validConfigProvider
*
* @param array $validConfig
*/
public function test_HasValidConfig_returns_true_on_valid_config(array $validConfig)
{
$this->assertTrue(MasterSlaveConfigParser::hasValidConfig($validConfig));
}

/**
* @dataProvider invalidConfigProvider
*
* @param array $invalidConfig
*/
public function test_HasValidConfig_returns_false_on_missing_config(array $invalidConfig)
{
$this->assertFalse(MasterSlaveConfigParser::hasValidConfig($invalidConfig));
}

/**
* @return array
*/
public function validConfigProvider()
{
$valid1['config'] = array_merge(
$this->getDatabaseConfigTemplate(),
[
'write' => [
'host' => '192.168.0.1',
],
'read' => [
[
'host' => '192.168.0.2',
'username' => 'read_user',
],
],
]
);
$valid1['expectedWrite'] = [
'user' => 'write_db_username',
'password' => 'write_db_password',
'host' => '192.168.0.1', //Override the parent's localhost
'dbname' => 'write_db_name',
'port' => 3306,
];
$valid1['expectedRead'] = [
[
'user' => 'read_user',
'password' => 'write_db_password',
'host' => '192.168.0.2', //Override the parent's localhost
'dbname' => 'write_db_name',
'port' => 3306,
],
];

$valid2['config'] = array_merge(
$this->getDatabaseConfigTemplate(),
[
'read' => [
[
'host' => '192.168.0.1',
],
]
]
);
$valid2['expectedWrite'] = [
'user' => 'write_db_username',
'password' => 'write_db_password',
'host' => 'localhost',
'dbname' => 'write_db_name',
'port' => 3306,
];
$valid2['expectedRead'] = [
[
'user' => 'write_db_username',
'password' => 'write_db_password',
'host' => '192.168.0.1', //Override the parent's localhost
'dbname' => 'write_db_name',
'port' => 3306,
],
];

return [
[$valid1['config'], $valid1['expectedWrite'], $valid1['expectedRead']],
[$valid2['config'], $valid2['expectedWrite'], $valid2['expectedRead']],
];
}

/**
* @return array
*/
public function getDatabaseConfigTemplate()
{
return [
'host' => 'localhost',
'database' => 'write_db_name',
'username' => 'write_db_username',
'password' => 'write_db_password',
'driver' => 'mysql',
'port' => 3306,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
];
}

/**
* @return array
*/
public function invalidConfigProvider()
{
$invalidConfig1 = [];
$invalidConfig2 = array_merge(
$this->getDatabaseConfigTemplate(),
[
'write' => [],
// 'read' => [], //Read is missing, it's invalid
]
);
$invalidConfig3 = array_merge(
$this->getDatabaseConfigTemplate(),
[
'write' => [],
'read' => ['host' => '123'], //Read should be an array
]
);

return [
[$invalidConfig1],
[$invalidConfig2],
[$invalidConfig3],
];
}

/**
* @dataProvider invalidConfigProvider
*
* @expectedException UnexpectedValueException
*
* @param array $invalidConfig
*/
public function test_ParseConfig_throws_exception_on_invalid_config(array $invalidConfig)
{
MasterSlaveConfigParser::parseConfig($invalidConfig);
}

/**
* @dataProvider validConfigProvider
*
* @param array $config
* @param array $expectedWrite
* @param array $expectedRead
*/
public function test_ParseConfig_can_inherit_settings(
array $config,
array $expectedWrite,
array $expectedRead
) {
$readWriteConfig = MasterSlaveConfigParser::parseConfig($config);

$this->assertEquals($expectedRead, $readWriteConfig['read']);
$this->assertEquals($expectedWrite, $readWriteConfig['write']);
}
}