Skip to content

Instantly share code, notes, and snippets.

@LouisLandry
Created June 8, 2012 16:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LouisLandry/2896715 to your computer and use it in GitHub Desktop.
Save LouisLandry/2896715 to your computer and use it in GitHub Desktop.
Nested Task/Sub-task Example
<?php
/*
* Two patterns: one for with and one without vcard support.
*
* 'my/users/:user_id' => 'MyControllerUsers'
* 'my/users/:user_id/vcard' => 'MyControllerUsersVcard'
*/
class MyControllerUsersCreate extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersDelete extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersGet extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersUpdate extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersVcardCreate extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersVcardDelete extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersVcardGet extends JControllerBase
{
public function execute()
{
}
}
class MyControllerUsersVcardUpdate extends JControllerBase
{
public function execute()
{
}
}
<?php
/*
* One pattern: Controller decides if it is using vcard based on route variable.
*
* 'my/users/:user_id/:format' => 'MyControllerUsers'
*/
class MyControllerUsersCreate extends MyControllerUsers
{
public function execute()
{
}
}
class MyControllerUsersDelete extends MyControllerUsers
{
public function execute()
{
}
}
class MyControllerUsersGet extends MyControllerUsers
{
public function execute()
{
}
}
class MyControllerUsersUpdate extends MyControllerUsers
{
public function execute()
{
}
}
abstract class MyControllerUsers extends JControllerBase
{
protected function fetchVcardModel()
{
}
protected function fetchStandardModel()
{
}
}
<?php
/*
* One pattern: Singular controller with subtasks mapped.
*
* 'my/users/:user_id/:format' => 'MyControllerUsers'
*/
class MyControllerUsers extends JControllerBase
{
public function create()
{
}
public function delete()
{
}
public function get()
{
}
public function update()
{
}
protected function fetchVcardModel()
{
}
protected function fetchStandardModel()
{
}
}
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* RESTful Web application router class for the Joomla Platform. Monolithic controller style.
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.3
*/
class JApplicationWebRouterMonolithic extends JApplicationWebRouterRest
{
/**
* Find and execute the appropriate controller based on a given route.
*
* @param string $route The route string for which to find and execute a controller.
*
* @return void
*
* @since 12.3
* @throws InvalidArgumentException
* @throws RuntimeException
*/
public function execute($route)
{
// Get the controller name based on the route patterns and requested route.
$name = $this->parseRoute($route);
// Get the controller object by name.
$controller = $this->fetchController($name);
// Validate that we have a map to handle the given HTTP method.
if (!isset($this->suffixMap[$this->input->getMethod()]))
{
throw new RuntimeException(sprintf('Unable to support the HTTP method `%s`.', $this->input->getMethod()), 404);
}
// Get the method to execute on the controller.
$method = strtolower($this->suffixMap[$this->input->getMethod()]);
// Execute the controller.
$controller->$method();
}
/**
* Get the controller class suffix string.
*
* @return string
*
* @since 12.3
* @throws RuntimeException
*/
protected function fetchControllerSuffix()
{
return '';
}
}

So I have three different ways of solving the problem here.

multiple.patterns.php

Here we have one pattern for capturing VCard and one for capturing standard requests. In these controllers it is very clear exactly what they do by name and they do exactly one thing. It's very simple to chain them, nest them, etc because ultimately the name of the class tells you exactly what they do. It is a little more explicit code, but not a whole lot because you don't end up with many if (vcard) { do something } else { do something else } paths in the code base. These also can be easier to test because of that very reason.

one.pattern.atomic.php

In this example I'm creating an abstract base class MyControllerUsers from which all of my action based controllers inherit. The base class simply has helper methods to either get a VCard model or a standard one. It could just as easily have the same thing for VCard/standard views. That's all up to how you design your system.

one.pattern.monolithic.php

For the last example I've also included an extension to the RESTful router that shows how simple it would be if this is what your application requires to achieve what you are looking for. Perhaps it could be done cleaner, but it is incredibly simple.

In the controller class I've added a couple of protected helper methods similar to what we put in MyControllerUsers in the one.pattern.atomic example above. If you look at one.pattern.atomic and one.pattern.monolithic there is really no substantive difference in code, but the organization of that code is (IMO) better in the atomic example. From a maintenance point of view it is always better touch as few files as possible when fixing issues or enhancing things. That makes it harder to make an accidental mistake for one reason or another. Additionally we still get a nice amount of that clarity in the controller names about exactly what this controller is doing, makes them easier to chain, nest, test, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment