Zend Framework 2 DB Adapter setup - php

I want to set up DB ADAPTER in Zend Framework 2 like as DB ADAPTER in Zend Framework 1.
IN ZF1 bootstrap.php I've
protected function _initDatabase() {
$this->bootstrap('db');
$dbResource = $this->getResource('db');
Zend_Registry::set(ESIGN_REGISTRY_KEY_DB, $dbResource);
}
and in application.ini
resources.db.adapter = "pdo_mysql"
resources.db.params.dbname = "DB NAME"
resources.db.params.host = "HOST"
resources.db.params.username = "DB USER"
resources.db.params.password = "DB PASSWORD"
In my application I can use
$dbAdapter = Zend_Registry::get('db');
and get DB ADAPTER.
Please help me to config it in ZF2.
Thanks.

There's a few ways you can do this and it depends on how you're structuring your application really. But the following is assuming you have everything setup correctly for the db adapter in you config file.
So you could do this somewhere,
use Zend\Db\TableGateway\Feature\GlobalAdapterFeature;
// note, $sm is the service manager here
GlobalAdapterFeature::setStaticAdapter($sm->get('adapter_alias_name'));
The alias is set in your config file like:
// both of these are from factories in the `service manager` key in the config file.
'aliases' => array(
'adapter_alias_name1' => 'Zend\Db\Adapter\Adapter1',
'adapter_alias_name2' => 'Zend\Db\Adapter\Adapter2',
)
Anyway, continuing, to get the static adapter you would then use:
\Zend\Db\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter();
That might be all you need, but you could also create a class that your models could extend, that would have the db adapter in it.
For example something like:
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\TableGateway\Feature\RowGatewayFeature;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\TableGateway\Feature;
abstract class AbstractTable extends AbstractTableGateway
{
public function __construct()
{
$this->featureSet = new Feature\FeatureSet();
$this->featureSet->addFeature(new Feature\GlobalAdapterFeature());
$this->featureSet->addFeature(new Feature\RowGatewayFeature($this->primary));
$this->featureSet->setTableGateway($this);
$this->featureSet->apply('preInitialize', array());
// view your adapter settings
echo '<pre>' . print_r($this->adapter, true) . '</pre>;die;
}
}
Then your models could just extend that class and the adapter would already be set. Hopefully that makes sense and helps some. I've seen this implemented in a lot of different ways.

Related

Symfony 1.4 connect to mysql via SSL

I need to change an old Symfony 1.4 application so that it's able to connect to mysql via ssl-connection.
I found a lot about this for Symfony >= 2. But unfortunately not for this dusty one.
For validation purposes I already made it work by editing
./apps/frontend/lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Connection.php
$this->dbh = new PDO($this->options['dsn'], $this->options['username'],
(!$this->options['password'] ? '':$this->options['password']), array(PDO::ATTR_PERSISTENT => true));
to
$this->dbh = new PDO($this->options['dsn'], $this->options['username'],
(!$this->options['password'] ? '':$this->options['password']),
array(PDO::ATTR_PERSISTENT => true,
PDO::MYSQL_ATTR_SSL_KEY => '/etc/my.cnf.d/ssl/client-key.pem',
PDO::MYSQL_ATTR_SSL_CERT => '/etc/my.cnf.d/ssl/client-cert.pem',
PDO::MYSQL_ATTR_SSL_CA => '/etc/my.cnf.d/ssl/ca-cert.pem'));
But I wonder if this ugly hack is actually the only solution?
It took me a while to see that this connection class is already overwritten (apps/frontend/lib...).
So I only had to make these variables configurable. There is an option in databases.yml configuration called attributes (doctrine::param::attributes). If you pass non-string keys you can get them with getAttribute.
So at least it works (it's inside the try area of connect-method).
$sslOptionKeys = array(PDO::MYSQL_ATTR_SSL_KEY, PDO::MYSQL_ATTR_SSL_CERT, PDO::MYSQL_ATTR_SSL_CA);
foreach($sslOptionKeys as $sslOptionKey) {
if(array_key_exists($sslOptionKey, $this->pendingAttributes)) {
$pdoOptions[$sslOptionKey] = $this->getAttribute($sslOptionKey);
}
}
$this->dbh = new PDO($this->options['dsn'], $this->options['username'],
(!$this->options['password'] ? '':$this->options['password']),
$pdoOptions);
In databases.yml you will have to type the following (comments help to understand these numbers)
all:
doctrine:
class: sfDoctrineDatabase
param:
dsn: mysql:host=localhost;dbname=db
username: user
password: pass
encoding: utf8
attributes:
#PDO::MYSQL_ATTR_SSL_KEY
1010: /etc/my.cnf.d/ssl/client-key.pem
#PDO::MYSQL_ATTR_SSL_CERT
1011: /etc/my.cnf.d/ssl/client-cert.pem
#PDO::MYSQL_ATTR_SSL_CA
1012: /etc/my.cnf.d/ssl/ca-cert.pem
We found that the attributes array was not working. We had to add an event listener that listened for the doctrine 'doctrine.configure_connection' event and set the properties on the connection directly.
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
//existing code
$this->dispatcher->connect('doctrine.configure_connection', array(
'ProjectConfiguration','addConnectionSSL'
));
}
static public function addConnectionSSL(sfEvent $event){
$connection = $event->getParameters()['connection'];
/* #var $connection Doctrine_Manager */
$other = $connection->getOption('other');
if(!is_array($other)) $other=array();
$other[PDO::MYSQL_ATTR_SSL_CA] = "PATH_TO_CERT_FILE"; //Set this to actual path. You can also set other properties in the same way.
$connection->setOption('other',$other);
}
}

