Controllers


1Presentation

Controllers are PHP objects that must meet 4 criteria:

  • Inherit from the \Temma\Web\Controller object.
  • Be named using StudlyCase (first letter in uppercase, the rest in lowercase, each word starting with an uppercase, without underscore): ArticleViewer, ApplicationCms, MobileAppLibrary.
  • Be contained in a file whose name is exactly that of the object, suffixed by ".php".
  • Be accessible by the autoloader:
    • If the object is not in a namespace, the file should be placed in the controllers/ directory of the project.
    • If the object is in a namespace, the file must be placed:
      • in a tree structure inside the controllers/ directory or the lib/ directory of the project,
      • or in a tree structure placed inside a path declared by the includePaths configuration directive,
      • or in a location declared by the namespacePaths configuration directive.

Actions are the controller's public methods, and their names must start with a lowercase letter.


2Example

/** Users controller. */
class User extends \Temma\Web\Controller {
    /**
     * Root action.
     * Call for the "/user" URL.
     */
    public function index() {
        // ...
    }
    /**
     * Display action.
     * Called for URLs like "/user/show/123".
     * @param  int  $id  Identifier of the user that must be shown.
     */
    public function show(int $id) {
        // ...
    }
    /**
     * Default action.
     * Called when the requested action doesn't exist.
     * @param  int    $name    Name of the requested action.
     * @param  array  $params  List of possible parameters.
     */
    public function __call($name, $params) {
        // ...
    }
}
/** Articles controller. */
class Article extends \Temma\Web\Controller {
    /** Initialization, called before action. */
    public function __wakeup() {
        // ...
    }
    /** Finalization, called after action. */
    public function __sleep() {
        // ...
    }
    /**
     * Proxy action, called all the time.
     * @param  string $name    Name of the called action.
     * @param  array  $params  List of given parameters.
     */
    public function __proxy($name, $params) {
        // ...
    }
}

3Root, proxy and default

It is possible to define a root controller, a proxy controller and a default controller (see configuration):

  • The root controller (rootController directive) is called when no controller has been explicitly requested. It is used to define the controller that manages the home page of the site.
  • The proxy controller (proxyController directive) is always called, even if the called controller exists. It is used to bypass the framework completely, to create your own management of all URLs.
  • The default controller (defaultController directive) is called when the requested controller does not exist. It allows to define a controller which manages URLs that are not known in advance.

A controller can also contain a root action, a proxy action and a default action:

  • The root action is defined with the magic method __invoke().
    It is used when no action has been explicitly requested (for example the /myController).
  • The proxy action is defined with the method __proxy().
    If it exists, it is always called, even if the requested action exists.
  • The default action is defined using the magic method __call().
    It offers the possibility for a controller to manage URLs which are not known in advance, in addition to its specified actions.

For example, if the User object contains the __invoke(), show(), and __call() methods. It can be called with the following URLs:

  • http://mysite.com/user => use the root action (the __invoke() method)
  • http://mysite.com/user/show => use the show action (the show() method)
  • http://mysite.com/user/list => use the default action (the __call method)
  • http://mysite.com/user/add => use the default action (the __call method)

If this object contained a __proxy() method, it would always be called.

The __invoke() method takes no parameter.
The __proxy() and __call() methods expect two parameters:

  1. A string containing the name of the action that was requested.
  2. An array containing the list of parameters that should have been passed to the action.

The hijacking of PHP's magic methods is not a problem and does not lead to ambiguity. This provides a clear dividing line between "normal" actions and root/proxy/default actions.


4Initialization

A controller can contain the __wakeup() magic method (which takes no parameters).

It will be called automatically before the action is called, which can be used to initialize the controller. It is thus possible to initialize private attributes of the controller, by instantiating objects which can be used in all actions.

This method may not return anything, in which case execution continues normally. It can also return the same values as the action methods (see Execution flow), which can modify the execution of the action, post-plugins and view.


5Finalization

A controller may contain the __sleep() magic method (which takes no parameters).

It will be called automatically after the action has been called. It could be useful to free memory initialized by the controller.

This method may not return anything, in which case execution continues normally. It can also return the same values as the action methods (see Execution flow), and thus modify the execution of post-plugins and of the view.


6Management of template variables

Template variables are values that are defined by plugins and/or actions. They can be used directly in templates, but it is also the main way to transfer data between pre-plugins, the controller and post-plugins.

To create or modify a template variable:

$this['myVariable'] = 'my value';

To read a template variable:

$value = $this['myVariable'];

To know if a template variable exists, or if it is empty:

// is the variable defined?
if (isset($this['myVariable']))
    doSomething();

// is the variable empty?
if (empty($this['myOtherVariable']))
    doSomethingElse();

// variable not defined: use of a default value
$var = $this['myVariable'] ?? 'defaultValue';

// variable not defined or empty: default value
$var = $this['myVariable'] ?: 'defaultValue';

To destroy a template variable:

unset($this['myVariable']);

Important: template variables which name begins with an underscore ("_") are “private” to plugins and to the controller; they are not available in the templates.


7Template variables defined by Temma

The framework defines several template variables that are available in controllers (and in plugins):

  • $URL: Requested URL, from the root slash.
  • $CONTROLLER: Name of the requested controller.
  • $ACTION: Name of the requested action.

