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.

  • Twitter
  • Facebook
  • Technorati Favorites
  • Share/Bookmark