zf2 dynamic database connection (with parameters) - php

I'm trying to create a ZF2 application with multiple databases. Based on a user, the database should be dynamically set.
Right now I've the following:
database.local.php
return array(
'db' => array(
'adapters' => array (
'master_db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=master_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD'
),
'tentant_db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=tenant_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD'
),
)
),
'service_manager' => array(
'abstract_factories' => array(
'Zend\Db\Adapter\AdapterAbstractServiceFactory',
)
),
);
For test purposes I've created a form that has a method to fetch some data and put it in a select box. The code to get the database connection is shown in the code below.
MyController.php (in some module)
//... some code
public function someAction(){
$dbAdapter = $this->getServiceLocator()->get('tentant_db');
$form = new AddEolConnectorForm($dbAdapter);
$viewModel = new ViewModel(array(
'form' => $form
));
return $viewModel;
}
//... some more code
My question is, how can I dynamically set the dbname for the tentant_db adapter in my controller (or module)?
Thanks for your help.

The config merge event is one of zend newer event's I believe. It triggers when zend is mergin the config array's which is perfect for the problem you are facing since you can override some array key's dynamically.
public function onMergeConfig(ModuleEvent $e)
{
$configListener = $e->getConfigListener();
$config = $configListener->getMergedConfig(false);
// I'm actually not sure if you have the route match here otherwise you may have to
// use some other method to retrieve the url.
$match = $e->getRouteMatch();
switch ($match) {
case 'first-dependency':
$config['db']['adapter'] => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=master_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD',
),
break;
case 'second-dependency':
$config['db']['adapter'] => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=tenant_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD',
),
break;
// Pass the changed configuration back to the listener:
$configListener->setMergedConfig($config);
}

Based on the above answer I've created to following:
Module.php
class Module implements AutoloaderProviderInterface
{
public function init(ModuleManager $moduleManager)
{
$events = $moduleManager->getEventManager();
// Registering a listener at default priority, 1, which will trigger
// after the ConfigListener merges config.
$events->attach(ModuleEvent::EVENT_MERGE_CONFIG, array($this, 'onMergeConfig'));
}
public function onMergeConfig(ModuleEvent $e)
{
$db = $this->getTentantDb();
$configListener = $e->getConfigListener();
$config = $configListener->getMergedConfig(false);
$config['db']['adapters']['tenant_db']['dsn'] = 'mysql:dbname='. $db .';host=localhost';
$configListener->setMergedConfig($config);
}
// Some more code
public function getTenantDb(){
$tenant_db = 'tenant_12345'
return $tenant_db;
}
}
I don't know if it is the best solution, but the above code is working. I think the next steps should be to put the code in a generic module or something so I can access it from all my modules.

Related

Zend Framework 2 database connection issue

I am used ZF2 for creating new applications.
We have some issue regarding db connection under controller and model file.
We have put all database credential in "global.php" and "db.local.php" and also fetch db adapter access in our "Module.php" file but we have not fetch database connectivity in controller and model files and don't run query under controller and model.
Here is my code :
**global.php :**
return array(
'db' => array(
'driver'=> 'Pdo',
'dsn'=> 'mysql:dbname=pick_fire;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
**db.local.php :**
<?php
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=pick_fire;host=localhost',
'username' =>'root',
'password' =>'123456',
'driver_options'=> array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'
),
),
'service_manager' => array(
'aliases' => array(
'db' => 'Zend\Db\Adapter\Adapter',
),
),);
----------------------------------------------
**Module.php :**
public function getServiceConfig()
{
return array(
'factories' => array(
'mail.transport' => function (ServiceManager $serviceManager) {
$config = $serviceManager->get('Config');
$transport = new Smtp();
$transport->setOptions(new SmtpOptions($config['mail']['transport']['options']));
return $transport;
},
),
);
return array(
'factories' => array(
'adapter' => function($serviceManager) {
$config = $serviceManager->get('config');
$dbAdapter = new \Zend\Db\Adapter\Adapter($config['db']);
return $dbAdapter;
}
),
);
}
----------------------------------------------
Please suggest me, how we get fetch database connection and run queries in our controller and model files.
Thank You in Advanced.
In your getServiceConfig() you have two return statements ... the second is never called.
This should work :
**Module.php :**
public function getServiceConfig()
{
return array(
'factories' => array(
'mail.transport' => function (ServiceManager $serviceManager) {
$config = $serviceManager->get('Config');
$transport = new Smtp();
$transport->setOptions(new SmtpOptions($config['mail']['transport']['options']));
return $transport;
},
'adapter' => function($serviceManager) {
$config = $serviceManager->get('config');
$dbAdapter = new \Zend\Db\Adapter\Adapter($config['db']);
return $dbAdapter;
}
),
);
}

