Building Joomla component with Doctrine, adding views – Part 2
Tags: Doctrine, Joomla, PHP, Software development March 4th, 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
In my previous post we set up initial structure for the component and generated models. Now it’s time to add some front-end views. I’ll show you how Doctrine can be used in controllers and how to create clean and simple views.
When using Joomla pattern, then most of the work is done in view’s display() method. I don’t like that. I’d like that my controller provides needed data to the view. So we need to create a base controller from which our component controllers inherit to provide an injection point for our views. View is created in controller’s display() method so we need to override that but most of the code remains same, though.
protected abstract function onViewLoaded(MvViewBase $view); public function display($cachable=false) { $document = JFactory::getDocument(); $viewType = $document->getType(); $viewName = ucfirst(JRequest::getCmd('view', 'topic')); $viewLayout = JRequest::getCmd('layout', 'default'); $view = $this->getView($viewName, $viewType, '', array('base_path' => $this->_basePath)); // Set the layout $view->setLayout($viewLayout); // Display the view if ($cachable && $viewType != 'feed') { global $option; $cache = JFactory::getCache($option, 'view'); $cache->get($view, 'display'); } else { $this->onViewLoaded($view); $view->display(); } } public function getModel($name='', $prefix='', $config=array()) { return false; }
So now controller can be very simple – only load data using Doctrine and initialize the view. Views contains also only setters and getters for the data they need.
class Controller extends MvControllerBase { protected function onViewLoaded(MvViewBase $view) { switch($view->getName()) { case 'movies': $this->dataBindMoviesView($view); break; case 'movie': $this->dataBindMovieView($view); break; } } private function dataBindMoviesView(ViewMovies $view) { $query = Doctrine_Query::create() ->select('m.*') ->from('JosMvMovie m') ->orderBy('m.title ASC'); $movies = $query->execute(); $view->setMovies($movies->getData()); } private function dataBindMovieView(ViewMovie $view) { $movieId = JRequest::getInt('id', 0); $movie = Doctrine_Core::getTable('JosMvMovie')->find($movieId); $view->setMovie($movie); } }
Doctrine (like most of the ORMs) can load related data automatically so no extra work needed for that. In our case, I can just ask genres from the movie, I don’t need to load them separately. Doctrine also allows tuning how and when relations are loaded – are they lazy-loaded or joined directly when executing query, for example. You can read more from here and here.
Because we have many-to-many relation between movies and genres, automatically generated models need some tuning. We have to specify the relation class name so that Doctrine can detect how it can fill the collections. When looking the definition of BaseJosMvMovie class, which contains mappings with the data model, then there is:
public function setUp() { parent::setUp(); $this->hasMany('JosMvMovieGenre', array( 'local' => 'genre_id', 'foreign' => 'genre_id')); }
We need to add refClass attribute there which defines the class for many-to-many relation.
public function setUp() { parent::setUp(); $this->hasMany('JosMvGenre as Genres', array( 'local' => 'movie_id', 'foreign' => 'genre_id', 'refClass' => 'JosMvMovieGenre')); }
Same kind of change needs to be done with the BaseJosMvGenre too, if you want access movies from the genre. Now we only need to add some views to display our movies. They are dead simple.
class ViewMovies extends MvViewBase { private $_movies; public function setMovies(array $movies) { $this->_movies = $movies; } public function getMovies() { return $this->_movies; } }
And HTML for that.
<table width="100%" border="1"> <caption>Movies</caption> <thead> <tr> <th>Title</th> <th>IMDB</th> <th>Genres</th> </tr> </thead> <tbody> <?php foreach($this->getMovies() as $movie): ?> <tr> <td title="<?php echo $movie->plot; ?>"><a href="<?php echo $this->getRouter()->createMovieLink($movie); ?>"><?php echo $movie->title; ?></a></td> <td><a href="<?php echo $movie->imdb_link; ?>" target="_blank">IMDB</a></td> <td><?php echo join(', ', $movie->Genres->getData()); ?></td> </tr> <?php endforeach; ?> </tbody> </table>
So this is it for now. Hope it gets you started. In my next post in the series I’ll create views for the back-end, there are things a little bit different, but not much. Full source code with both the views can be downloaded here.
July 27th, 2010 at 19:46
hello,
I was testing, but after installing the component, when I go to gives me the following problem:
Fatal error: Uncaught exception ‘LogicException’ with message ‘Function ‘JLoader::load’ not callable’ in /towork/sites/online/test/administrator/components/com_movies/bootstrapper.php:26 Stack trace: #0 /towork/sites/online/test/administrator/components/com_movies/bootstrapper.php(26): spl_autoload_register(‘JLoader::load’) #1 /towork/sites/online/test/administrator/components/com_movies/bootstrapper.php(13): MvBootstrapper::configureJoomla() #2 /towork/sites/online/test/administrator/components/com_movies/movies.php(5): MvBootstrapper::configure(Object(BackEndConfigurationMode)) #3 /towork/sites/online/test/libraries/joomla/application/component/helper.php(162): require_once(‘/towork/sites/o…’) #4 /towork/sites/online/test/administrator/includes/application.php(130): JComponentHelper->renderComponent(‘com_movies’) #5 /towork/sites/online/test/administrator/index.php(67): JAdministrator->dispatch(‘com_movies’) #6 {main} thrown in /towork/sites/online/test/administrator/components/com_movies/bootstrapper.php on line 26
because it can be?
Thank you very much
Nicolás
July 28th, 2010 at 11:28
Hey,
It’s because Joomla is backward compatible with PHP4 and JLoader::load method is not explicitly declared as a static method. And when using construct ‘Class::staticMethod’, PHP assumes that it is declared as static.
You have two choices:
- mark that method as a static (my preference)
- or change the bootstrapper to use the old format, I guess it should be possible
When you see posting http://www.siimviikman.com/2010/03/01/building-joomla-component-with-doctrine-part-1/ then under ‘Creating a component structure’ you also see my note about that.