Controllers

What is a controller?

Controllers are classes that can be reached through the URL and take care of handling the request. A controller calls models and other classes to fetch the information. Finally, it will pass everything to a view for output. If a URL like www.yoursite.com/example/index is requested, the first segment ("example") will be the controller that is called and the second segment ("index") will be the method of that controller that is called.

Creating a controller

In FuelPHP, Controllers are put in the fuel/app/classes/controller directory, and are prefixed with "Controller_". Optionally, they should extend the Controller class for the full feature set. Below is an example of the controller "example":

class Controller_Example extends Controller
{

	public function action_index()
	{
		$data['css'] = Asset::css(array('reset.css','960.css','main.css'));
		return Response::forge(View::forge('welcome/index'));
	}
}

Methods that can be requested through the URL are prefixed with "action_". This means that you're not limited by PHP's constructs on which name you might use (example: method "list" isn't allowed, "action_list" is no problem). But this also means you can give your controller public methods that can be used from other classes but are not routable.

HTTP method prefixed action.

It's possible to route to HTTP method prefixed actions. Here is an example:

class Controller_Example extends Controller
{
	public function get_index()
	{
		// This will be called when the HTTP method is GET.
	}

	public function post_index()
	{
		// This will be called when the HTTP method is POST.
	}
}

Controllers in Sub-Directories

You can also put a controller in a subdirectory, like fuel/app/classes/controller/subdir/test.php. In this case, the controller must include the dirname in the classname like this: Controller_Subdir_Test.

Unlimited nested sub-directories are supported, so fuel/app/classes/controller/subdir1/subdir2/subdir3/test.php. would have a class name of Controller_Subdir1_Subdir2_Subdir3_Test.

Namespacing controllers

As mentioned in the introduction by default controllers are created in the app/classes/controller folder, and are prefixed with Controller_. This prefix is defined in your app/config/config.php configuration file (and if not this is the default), but can be changed if you want to namespace your controllers, or if you want to move them to a different folder structure.

Lets move the example given above to the Controller namespace. You tell FuelPHP that you've done this by setting the config setting controller_prefix from 'Controller_' to 'Controller\\' in your app's config.php file.

namespace Controller;

class Example extends \Controller
{

	public function action_index()
	{
		$data['css'] = Asset::css(array('reset.css','960.css','main.css'));
		return Response::forge(View::forge('welcome/index'));
	}
}

Note that now that your controller lives in a namespace, you have to prefix "Controller" with a backslash, since it needs to be loaded from the global namespace.

Using more parameters from the URL

Let say we also have the following method in our Controller_Example:

public function action_name_to_upper($name_1, $name_2)
{
	$data['name_1'] = strtoupper($name_1);
	$data['name_2'] = strtoupper($name_2);
	return View::forge('test/name_to_upper', $data);
}

If we call this method using: www.yoursite.com/example/name_to_upper/fuel/php, it will return the view test/name_to_upper. "FUEL" and "PHP" will be passed along as values $name_1 and $name_2 in the $data array.

Returning the result

Ideally, a controller action must return a Response object. Optionally you can specify special HTTP headers, or a custom HTTP status code (a code other than "200 OK"). If you don't return a response object, the default after() method will wrap the return value of the action in a Response object for you.

If your controller extends one of the base controllers, your action can also return any value that can be cast to string, like for example a View object. The after() method of the base controller will convert it into a Response object for you.

If your controller does not extend one of the base controllers, and you want to use this feature, your controller must contain it's own after() method that will accept the actions return value, and has to wrap it into a Reponse object which it must return.

Special controller methods

Don't overwrite the class constructor __construct(), use before() instead. Unless you have studied the base Controller from the Core first and understand how it must be extended not to break Fuel.

action_index()

This method will be called if the controller is called without a second parameter. In the above example www.yoursite.com/example/index will be the same as www.yoursite.com/example.

before()

The before() method is used as a general pre-method prepping method, to execute code required in every controller method called. This method will be executed before the method from the URL is called on your controller. It will not be called if that method turns out not to exist. You should not use this method for routing decisions. If you need that, use the router method instead.

after($response)

This method will be executed after the method from the URL was called successfully, this will not be called if the method turns out not to exist. $response parameter is required. The after() method MUST return a Response object.

If the after() method has to construct a Response object, it can use the response_status property of the controller to set the HTTP status of the response. By default, this property contains "200" (OK).

router($method, $params)

This method will take over the internal routing of the controller. Once the controller is loaded, the router() method will be called and use the $method that is being passed in, instead of the default method. It will also pass in $params, in an array, to that $method. The before() and after() methods will still work as expected.

Extending other controllers

Thanks to the autoloader, you can extend other controllers without writing anything more than its name in the class definition:

class Controller_Example extends Controller_Welcome
{

	// your methods

}

This may sound strange at first, but extending controllers allows you to share methods and create base controllers really easily.

Creating Base Controllers

A base controller is a shared controller, like Controller_Public or Controller_Admin and are used to share logic between groups of controllers. For example, the Controller_Admin controller could contain your login/logout actions and maybe a dashboard, but it could also contain a before() method that checks to see if the user is logged in as an admin. Then all other controllers in your admin panel will extend this and automatically be secured.

class Controller_Admin extends Controller
{

	public function before()
	{
		// check for admin
	}

	// your methods

	public function action_index()
	{
		// load the dashboard
	}

	public function action_login()
	{
		// log in the user
	}
}

That code will go in fuel/app/classes/controller/admin.php and all of your other controllers should go in fuel/app/classes/controller/admin/, like this one:

class Controller_Admin_User extends Controller_Admin
{

	public function action_index()
	{
		// overrides the dashboard with the user index listing
	}

	public function action_edit($id)
	{
		// edit users
	}
}