ZF2 - Missing instance/object for parameter driver error

I'm getting the following error message with zend framework 2 application:
Missing instance/object for parameter driver for
Zend\Db\Adapter\Adapter::__construct (File:
C:\xampp\zendfw2\ZendSkeletonApplication\vendor\zendframework\zendframework\library\Zend\Di\Di.php:856)
My code is the following:
autoload/local.php:
return array(
'di' => array(
'instance' => array(
'Zend\Db\Adapter\Adapter' => array(
'parameters' => array(
'driver' => 'Zend\Db\Adapter\Driver\Pdo\Pdo',
),
),
'Zend\Db\Adapter\Driver\Pdo\Pdo' => array(
'parameters' => array(
'connection' => 'Zend\Db\Adapter\Driver\Pdo\Connection',
),
),
'Zend\Db\Adapter\Driver\Pdo\Connection' => array(
'parameters' => array(
'connectionInfo' => array(
'dsn' => "mysql:dbname=zendtest;host=localhost",
'username' => 'root',
'password' => '',
'driver_options' => array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''),
),
),
),
),
),
);
module.config.php:
return array(
'di' => array(
'Test2\Model\ManagerAbstract' => array(
'parameters' => array(
'adapter' => 'Zend\Db\Adapter\Adapter',
),
),
),
...);
ManagerAbstract.php:
namespace Test2\Model;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Adapter\AdapterAwareInterface;
abstract class ManagerAbstract implements AdapterAwareInterface
{
protected $adapter;
public function setDbAdapter(Adapter $adapter)
{
$this->adapter = $adapter;
}
public function getUserList() {
$sql = new Sql($this->adapter);
$select = $sql->select();
$select->from(array('u'=>'tsrv_user'));
$select->where(array('username' => $username));
$statement = $sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
return $results;
}
}
User.php:
namespace Test2\Model;
class User extends ManagerAbstract
{
public function __construct() {
}
}
And I call it in the Controller like:
$di = new Di();
$model = $di->get('Test2\Model\User');
$model->getUserList();
Can you please help me by pointing out what causes the error message?
I think the definition of the Zend\Db\Adapter\Adapter in your Di configuration isn't correct.
You can find here a correct way how to do that : Zend\Db Adapter instantiation through Zend Framework 2 Di.

Zend Framework 2 – can't get around ServiceNotFoundException

Today I bought a book to learn the Zend Framework 2. Having started with the skeleton Application & skeleton module, I made good progress, until database interaction started. Now every time I want to do something with the database, I get the following exception:
Zend\ServiceManager\Exception\ServiceNotFoundException
File: /Applications/AMPPS/www/myproject/ZendSkeletonApplication/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:529
Message:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter
Possible reasons for this could be incorrect database details, but having used the same credentials for other projects, I know they are correct (local development).
My global.php looks like this – I can't see any errors:
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=mydatabasename;host=localhost',
'username' => 'root',
'password' => 'mysql',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory'
),
),
)
);
Following the stacktrace, the error must be in the beginning of this method:
protected function createUser(array $data)
{
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new \Zend\Db\ResultSet\ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new \Users\Model\User);
$tableGateway = new \Zend\Db\TableGateway\TableGateway('user', $dbAdapter, null, $resultSetPrototype);
$user = new User();
$user->exchangeArray($data);
$userTable = new UserTable($tableGateway);
$userTable->saveUser($user);
return true;
}
But again, I can't see any error here, which leaves me a bit puzzled. I guess the error is more than just a typo.
The class that method is located in uses the following Zend Framework 2 components (besides self written ones):
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
Do I need to add something here, maybe to be able to use the getServiceLocator() method? I don't know there that method is located.
Do you have anymore ideas what could cause this exception? I am using Zend Framework 2.3.2
You have put the service_manager key as a subset to db. This is wrong.
The service_manager key needs to be top level.
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=mydatabasename;host=localhost',
'username' => 'root',
'password' => 'mysql',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory'
)
)
);

