How can I configure (and use) multiple databases in Zend Framework 2 with doctrine 2 ? Currently I have this in my local.php:
return array(
'doctrine' => array(
'connection' => array(
// default connection name
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'data1',
'charset' => 'utf8',
'driverOptions' => array(
1002=>'SET NAMES utf8'
)
)
)
)
),
);
But I do not see a way to add a second one.
Perhaps this example might show you the way, this is how I configured two databases in my application, but I'm not using Doctrine.
DBs configuration
return array(
'doctrine' => array(
'connection1' => array(
// default connection name
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'data1',
'charset' => 'utf8',
'driverOptions' => array(1002 => 'SET NAMES utf8')
)
)
),
'connection2' => array(
// default connection name
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'data2',
'charset' => 'utf8',
'driverOptions' => array(1002 => 'SET NAMES utf8')
)
)
)
),
);
Then you need to create a DbAdapterFactory that connect to DB connection1 and another factory to connect to DB connection2:
PS: Of course the names are just examples and you can use better ones.
class Adapter1Factory implements FactoryInterface
{
/**
* Create DbAdapter
*
* #param ServiceLocatorInterface $serviceLocator
* #return DbAdapter
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('config');
$configDb1 = $config['connection1'];
$adapter = new \Zend\Db\Adapter\Adapter($configDb1);
return $adapter;
}
}
class Adapter2Factory implements FactoryInterface
{
/**
* Create DbAdapter
*
* #param ServiceLocatorInterface $serviceLocator
* #return DbAdapter
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$config = $serviceLocator->get('config');
$configDb2 = $config['connection2'];
$adapter = new \Zend\Db\Adapter\Adapter($configDb2);
return $adapter;
}
}
Now just use the specific AdapterFactory when you want to connect to certain DB.
Related
I'm trying to migrate multiple migration tables into multiple databases I'm getting this kind of error:
// If the configuration doesn't exist, we'll throw an exception and bail. 149| $connections = $this->app['config']['database.connections'];
150|
151| if (is_null($config = Arr::get($connections, $name))) { >
152| throw new InvalidArgumentException("Database [{$name}] not configured."); 153| }
154|
155| return (new ConfigurationUrlParser)
156| ->parseConfiguration($config); Exception trace: 1 Illuminate\Database\DatabaseManager::configuration("s_request") /home/dipu/A1pathshala/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php:115 2 Illuminate\Database\DatabaseManager::makeConnection("s_request") /home/dipu/A1pathshala/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php:86 Please use the argument -v to see more details.
here's my configuration:
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=A1pathshala
DB_USERNAME=root
DB_PASSWORD=
DB_CONNECTION_SECOND=mysql2
DB_HOST_SECOND=127.0.0.1
DB_PORT_SECOND=3306
DB_DATABASE_SECOND=school_request
DB_USERNAME_SECOND=root
DB_PASSWORD_SECOND=
database.php
connection=>[
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'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' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
//secondary database
'mysql2' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST_SECOND', '127.0.0.1'),
'port' => env('DB_PORT_SECOND', '3306'),
'database' => env('DB_DATABASE_SECOND', 'forge'),
'username' => env('DB_USERNAME_SECOND', 'forge'),
'password' => env('DB_PASSWORD_SECOND', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
In migration table use this.
public function up()
{
Schema::connection('mysql2')->create('schools', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::connection('mysql2')->dropIfExists('schools');
}
}
and migrating use this
php artisan migrate --database=mysql2
Each connected member of my site has its database.
Here is the doctrine config for "user_1":
return array(
'doctrine' => array(
'connection' => array(
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'user_1',
'password' => 'psswd_user_1',
'dbname' => 'database_user_1',
'charset' => 'utf8',
'driverOptions' => array (1002 => 'SET NAMES utf8'),
)),),),);
Is there a way to replace :
'user_1', 'psswd_user_1' and 'database_user_1'
with
'user_x', 'psswd_user_x' and 'database_user_x' for user_x ?
I don't know how to do that !
I'd like to avoid copying the same code for each user ...
Thank you for help
The proper way to do this might be to inject the configuration that you need when the connection is created. I couldn't find any event that you could hook into to do that, so you have to find the right service manager key to override.
With a little bit of source code digging, I found that these options are sent to a DoctrineORMModule\Options\DBALConnection instance and that this instance is created by DoctrineORMModule\Service\DBALConnectionFactory
You need to override this factory with something like this:
<?php
namespace MyModule\Service;
use DoctrineORMModule\Service\DBALConnectionFactory;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyDBALConnectionFactory extends DBALConnectionFactory
{
public function getOptions(ServiceLocatorInterface $sl, $key, $name = null)
{
$options = parent::getOptions($sl, $key, $name);
// override for everyone that needs a DBALConnection
if ($this->getOptionClass() === 'DoctrineORMModule\Options\DBALConnection') {
// set custom parameters here
// maybe fetch the current user with $sl->get('...')
$params = [/* ... */];
$options->setParams($params);
}
return $options;
}
}
And then you just tell the service manager about it:
<?php
return [
...
'doctrine' => [
'doctrine_factories' => [
'connection' => 'MyModule\Service\DBALConnectionFactory',
]
]
...
];
Thanks to Alejandro Celaya.
1st link and 2d link
I hope it will be useful.
I know it's not perfect but I can't do better ! I'd like critical.
in config/autoload/doctrine.local.php:
'doctrine' => array(
'connection' => array(
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
'dbname' => 'gestion_toto_default',
'charset' => 'utf8',
'driverOptions' => array(1002 => 'SET NAMES utf8'),
)
),
'orm_toto_users' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'password' => '',
//gestion_toto_users has 2 tables : users and db_users
'dbname' => 'gestion_toto_users',
'driverOptions' => array(1002 => 'SET NAMES utf8'),
)
),
'dynamic_orm' => array(
'driverClass' =>'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => 'localhost',
'port' => '3306',
'user' => '',
'password' => '',
'dbname' => '',
'driverOptions' => array(1002 => 'SET NAMES utf8'),
),
),
),
'driver' => array(
'orm_toto_users' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\DriverChain',
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
),
'dynamic_orm' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
),
),
'entitymanager' => array(
'orm_toto_users' => array(
'connection' => 'orm_toto_users',
'configuration' => 'orm_default'
),
'dynamic_orm' => array(
'connection' => 'dynamic_orm',
),
),
'eventmanager' => array(
'orm_toto_users' => array()
),
'sql_logger_collector' => array(
'orm_toto_users' => array(),
),
'entity_resolver' => array(
'orm_toto_users' => array()
),),
module.config.php :
'doctrine' => array(
'driver' => array(
__NAMESPACE__ . '_driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(
__DIR__ . '/../src/' . __NAMESPACE__ . '/Entity'
),
),
'orm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
)
),
),
'authentication' => array(
'orm_default' => array(
'object_manager' => 'doctrine.entitymanager.orm_toto_users',
'identity_class' => 'MyModule\Entity\User',
'identity_property' => 'identifiant',
'credential_property' => 'password',
'credential_callable' => function(\MyModule\Entity\User $user, $passwordGiven) {
$bcrypt = new \Zend\Crypt\Password\Bcrypt();
return $bcrypt->verify($psswdGiven, $user->getPsswd()) && $user->getIsactif();
},
),
),
),
in Xcontroller :
public function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('dynamic_entity_manager');
}
return $this->em;
}
in userController :
public function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_toto_users');
}
return $this->em;
}
in DynamicEMFactory.php:
class DynamicEMFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator)
{
// Get current user
$authService = $serviceLocator->get('Zend\Authentication\AuthenticationService');
if (! $authService->hasIdentity()) {
throw new \RuntimeException(
'It is not possible to create a dynamic entity manager before a user has logged in'
);
}
$user = $authService->getIdentity();
$db_User = $user->getUser_db()->getDbuser();
$db_Psswd = $user->getUser_db()->getDbpsswd();
$db_Name = $user->getUser_db()->getDbname();
// Update connection config
$globalConfig = $serviceLocator->get('config');
$globalConfig['doctrine']['connection']['dynamic_orm']['params']['user'] = $db_User;
$globalConfig['doctrine']['connection']['dynamic_orm']['params']['password'] = $db_Psswd;
$globalConfig['doctrine']['connection']['dynamic_orm']['params']['dbname'] = $db_Name;
$isAllowOverride = $serviceLocator->getAllowOverride();
$serviceLocator->setAllowOverride(true);
$serviceLocator->setService('config', $globalConfig);
$serviceLocator->setAllowOverride($isAllowOverride);
return $serviceLocator->get('doctrine.entitymanager.dynamic_orm');
}
}
in module.config :
'service_manager' => array(
'factories' => array(
'dynamic_entity_manager' => 'XXX\Service\Factory\DynamicEMFactory',
in onBootstrap (to change appearence):
$authService = $serviceManager->get('Zend\Authentication\AuthenticationService');
if ($authService->getIdentity()) {
$em = $serviceManager->get('dynamic_entity_manager');
} else {
$em = $serviceManager->get('doctrine.entitymanager.orm_default');
}
$viewModel = $e->getApplication()->getMvcEvent()->getViewModel();
$query = $serviceManager->get('param_user');
$tab = $query->getReponse($em);
$nom_theme = $tab['something']));
$viewModel->nom_theme = $nom_theme;//to layout
to populate my form I use the fieldset approach. For one given form field I will use a select and the options are coming directly from an entity like this:
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'city',
'options' => array(
'label' => 'City: ',
'object_manager' => $this->_om,
'target_class' => 'Hotbed\Entity\AllAdresses',
'property' => 'city',
'is_method' => true,
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('postal_code' => $postalCode),
'orderBy' => array('city' => 'ASC'),
),
),
),
'attributes' => array(
'class' => 'form-control input-large',
'required' => '*'
),
)
);
This works pretty well. The only inconvient is that I have to put a distinct on the field city. How can I solve this problem?
Regards Andrea
The way I got around this was to create a function in the repository to return the distinct entities, and then specify that function name in your form element.
So in your case, for example:
In your repository:
public function findDistinctCitiesByPostalCode($postalCode) {
$dql = "SELECT DISTINCT a.city "
. "FROM Hotbed\Entity\AllAdresses a "
. "WHERE a.postalCode :postalCode";
$qry = $this->getEntityManager()->createQuery($dql);
$qry->setParameter('postalCode', $postalCode);
$results = $qry->getArrayResult();
// $results will be an array in the format
// array(array('city' => 'city_1'), array('city' => 'city_1'),....)
// so you'll need to loop through and create an array of entities
foreach ($results as $row) {
$addresses[] = new Hotbed\Entity\AllAdresses(array('city' => $row['city']);
}
return $addresses;
}
And then in your form:
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'city',
'options' => array(
'label' => 'City: ',
'object_manager' => $this->_om,
'target_class' => 'Hotbed\Entity\AllAdresses',
'property' => 'city',
'is_method' => true,
'find_method' => array(
'name' => 'findDistinctCitiesByPostalCode'
)
)
)
);
I'm trying to set a login(Authentication) using Zend2 and DoctrineODMModule but I am getting an error.
I have followed the tutorial to setup the Authentication of Zend2 with doctorineODMModule on github
any suggestion what I am doing wrong? or what I have to do?
i have done it in the following way.
in doctrine mdule.config.php
'authentication' => array(
'odm_default' => array(
'object_manager' => 'doctrine.documentmanager.odm_default',
'identity_class' => 'Admin\Document\User',
'identity_property' => 'username',
'credential_property' => 'password',
),
),
'odm_driver' => array(
'class' => 'Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver',
'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Document')
),
'odm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Document' => 'odm_driver'
)
)
in Admin/Document/User.php created two methods getUsername and getPassword.
public function getUsername(){
return $this->username;
}
public function getPassword(){
return $this->password;
}
created controller in index controller.php
public function loginAction(){
$this->layout('layout/login-layout.phtml');
$login_error=false;
$loginForm = new LoginForm();
if ($this->request->isPost())
{
$loginForm->setData($this->request->getPost());
if ($loginForm->isValid())
{
// try {
// throw new \Exception("My exception");
$data = $loginForm->getData();
$authService = $this->getServiceLocator()
->get('doctrine.authenticationservice.odm_default');
$adapter = $authService->getAdapter();
$adapter->setIdentityValue($data['username']); // i am using username
$adapter->setCredentialValue(md5($data['password']));
$authResult = $authService->authenticate();
if ($authResult->isValid()) {
$this->redirect()->toRoute('admin_index'); // or last viewed page
}
/*} catch (Exception $e) {
echo "Caught exception $e\n";
echo $e->getPrevious();
$login_error=false;
return new ViewModel(array(
'loginForm' => $loginForm,
'login_error' => $login_error,
));
//exit;
}/
return array(
'loginForm' => $loginForm,
'errors' => 'username or password is not valid',
);
$this->redirect()->toRoute('admin_index');
} else {
//
// LOG Event ( login|password not valide )
//
//Zend\Debug\Debug::dump("not valid data");
//Zend\Debug\Debug::dump($loginForm->getMessages());
$login_error=true;
}//* */
}
}
//
return new ViewModel(array(
'loginForm' => $loginForm,
'login_error' => $login_error,
));
}
With the message you give,
A value for the identity was not provided prior to authentication with ObjectRepository authentication adapter
I'd say that either you didn't give the field to use as identity on you're User Document or during authentication process (in your action) you didn't populate the value for identity (aka login)
Please give more informations about your app (odm module configuration, Identity class...) to provide you a better help
as configuration you should have stg like :
...
'doctrine' => array(
'driver' => array(
__NAMESPACE__ . '_orm_driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Entity')
),
'orm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_orm_driver'
)
),
__NAMESPACE__ . '_odm_driver' => array(
'class' => 'Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver',
'paths' => array(__DIR__ . '/../src/' . __NAMESPACE__ . '/Document')
),
'odm_default' => array(
'drivers' => array(
__NAMESPACE__ . '\Document' => __NAMESPACE__ . '_odm_driver'
)
)
),
'authentication' => array(
'odm_default' => array(
'objectManager' => 'doctrine.documentmanager.odm_default',
'identityClass' => 'Application\Document\User',
'identityProperty' => 'username',
'credentialProperty' => 'password',
'credentialCallable' => 'Application\Utils::hashPassword' // Not needed if you don't hash passwords
),
),
),
...
This was working great for some of my projects
I need some advice on how to set up a unit test in Cake 2.3 that tests OAuth login. I'm using the thomseddon/cakephp-oauth-server plugin. Note: I've reviewed examples such as CakePHP 2.3 - Unit testing User Login, but I'm still confused about how exactly to approach an OAuth test using the plugin. Any help appreciated.
The following is what I currently have in my unit test. Not very much of a test, yet.
/**
* testOAuthLogin method
* Tests that OAuth login works
* #return void
*/
public function testOAuthLogin(){
$data = array(
'response_type' => 'code',
'client_id' => getenv('THREE_SCALE_APP_ID'),
'User' => array(
'username' => TEST_USERNAME,
'passwd' => TEST_PASSWORD
)
);
$result = $this->testAction('/oauth/login', array(
'data' => $data,
'method' => 'post'
));
debug($result);
}
This returns:
{"error":"invalid_client","error_description":"No client id supplied"}
I was able to figure this out. I just needed to setup up proper fixtures for User and AccessToken. And then I had to ensure that these were imported in the controller that I was testing in via $fixtures.
Example of my AccessTokenFixture:
<?php
App::uses('OAuthComponent', 'OAuth.Controller/Component');
/**
* AccessTokenFixture
*
*/
class AccessTokenFixture extends CakeTestFixture {
/**
* Fields
*
* #var array
*/
public $fields = array(
'oauth_token' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 40, 'key' => 'primary', 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'client_id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'user_id' => array('type' => 'integer', 'null' => false, 'default' => null),
'expires' => array('type' => 'integer', 'null' => false, 'default' => null),
'scope' => array('type' => 'string', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'indexes' => array(
'PRIMARY' => array('column' => 'oauth_token', 'unique' => 1)
),
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'MyISAM')
);
/**
* init method
* #return void
*/
public function init() {
$this->records = array(
array(
'oauth_token' => OAuthComponent::hash('SAMPLE_ACCESS_TOKEN'),
'client_id' => 'YOUR_CLIENT_ID',
'user_id' => 1,
'expires' => 1367263611232323,
'scope' => ''
),
array(
'oauth_token' => OAuthComponent::hash('SAMPLE_ACCESS_TOKEN'),
'client_id' => 'YOUR_CLIENT_ID',
'user_id' => 2,
'expires' => 13672640632323323,
'scope' => ''
)
);
parent::init();
}
}