How to access dynamic references from other container items?

How can I pass a dynamic dependency from one registered container definition to another? In this case, a generic Database object wants to inherit from a generic Config object. The twist is config is not static, but loaded depending on a given environment variable.
Config pertinent methods
public function __construct()
{
$configFile = 'example.config.yml';
$yamlParser = new Parser();
$reader = new Config\Reader($yamlParser);
$configYaml = $reader->parse(file_get_contents($configFile));
$config = new Config\Environment(getenv('SITE'), $configYaml);
$this->config = $config;
}
public function getEnvironmentConfig()
{
return $this->config;
}
Registering config is as simple as
$container->register('config', 'Config');
Database is currently added to the container as follows:
$container
->register('database', 'Database')
->addArgument($config->getEnvironmentConfig('Database', 'db.username'))
->addArgument($config->getEnvironmentConfig('Database', 'db.password'))
;
But I want to do something like
$container
->register('database', 'Database')
->addArgument(new Reference('config')->getEnvironmentConfig('Database', 'db.username'))
->addArgument(new Reference('config')->getEnvironmentConfig('Database', 'db.password'))
;
The $config in-PHP variable makes migrating from a PHP-built config impossible. I want to define the services in yaml force the container to:
Instantiate Config
Parse the config yaml file and create an environment-specific version
Return this on a call to getEnvironmentConfig
Is this possible?
This was solved by using the Expression Language Component
So you can easily chain method calls, for example:
use Symfony\Component\ExpressionLanguage\Expression;
$container->register('database', 'Database')
->addArgument(new Expression('service("config").getEnvironmentConfig("Database", "db.username")'));

How to write and use config files in Zend Framework 2

Hi,
In Zend Framwork 1, I used to have an application\configs\appsettings.xml, where I used to store params and values like hostnames for Rest API URLs, debug settings and other application specific settings for dev, test and prod environments. This registry was available to me across all controllers and models and was created in index.php
$applicationEnvironment = 'development';
$config = new Zend_Config_Xml( APPLICATION_PATH . '/configs/appsettings.xml',
$applicationEnvironment, true );
Zend_Registry::set( 'config', $config );
How do I achieve similar thing in Zend Framework 2?
Thanks
There is no such thing as a Registry in ZF2 because it is kind of an anti pattern. It's just a fancy substitution for global variables, which can cause all sort of unwanted side effects in your application.
In ZF2 you have the serviceManager and this allow to cleanly inject all your dependencies into your controllers/models/services. All config files in the config/autoload directory are automaticaly merged into one single array by ZF2 and you can retrieve this from the service manager using $serviceLocator->get('Config'). Whenever you need to use configuration in your controller just create a serviceFactory and inject the config.
class FooController
{
protected $config;
public __construct($config)
{
$this->config = $config;
}
public barAction()
{
//use $this->config
}
}
class Module
{
public function getControllerConfig()
{
return array(
'factories' => array(
'fooController' => function(ControllerManager $cm)
{
$sm = $cm->getServiceLocator();
$config = $sm->get('Config');
$controller = new FooController($config);
return $controller;
},
),
);
}
}
For sake of simplicity the factory above is defined as a closure, but I'd suggest to create a seperate factory class. There are many resources which explain how to do that.
In this example we are injecting the complete configuration, but depending on your use case it will generally be better to only inject the config keys you need.
Alternatively you can wrap certain config values into a dedicated config object with explicit getters and setters and inject this into your controller. Zend\StdLib\AbstractOptions can help you.
If you wish to work with config files and you dont have access to the Service Manager or you wish to write content to it, you can use Zend\Config
To read from, you can do something like:
$config = new Config(include 'config/autoload/my_amazing_config.global.php');
$details = $config->get('array_key')->get(sub_key)->toArray();
To write to, you can do:
// Create the config object
$config = new Zend\Config\Config(array(), true);
$config->production = array();
$config->production->webhost = 'www.example.com';
$config->production->database = array();
$config->production->database->params = array();
$config->production->database->params->host = 'localhost';
$config->production->database->params->username = 'production';
$config->production->database->params->password = 'secret';
$config->production->database->params->dbname = 'dbproduction';
$writer = new Zend\Config\Writer\Xml();
echo $writer->toString($config);
The class support ini, xml, phpArray, json, yaml
You can read more at:
http://framework.zend.com/manual/2.2/en/modules/zend.config.introduction.html

