Controller Handler
The controller handler provides the "C" part of the MVC application paradigm. Controller actions are the entry point to your application. The router provides the environment information through the controller class.
Controllers must follow the naming convention "${NAME}Controller"
and actions
must follow the naming convention `"${NAME}Action". Actions can optionally
accept an array as an argument which will be populated with the remaining
path components.
SnappyRouter also integrates the popular Twig templating engine to provide the "V" part of MVC, with a natural convention mapping template files to controller actions.
An example controller:
<?php
namespace Vendor\MyNamespace\Controllers;
use Vectorface\SnappyRouter\Controller\AbstractController;
class ExampleController extends AbstractController
{
public function indexAction($routeParams)
{
}
public function loginAction($routeParams)
{
if ($this->request->isPost()) {
// retrieve the username from the form trimmed
$username = $this->request->getPost('username', '', 'trim');
// retrieve the password from the form
$password = $this->request->getPost('password', '');
if (!empty($username) && !empty($password)) {
// retrieve the authentication system from the DI layer
$authService = $this->get('authenticationService');
if ($authService->isValidCredentials($username, $password)) {
// login was successful so redirect the user
header('location: /some/location');
exit();
} else {
// pass the login errors back to the template
return array(
'loginErrors' => $authService->getErrors()
);
}
}
// pass the login errors back to the template
return array(
'loginErrors' => array('Missing username or password.')
);
}
}
}
and an example configuration for this handler:
<?php
use Vectorface\SnappyRouter\Config\Config;
use Vectorface\SnappyRouter\Handler\ControllerHandler;
$config = new Config(array(
Config::KEY_HANDLERS => array(
'MvcHandler' => array(
Config::KEY_CLASS => 'Vectorface\\SnappyRouter\\Handler\\ControllerHandler',
Config::KEY_OPTIONS => array(
Config::KEY_NAMESPACES => array(
'Vendor\\MyNamespace\\Controllers'
),
ControllerHandler::KEY_VIEWS => array(
ControllerHandler::KEY_VIEWS_PATH => '/home/user/project/views'
)
)
)
)
));
$router = new Vectorface\SnappyRouter\SnappyRouter($config);
echo $router->handleRoute();
Controller Routing
The controller handler uses a simple convention for mapping routes to controllers and actions.
/(optional/base/path/)${controller}/${action}/(param1/)(param2)
After stripping the base path prefix, the first path component is assumed to be the controller and second path component is assumed to be the action. Any subsequent path components are assumed to be route parameters to be passed to the controller action.
- If the action is missing in the path the router will fall back to
indexAction
by default. - If the controller is missing in the path, the router will fall back to
IndexController
.
The base path is specified in the handler options like such:
...
Config::KEY_OPTIONS => array(
ControllerHandler::KEY_BASE_PATH => '/my/base/path';
...
),
...
Specifying Controllers in the Configuration
There are three ways to specify the how to resolve a controller in the
configuration. Controllers are listed in the options
key within the handler.
Explicit List of Controllers
The list of controllers can be explicitly given as a key/value pair. The key for
the controller must match the convention "${NAME}Controller"
and the value
must be a valid PHP class or an array with specific fields (depending on whether
your controllers are namespaced).
Example for namespaced controllers:
...
Config::KEY_OPTIONS => array(
Config::KEY_CONTROLLERS => array(
'ExampleController' => 'Vendor\\MyNamespace\\Controllers\\ExampleController',
'AnotherController' => 'Vendor\\MyNamespace\\Controllers\\AnotherController',
...
)
),
...
Example for non namespaced controllers:
...
Config::KEY_OPTIONS => array(
Config::KEY_CONTROLLERS => array(
'ExampleController' => array(
Config::KEY_CLASS => '\ExampleController',
Config::KEY_FILE => '/home/user/project/controllers/ExampleController.php'
)
)
),
...
Registering a list of Controller Namespaces
If your code is namespaced, you can register a list of namespaces for SnappyRouter to use to autodetect the appropriate controller class.
...
Config::KEY_OPTIONS => array(
Config::KEY_NAMESPACES => array(
'Vendor\\MyNamespace\\Controllers',
'Vendor\\AnotherNamespace\\Controllers',
...
)
),
...
The namespaces will be scanned in the order listed in the array.
Registering a Folder of Controller PHP Files
If your code is not namespaced, you can give SnappyRouter a list of folders
to check (recursively) for a PHP file matching ${NAME}Controller.php
.
...
Config::KEY_OPTIONS => array(
Config::KEY_FOLDERS => array(
'/home/user/project/app/controllers',
'/home/user/project/app/moreControllers',
...
)
),
...
Getting Request Details
The controller handler automatically registers request information in the
$request
property of the class
Vectorface\SnappyRouter\Controller\AbstractController
. As long as your
controller subclasses this class, you can access the current request through
$this->request
. The request object is an instance of
Vectorface\SnappyRouter\Request\HttpRequest
.
Example demonstrating how to check if the request made was a POST
request
and to get the variables from the form.
<?php
...
public function myAction($routeParams)
{
if (false === $this->request->isPost()) {
throw new MethodNotAllowedException(
'Only POST requests allowed.',
array('POST')
);
}
// get the object ID typecasted to an integer
// if not found, we get back a default value of 0
$objectId = max(0, $this->request->getPost('id', 0, 'int'));
if (0 === $objectId) {
throw new Exception('Invalid Object ID');
}
// handle stuff
...
return array('MyObject' => $object');
}
...
Retrieving and Filtering Inputs
The request object can retrieve the values of query and post parameters and provides the option to specify default values if the parameters are not found. Furthermore, you can specify a convenient list of filters to apply against the value.
Example retrieving a simple field id
from both the query and post parameters:
...
// retrieve the ID from the query parameters
$id = $this->request->getQuery('id');
// retrieve the ID from the post parameters
$id = $this->request->getPost('id');
...
Example retrieving the field id
from the query parameters and specifying a
default value to return if the parameter is missing:
...
// retrieve the ID from the query parameters or use 0
$id = $this->request->getQuery('id', 0);
...
Example retrieving the field id
and applying the int
filter to type cast
the value to an integer:
...
// retrieve the ID from the query parameters and ensure it is a positive
// integer
$id = max(0, $this->request->getQuery('id', 0, 'int'));
// alternatively the filters can be specified as an array
$id = max(0, $this->request->getQuery('id', 0, array('int')));
...
Filters can also be combined as an array. An example of a string that is trimmed and fully converted to lower case.
...
// usernames are trimmed and stored in lower case for case insensitivity
$username = $this->getPost('username', '', array('trim', 'lower'));
...
List of Valid Filters
int
- Type casts the value as an integer.float
- Type casts the value as a float.trim
- Trims leading and trailing whitespace from the string.lower
- Converts the entire string to lower case.upper
- Converts the entire string to upper case.squeeze
- Condense a multi-line string by removing any empty lines or lines containing only whitespace.
Additional Route Parameters
Any additional route parameters will be passed as an array to the action method.
For example, a route path like:
/example/demo/1234/perform
will be routed to the ExampleController
and demoAction
method with the
argument array('1234', 'perform')
.
Integration with Twig
For convenience, SnappyRouter provides optional integration with the Twig template engine. A simple convention provides an automatic mapping between the controller action and the view. Manual view rendering is also made easy.
Convention-based Twig Integration
For the convention-based method, simply provide a Twig view
folder in the
view configuration. Any options specified in the view will be handed off to
the Twig_Environment
so you can also specify additional environment
options such as cache
, debug
, auto_reload
, etc. See the
Twig documentation
for more details.
<?php
use Vectorface\SnappyRouter\Config\Config;
use Vectorface\SnappyRouter\Handler\ControllerHandler;
$config = new Config(array(
Config::KEY_HANDLERS => array(
'MyHandler' => array(
Config::KEY_CLASS => 'Vectorface\\SnappyRouter\\Handler\\ControllerHandler',
Config::KEY_OPTIONS => array(
ControllerHandler::KEY_VIEWS => array(
ControllerHandler::KEY_VIEWS_PATH => '/path/to/views/folder'
)
)
)
)
));
$router = new Vectorface\SnappyRouter\SnappyRouter($config);
echo $router->handleRoute();
The view folder must follow the convention:
/path/to/views/folder
/controller1
/action1.twig
/action2.twig
/controller2
/action1.twig
/action2.twig
For example, given the controller ExampleController
and the action
helloworldAction
we may have a view structure like:
/home/user/project
/views
/example
helloworld.twig
Note that the filename of your Twig template must match ${ACTION}.twig
.
To pass variables to the Twig template, you can add additional fields to the
associative array $this->viewContext
inside your controller action.
For example:
...
public function loginAction()
{
$this->viewContext['username'] = 'Fred';
}
...
and your login.twig
template can use the variable username
:
Hello {{ username|e }}.
Alternatively, if a controller action returns an associative array, the keys and values get bound to variables in the view. For example:
...
public function loginAction()
{
return array('username' => 'Fred');
}
...
Using Twig Manually
The class Vectorface\SnappyRouter\Controller\AbstractController
provides an
easy to use method (renderView
) for rendering Twig templates directly. The
method takes an array of a variables to pass to the view and a string indicating
which template to render.
For example:
...
public function loginAction()
{
return $this->renderView(
array('username' => 'Fred'),
'login.twig'
);
}
...
Note that Twig always requires a base views folder in the configuration and all templates are specified relative to the base folder.