Database could not connect to Zend Framework

I'm sorry I'm completely new to Zend Framework 2 with some tutorials I'm trying to connect my DB connection as follows,
Created a file in
xampp\htdocs\articlemanager\application\configs\autoload\global.php
Inserted the following Zend DB connection code to global.php
<?php
return array(
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
),
'aliases' => array(
'db' => 'Zend\Db\Adapter\Adapter',
),
),
'db' => array(
'driver' => 'PDO_MYSQL',
'dsn' => 'mysql:dbname=articlemanager;host=localhost',
'username' => 'root',
'password' => '',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
);
and In the Indexcontroller (\xampp\htdocs\articlemanager\application\controllers\IndexController.php) tested adding $this->db = $this->getServiceLocator()->get('db'); in the indexAction as follows
public function indexAction()
{
$this->db = $this->getServiceLocator()->get('db');
}
When I refresh page it display as
An error occurred
Application error
Can I know what I missed here?
Also I would like to know My Zend Library is in the \xampp\php\Zend and My global.php file in the xampp\htdocs\articlemanager\application\configs\autoload\global.php is it OK?
Why are you using an Alias on 'Db' ?
Try this, your driver name is different from mine.
In addition, please move your username and password to local.php.. so they do not persist in Git projects
global.php
<?php
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=articlemanager;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
local.php example
<?php
return array(
'db' => array(
'username' => 'DB_USERNAME',
'password' => 'DB_PASSWORD',
),
);
database connection problems can be a number of different issues that are not necessarily obvious from looking at the creds / db config info.
have you taken a look at any logs? my application has several different logs setup - PHP error, access log, mysql error log, etc. - depending on your application, you may not have this many as discretely defined, but checking any logs you do have will give you a whole lot more information than just "An error has occurred" :)

PHP and Zend framework

How can I easily implement queries in Zend framework?
Check this document:
Zend Framework Database Quick Start (PDF)
You can use doctrine2
Doctrine project.
There is a module compatibile with ZF3
DoctrineModule.
You can use QueryBuilder that brings creation of query to object manipulation.
You can use the Zend Db Adapter object like so:
$sql = 'SELECT * FROM bugs WHERE bug_id = ?';
$result = $db->fetchAll($sql, 2);
Use Zend_Db and just create a $db Object using Zend_Db Factory Method, and then create SQL Statements using Zend_Db_Select Class and pass the $select SQL Statement to the fetch* (fetchRow, fetchAll...) Methods.
1.Config:
config/autoload/dbAdapter.local.php
<?php
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=name;host=localhost',
'username' => 'root',
'password' => 'root',
),
'service_manager' => array(
'abstract_factories' => array(
'Zend\Db\Adapter\AdapterAbstractServiceFactory',
),
),
);
Implementation:
public function testAction()
{
$username = 'user';
$sql = "SELECT email FROM users WHERE username = ?";
$statement = $this->getDbAdapter()->createStatement($sql, array($username));
$result = $statement->execute()->current();
}
protected function getDbAdapter()
{
if($this->dbAdapter == null) {
$this->dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');
}
return $this->dbAdapter;
}
Zend framework has abstract_factories ,it allows us to handle multiple DB queries :
Zend\Db\Adapter\AdapterAbstractServiceFactory
Need to set Service Manager :
'service_manager' => array(
'abstract_factories' => array(
'Zend\Db\Adapter\AdapterAbstractServiceFactory',
),
),
Configure adapters in config/autoload/local.php
db' => array(
'adapters' => array(
'database1' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=userDB;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
etc...
),
),
Configure adapters in config/autoload/global.php
return array(
'db' => array(
'adapters' => array(
'database1' => array(
'username' => 'root',
'password' => '',
),
),
),
);
Call adapters
$dbmanager->get('database1');
Use in Model
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Adapter\Adapter;
class UserTable extends AbstractTableGateway
{
protected $table ='user';
public function __invoke(Adapter $adapter)
{
$this->adapter = $adapter;
$this->initialize();
}
public function fetchAll()
{
$resultSet = $this->select();
return $resultSet->toArray();
}
}

Categories