In addition to these variables, all data configured in the autoimport section of the configuration is automatically imported as template variables.


8Methods usable in controllers

Controllers provide specific methods that can be used in the action code.

$this->_template(string)
If we use the default view (\Temma\Views\Smarty), this method redefines the path to the template to use. If this method is not used to specify a template, Temma will try to use a file whose name is that of the action (with the ".tpl" suffix), placed in a directory whose name is that of the controller.
Returns the instance of the current object.

It is also possible to define the template to be used with the Template attribute.

$this->_redirect(string)
If you want to make a redirection (and therefore not use a view), this method defines the redirection URL.
Always returns the value self::EXEC_HALT (see the documentation on execution flow).

$this->_redirect301(string)
Same as the previous method, but performs a 301 (permanent) and not a 302 (temporary) redirect.
Always returns the value self::EXEC_HALT (see the documentation on execution flow).

$this->_httpError(int)
If an error is encountered that must be handled generically, this method allows you to specify the HTTP error code (4XX, 5XX).
Always returns the value self::EXEC_HALT (see the documentation on execution flow).

$this->_httpCode(int)
Allows you to specify the HTTP response code, such as the httpError() method, except that no error is raised and processing is not interrupted.
Always returns the value self::EXEC_HALT (see the documentation on execution flow).

$this->_view(string)
Used to define the view that will be used. By default, this is \Temma\Views\Smarty.
Returns the instance of the current object.

It is also possible to define the view to be used with the View attribute.

$this->_header(string)
Used to define an HTTP header to be sent by the view.

$this->_getHttpError()
Returns the HTTP error code that was defined with httpError(), or null if none was defined.

$this->_getHttpCode()
Returns the HTTP response code that has been defined with httpCode(), or null if none has been defined.

$this->_templatePrefix(string)
Allows to define a prefix to the loading path of template files.
Returns the instance of the current object.

It is also possible to define the template to be used with the Template attribute, by passing it the prefix parameter.

$this->_subProcess(string, string)
Used to perform an action from another controller. See below.
Returns the execution status of the sub-controller.


9Controller attributes

$this->_loader
Dependency injection object, which allows you to retrieve other objects.
More information on the dependency injection documentation.

$this->_session
$this->_loader->session
Unless the configuration has planned to disable session management, this attribute contains an instance of \Temma\Base\Session.
More information on session documentation.

$this->_config
$this->_loader->config
This attribute contains an instance of \Temma\Web\Config. The controller can thus access (read only) both the parameters of the Temma application and the extended parameters.
More information on the configuration documentation and the Config object documentation.

$this->_request
$this->_loader->request
This attribute contains an instance of type \Temma\Web\Request. This makes it possible to retrieve, or even modify, the different components of the request.
More information on the Request object documentation.

$this->_response
$this->_loader->response
This attribute contains an instance of \Temma\Web\Response. This allows you to control some response parameters.
More information on the Response object documentation.

$this->_dao
If requested by the controller, Temma will automatically instantiate a DAO object to facilitate communication with the database.
More information on the model documentation.

$this->_temmaAutoDao
This attribute is different from the others. It is not positioned by the framework. It is up to you to use it when creating the controller, to ask Temma to automatically create the DAO object that facilitates access to the database.
More information on the model documentation.


10Access to data sources

A controller can access data sources in two different ways: either through the dependency injection component, or directly as if they were attributes of the controller.

Imagine that the etc/temma.php file defines data sources named db (MySQL database), ndb (Redis database) and cache (Memcached server).

Access as attributes:

$db = $this->db;
$ndb = $this->ndb;
$cache = $this->cache;

Access via the dependency injection component:

// object-oriented access
$db = $this->_loader->dataSources->db;
$ndb = $this->_loader->dataSources->ndb;
$cache = $this->_loader->dataSources->cache;

// array-like access
$db = $this->_loader->dataSources['db'];
$ndb = $this->_loader->dataSources['ndb'];
$cache = $this->_loader->dataSources['cache'];

11Sub-processing

In an action, we can very easily call the execution of another action of the same controller:

class Article extends \Temma\Web\Controller {
    // we don't want to display the list of articles,
    // we just want to display the first article
    public function list() {
        // call the show() action
        $this->show(1);

        // we get the article in the template variable
        // created by the show() action
        $article = $this['article'];

        // we create a new template variable
        // containing a list of a single item
        $this['articles'] = [$article];
    }

    // action used to display an article
    public function show(int $id) {
        $this['article'] = $this->_loader->ArticleDao->get($id);
    }
}

A controller can also transmit the execution of the current request to another controller. To do this, just use the subProcess() method. It is even possible to modify the request in advance, to adapt it to the sub-controller:

class HomepageController extends \Temma\Web\Controller {
    public function index() {
        // we modify the first parameter of the action
        $this->_loader->request->setParam(0, 'display');

        // we call the "publish" action of the "User" controller
        $this->subProcess('User', 'publish');

        // we can retrieve a template variable
        // defined by the sub-controller...
        $name = $this['name'];

        // ...then conditionally overwrite this value
        if (strlen($name) < 3)
            $this['name'] = 'default name';
    }
}

In this example, the controller that manages the home page of the site performs a subquery equivalent to a connection to the /user/publish/display URL and then performs conditional processing on the generated data.