Zend_Db and Symfony 2 using Dependency Injection
This week I spent some time playing with Symfony 2, a framework that everyone heard about which become as famous as Zend. After few hours juggling with the bundles, configuration, routing, etc it come to life and does not look as complex as it seems to be.
The first thing you need to know about Symfony 2 is that everything is base on Dependency Injection. So before to go further down into this article I recommend you to read this article which is very, very, very well done and explain DI with simplicity. Just be aware that some of the functionality explained are not up to date so some code sample might not work.
Why using Zend_Db with Symfony 2
So now, why using Zend_Db and not taking advantage of the new Doctrine 2?
Well we can talk for a while about it, some of you would say this is bulls**t and some other would say why not. Really it depends of your application. In the team I work we talked a lot about it and to be honest we first thought using Doctrine 2 and from this decision started the problems…
First our application is running on a centOS server and the database is a MSSQL database running on a Windows box. So to make it work you need to download the dblib drivers. Unfortunately those driver are not up to date and you need to go through them and update whatever is not working (few hours of work and swearing in perspective). We initially chose Doctrine 2 with the idea to use whatever database is supported by Doctrine’s driver, easy to switch. There is even tools to generate the tables from the schema. So one developer would have used mysql, the other MSSQl, etc…
After having fixed the dblib driver (almost, transaction still not work properly) we decided to generate entities from the database. We had a couple of issue because some of our tables didn’t have primary keys. We also had issue with composite key. After successfully generate the entities, we tried to export the schema to a MySQL database and again troubles… Our tables contains reserved name like “date”, “from”, … and Doctrine 2 is not supported quoting.
You can imagine how frustrating it is, spending time trying to setup the database and just hitting the wall at each and every steps. The other other limitation of Doctrine 2 is that everything is based on active record (one entity = one table). If your application has object such as an “article” which can have an “author”, a “category”, “comments”, etc… you’ll need to join those tables using Doctrine records or create your custom queries so what’s the point to have enities? Again, it’s really depending of your application, you have to investigate and chose what you think is best for you.
After few days of investigation we wanted to give a try to Zend_Db and see how it would perform. And here come the reason of this post.
How to import Zend libs in Symfony 2
This is the easy part, you just need to download Zend Framework and paste the lib folder it in the vendor directory. For the sake of the naming convention and because I’m picky sometime, your folder structure should looks like this:
|- vendor | |- zend | | |-lib | | | |- Zend | | | |- Db | | | |- ...
You need to add Zend_ prefix in the autoload.php file:
...
$loader->registerPrefixes(array(
'Twig_Extensions_' => __DIR__.'/../vendor/twig-extensions/lib',
'Twig_' => __DIR__.'/../vendor/twig/lib',
'Swift_' => __DIR__.'/../vendor/swiftmailer/lib/classes',
'Zend_' => __DIR__.'/../vendor/zend/lib',
));
...
After that you need to get rid of all the include directive in all the files.
$ cd path/to/vendor/zend/lib $ find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \ -not -wholename '*/Application.php' -print0 | \ xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
You should be able to call Zend_* objects in your application.
Inject Zend_Db in a repository
In your configuration file you can
parameters:
package.repository.article: Package\BundleName\Repository\Article
services:
myapp.db:
class: Zend_Db
factory_class: Zend_Db
factory_method: factory
arguments: [%driver%, {host: %database_host%, username: %database_user%, password: %database_password%, dbname: %database_name%}]
repository.article:
class: %package.repository.article%
calls:
- [setDb, [@application.db]]
namespace Package\BundleName\Repository;
class Base
{
protected $db;
public function setDb($object)
{
// Here you can check that the db connection is valid
$this->db = $object;
}
}
Now you can extends all your repositories from the base and use Zend_Db for your queries.
Here is an example:
namespace Package\BundleName\Repository;
class Article extends Base
{
/**
* Return an article according its id
*
* @params integer $id Unique Identifier of an article
*
* @return mixed
**/
public function getById($id)
{
$select = $this->db
->select()
->from('article')
->where('id = ?', $id);
return $this->db->query($select)->fetchOne();
}
}
To get the repository you need to call the service in your controller. If you don’t call the service but instantiate directly your repository, it won’t be configured by the service container so the database connection will never be set.
namespace Package\BundleName\Controller;
class ArticleController extends Controller
{
public function IndexAction()
{
$repo = $this->get('repository.article');
$article = $repo->getById(1);
}
}
And here we go… You can now build as much repo as you wish, you just have to add a service for every one of them. Hope this post can help you