Building Joomla component with Doctrine – Part 1
Tags: Doctrine, Joomla, PHP, Software development March 1st, 2010- Part 1 – Defining component structure
- Part 2 – Creating views for the front-end
- Part 3 – Creating views for the back-end to manage movies
I have done plenty of development on Joomla platform. Although generally it’s pretty good CMS platform for PHP but the part of that I don’t like is it’s MVC implementation. The Model implementation feels just so weird to me. Maybe because I haven’t used to that approach it uses, because I’m more NHibernate guy from .NET space… Anyway, I decided to look for ORMs in a PHP environment and found Doctrine. I did a simple test project with that and decided to use it to develop a simple Joomla component and keep things as simple as possible.
In it’s standard implementation of MVC in Joomla, controller injects needed models to the view and view asks data directly from the models. In other cases (when there is no need for a view) controller uses models directly to accomplish some task. I don’t like that. I’d like when there is just a plain view which is filled with the data by the controller and I would loose the concept of model (in terms of Joomla) wholly. Model layer will be replaced with the Doctrine and it will be used only by the controller not views.
So lets start.
Sample component
For a sample component we use simple movies catalog. It contains models for Movie and Genre. Movie has attributes like name, plot and link to IMDB. Administrators can manage movies and genres from the back-end. From front-end users can browse movies and filter them by genre and name. This should me simple enough to start with. The purpose of this post is to demonstrate how to integrate Doctrine into Joomla, not to demonstrate capabilities of Doctrine. This is the data model:
Firstly we need to install Doctrine under the Joomla libraries folder. You can download the latest stable version (which is 1.2.1) from here. After downloading extract the contents of the lib directory under Joomla libraries/doctrine directory.
Configuring Doctrine and creating models
First we need to configure Doctrine to use Joomla database settings. Interesting thing here is the table name format. Firstly we use the Joomla configured table name prefix. Secondly we add our own table prefix because we don’t want to use all the Joomla tables (at least in this case). Here is sample code.
$componentTablePrefix = 'mv_'; $config = JFactory::getConfig(); $ormManager = Doctrine_Manager::getInstance(); $ormManager->setAttribute(Doctrine_Core::ATTR_TBLNAME_FORMAT, $config->getValue('config.dbprefix').$componentTablePrefix.'%s'); $ormManager->setAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, true); $ormManager->setAttribute(Doctrine_Core::ATTR_VALIDATE, Doctrine_Core::VALIDATE_ALL); $ormManager->setAttribute(Doctrine_Core::ATTR_QUOTE_IDENTIFIER, true); $conn = Doctrine_Manager::connection( $config->getValue('config.dbtype').'://'. $config->getValue('config.user').':'. $config->getValue('config.password').'@'. $config->getValue('config.host').'/'. $config->getValue('config.db'), 'default');
Then we use Doctrine to generate object model from the database tables for us. Of course you can create (or update) them manually if you want, there’s a pretty good documentation on that. We use this command to do that.
$modelsPath = JPATH_SITE.DS.'administrator'.DS.'components'.DS.'com_movies'.DS.'models'.DS; Doctrine_Core::generateModelsFromDb($modelsPath, array('default'), array('generateTableClasses' => true)); Doctrine_Core::loadModels($modelsPath);
Unfortunately Doctrine also adds table prefix to the class name. So table name jos_mv_movie is converted to class name JosMvMovie. Generally I would remove that but currently I leave it as it is.
Creating a component structure
Next we create a bootstrapper script for our component to do all the wiring for us. It will include setup for Doctrine and for our component. We use PHP spl_autoload_register function to provide functionality to load all necessary scripts automatically without the need to scatter our scripts with require statements. Because Joomla uses __autoload function, we also need to re-register Joomla autloader with spl_autoload_register. That’s because __autoload can be used only once but we need different autoload functions – for Doctrine, our component and for Joomla itself.
Note: Because of Joomla isn’t written for PHP5 specifically, you need to explicitly set JLoader::load function to static.
We will use the same bootstrapper for front-end and back-end so we need to create separate configuration options for those. Because in front-end we need to load classes that are found from the front-end directories but also classes that are defined in the back-end, whereas database configuration remains same. And when creating some integration tests you may want to connect to different database.
Here is part of the code for bootstrapper.
class MvBootstrapper { private static $configurationMode; public static function configure(ConfigurationMode $mode) { self::$configurationMode = $mode; self::configureJoomla(); self::configureMovies(); self::configureDoctrine(); } private static function getModelsPath() { $modelsPath = JPATH_SITE.DS.'administrator'.DS.'components'.DS.'com_movies'.DS.'models'.DS; return $modelsPath; } private static function configureJoomla() { spl_autoload_register('JLoader::load'); } private static function configureDoctrine() { JLoader::register('Doctrine_Core', JPATH_SITE.DS.'libraries'.DS.'doctrine'.DS.'Doctrine'.DS.'Core.php'); JLoader::load('Doctrine'); spl_autoload_register('Doctrine_Core::autoload'); $ormManager = Doctrine_Manager::getInstance(); $ormManager->setAttribute(Doctrine_Core::ATTR_TBLNAME_FORMAT, self::$configurationMode->getTablePrefix().'%s'); $ormManager->setAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, true); $ormManager->setAttribute(Doctrine_Core::ATTR_VALIDATE, Doctrine_Core::VALIDATE_ALL); $ormManager->setAttribute(Doctrine_Core::ATTR_QUOTE_IDENTIFIER, true); $conn = Doctrine_Manager::connection( self::$configurationMode->getDbType().'://'. self::$configurationMode->getDbUsername().':'. self::$configurationMode->getDbPassword().'@'. self::$configurationMode->getDbHost().'/'. self::$configurationMode->getDbName(), 'default'); Doctrine_Core::loadModels(self::getModelsPath()); } private static function configureMovies() { MvBootstrapper::registerPath('base', self::$configurationMode); MvBootstrapper::registerPath('models/generated', ConfigurationMode::BackEnd()); spl_autoload_register('MvBootstrapper::autoload'); } public static function autoload($className) { // Autoload logic } private static function registerPath($path, ConfigurationMode $mode) { // Path registration logic } }
Now we need to create startup scripts for our component. It’s nothing special, just a regular component starter page. Here is the code for front-end view.
defined('_JEXEC') or die('Restricted access'); require_once (JPATH_COMPONENT_ADMINISTRATOR.DS.'bootstrapper.php'); MvBootstrapper::configure(ConfigurationMode::FrontEnd()); require_once dirname(__FILE__).DS.'controller.php'; $className = 'Controller'; $controller = new $className(); $controller->execute(JRequest::getCmd('task')); $controller->redirect();
That’s it for now. In my next post I’ll show how to create controllers and views for the front-end. And how to use Doctrine in controllers and to pass data to a view.
You can download current version of the component from here.
August 29th, 2010 at 11:51
Thanks for the article!
Especially for the note that JLoader::load must be declared as static: without it code:
spl_autoload_register(‘JLoader::load’);
brokes Joomla.
You saved me several hours!
March 17th, 2011 at 19:09
Thanks for the excellent tutorial.
I found a other simple method which does not requires Jloader::load to be static function. It can be done very easily by adding new function in MvBootstrapper and changing the configureJoomla function as
private static function configureJoomla()
{
//spl_autoload_register(‘JLoader::load’);
spl_autoload_register(array(‘MvBootstrapper’, ‘jooomload’));
}
static function jooomload($class){
JLoader::load($class);
}
Thanks
Kishore
Kishore
August 18th, 2011 at 23:45
Hello, I’m struggeling my way through Joomla and Doctrine. Seperately I know a little about both and got them running. But I can’t manage to combine them. Where or how are those 2 code segements from the beginning of this tutorial (‘Configuring Doctrine and creating models’) to be called?
Thank you Alexander
August 19th, 2011 at 10:25
These parts are integrated into Bootstrapper class, but of course you can use them separately..php (which creates controllers for given component).
And they are called from component’s base file –
Hope it helps
December 5th, 2011 at 06:36
I can’t find right place edit the config file. Please provide more details…
Thanks
December 5th, 2011 at 14:02
Which configuration file you meant?
Only file which contains configuration is bootstrapper.php under admin folder.
October 16th, 2013 at 18:28
Function ‘Doctrine_Core::autoload’ not found (class ‘Doctrine_Core’ not found)
July 9th, 2015 at 09:14
Hi, This is great.
Is you test doctrine with joomla 3 and for a high traffic website using joomla 3 you think it’s work good?
I’m going to develop an component.