Is there zend bootstrap analog in yii?

I want to try db connection to check is db available. In zend I can place my code in boostrap file and wrap it in try catch.
How to implement this in yii?
Is in yii analog of zend boostrap?
UPD: db is mongo, yii extention for working with db is a directmongosuite
Seems that I find appropriate solution:
Need to prohibit auto connect in config file:
'components' => array(
'edms' => array(
'class' => 'EDMSConnection',
'dbName' => 'homeweb',
'server' => 'mongodb://localhost:27017',
'options' => array('connect' => false)
)
)
all controllers should extend one custom controller (BaseController for example).
Need to write own public function beforeAction method where I can add boostrap code.
class BaseController extends CController
{
public $layout = '//layouts/main';
public $navigationMenu = array();
public $breadcrumbs = array();
public function beforeAction($action)
{
try {
Yii::app()->edmsMongo()->connect();
} catch (Exception $e) {
die('Cannot connect to the database server. Please Try again later.');
}
$isGuest = Yii::app()->user->isGuest;
$this->navigationMenu = $this->_getNavigationMenu($isGuest);
return parent::beforeAction($action);
}
In the beforeAction method need to add return true or execute parent's method.
The bootstrap in yii is pretty much the index.php file under public_html or the yiic.php file (for command line applications).
You will probably have to separate the creating of the application instance and running it (by default it does both on 1 line), so you can do your try/catch between the calls.
Just try to fetch the app component, the mongo plugin will throw an exception if it can't open the connection:
try
{
Yii::app()->mongoDb;
}
...
or Yii::app()->getComponent('mongoDb');

make db connection persistent throught zend framework

I'm using zend framework. currently everytime I need to use the db I go ahead and connect to the DB:
function connect()
{
$connParams = array(
"host" => $host,
"port" => $port,
"username" => $username,
"password" => $password,
"dbname" => $dbname
);
$db = new Zend_Db_Adapter_Pdo_Mysql($connParams);
return $db
}
so I would just call the connect() function everytime I need to use the db
My question is...suppose I want to reuse $db everywhere in my site and only connect once in the very initial stage of the site load and then close the connection right before the site gets sent to the user, what would be the best practice to accomplish this?
Which file in Zend should I save $db in, what method should I use to save it (global variable?), and which file should I do the connection closing in?
If you are using the default project structure (with the application, library, tests and public folders), you should use set up the db parameters in application/configs/application.ini
Example application.ini:
[production]
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "testuser"
resources.db.params.dbname = "testdb"
resources.db.params.password = "testpasswd"
resources.db.isDefaultTableAdapter = true
In this way zend framework will automatically open and close the connections to the database and you can use the Zend_Db_Table or Zend_Db_Table_Abstract classes to query your tables easily, for example, to retrieve the student's data for a given SSN you could write a model (application/models/Student.php) that looks something like this:
<?php
class Model_Student extends Zend_Db_Table_Abstract
{
protected $_name = "student";
public function fetchRowsBySSN($ssn)
{
$select = $this->select();
$select->where('ssn = ?', $ssn);
return $this->fetchRow($select)->toArray();
}
}
As you can see there's no need to open/close the connection and you get an associative array with the fields and values of the student record.
Your best best may be to move all of your db connection code into a separate class in which you can set a static $db var.
protected static $_db;
public static function connect()
{
if (self::$_db == null) {
$config = Zend_Config_Xml(); // whatever you'd use
self::$_db = Zend_Db::factory($config->database);
self::$_db->setFetchMode(Zend_Db::FETCH_OBJ);
self::$_db->query('SET NAMES UTF8');
Zend_Db_Table::setDefaultAdapter(self::$_db); // optional
}
return self::$_db;
}
public static function close()
{
if (self::$_db != null) {
self::$_db->closeConnection();
}
}
According to Zend:
Normally it is not necessary to close a database connection. PHP automatically cleans up all resources and the end of a request. Database extensions are designed to close the connection as the reference to the resource object is cleaned up.
However, if you have a long-duration PHP script that initiates many database connections, you might need to close the connection, to avoid exhausting the capacity of your RDBMS server. You can use the Adapter's closeConnection() method to explicitly close the underlying database connection.

Categories