Now it’s time to add some back-end administration functionality to our little component. In general it is mostly same as in front-end: controllers, views, models. Backend lists also include functionality for sorting and paging so I created a list helper for that to decouple it from the rest of the code.

I created two separate controllers for movies and genres, because controllers have more tasks in the back-end and it’s not very good idea to stuff all that together in a single controller. Each controller contains methods for viewing, editing and saving. When updating database, I’ll use transactions to maintain consistency. Doctrine uses application level transactions and it also supports nesting. So it’s one more plus point for using Doctrine over Joomla DB layer. As far as I know, Joomla default DB layer doesn’t support transactions at all. Here is part of the movies controller:

public function viewMovies()
{
	$listHelper = new ListHelper($this, 'title');

	// Doctrine extracts JosMvMovie objects with all genres it has from this query
	$query = Doctrine_Query::create()
			->select('m.*, g.*')
			->from('JosMvMovie m')
			->leftJoin('m.Genres g')
			->addOrderBy($listHelper->getOrderBy().' '.$listHelper->getOrderByDir())
			->offset($listHelper->getRowIndex())
			->limit($listHelper->getPageSize());
	$items = $query->execute();
	$totalCount = Doctrine_Query::create()
				->select('count(m.movie_id) as count')
				->from('JosMvMovie m')
				->execute();

	$view = $this->getView('MoviesList');
	$view->setViewHelper($listHelper);
	$view->setItems($items);
	$view->setItemsTotalCount($totalCount[0]->count);
	$view->display();
}

public function editMovie()
{
	$movie = $this->getItem();
	// Used by the genres selectbox
	$genres = Doctrine_Query::create()->from('JosMvGenre')->addOrderBy('name ASC')->execute();

	$view = $this->getView('MovieDetails');
	$view->setViewHelper(new ViewHelper($this));
	$view->setLayout('form');
	$view->setGenres($genres->getData());
	$view->setItem($movie);
	$view->display();
}

public function saveMovie()
{
	Doctrine_Manager::connection()->beginTransaction();
	try
	{
		$movie = $this->getItem();
		$movie->title = JRequest::getString('title');
		$movie->imdb_link = JRequest::getString('imdb_link');
		$movie->plot = JRequest::getString('plot');
		$this->updateGenres($movie);
		$movie->save();
		Doctrine_Manager::connection()->commit();

		$this->getApplication()->enqueueMessage(JText::_('Saved'));
		$this->redirectAfterSave($movie);
	}
	catch(Exception $ex)
	{
		JError::raiseWarning(0, JText::_('Save failed').': '.$ex->getMessage());
		Doctrine_Manager::connection()->rollback();
	}
}

ListHelper you saw, is basically a wrapper for using applications state with some magic strings. One of it’s methods looks like this:

public function getOrderBy()
{
	return $this->getController()->getApplication()->getUserStateFromRequest(
		$this->getController()->getName().'.list.filter_order', 'filter_order', $this->_defaultOrderBy, 'cmd');
}

I also created a bunch of base classes for controllers and views, because I don’t like repetitive code that Joomla has all over the place. And in general it is a good idea to write your own base classes between your concrete classes and framework base classes when you have more than a few views. It‘s a good place to put component wide logic that is applied to all views. I have also encapsulated usage of Joomla global variables like $mainframe (which is actually of type JApplication) and $option into base classes. I tried to avoid usage of magic strings and global variables by encapsulating them into methods/properties, making the usage more explicit.

Views are as lightweight as they were in the front-end. They contain only setup of the JToolBar and setters for data which are used by the controllers, nothing more.

<form method="post" name="adminForm" id="adminForm">
<div class="col width-100">
<fieldset class="adminform">
	<legend><?php echo JText::_('Details'); ?></legend>
	<table class="admintable">
	<tr>
		<td width="100" align="right" class="key">
			<label for="name"><?php echo JText::_('Name'); ?>:</label>
		</td>
		<td>
			<input class="text_area" type="text" name="name" id="name" size="32" maxlength="250" value="<?php echo $this->item->name; ?>" />
		</td>
	</tr>
	</table>
</fieldset>
</div>
<?php echo $this->createMetaInputs(); ?>
</form>

In my view base class I have a method createMetaInputs() to create hidden fields, common to all views in Joomla. I also created helper method createHeaderLink(..) used by the list views. DRY.

I think it’s pretty much it. Final result looks much more cleaner and simpler than by using ‘”Joomla’s way”. Final component source code can be downloaded here.

Conclusion

Joomla don’t dictate how you should build your component. This is in some way bad (when looking the source code of the most of the components built by the community), but it’s also good. It allows us to use different (and sometimes better) implementation of the DB layer (and why not other layers too). It also provides pretty good base functionality, you just have to use it wisely.

  • Twitter
  • Facebook
  • Technorati Favorites
  • Share/Bookmark