Development - Controllers and Actions


General Info

The "Controller" layer, as it defined in MVC represents two thinner layers in a framework: controllers and actions. (The "Controller" layer also includes the main dispatcher script and the request routing system, though they are not directly involved in application development.)

Controller is just a part of web application which is used to handle user requests. It is where an application "determines", which blocks the resulting web page should consist of. In complex applications, for better source code readability, it is reasonable to split the page generation logic into several parts. Each such part (responsible for generation of a single informational block on a page) is called an action.

Example of controllers:
UserController, AccountController, PageController, etc.
Ussually, Controller has more than one Action and may implement also some internal logic, that is not related to Actions. Example of acceptable controller class:
<?php
class AccountController extends CController
{
    // Default constructor
    public function __construct()
    {
        parent::__construct();
    }
    
    // Used to define access rules to controller
    // This method should be overridden
    protected function _accessRules()
    {
        return array(
            //array('allow', 'actions' => array('*'), 'ips' => array('127.0.0.1')),
            //array('deny', 'actions' => array('*'), 'ips' => array('127.0.0.2'))
        );
    }
    
    // Default action
    public function indexAction()
    {
        $this->redirect('account/manage');
    }

    // Manage action
    public function manageAction()
    {
        ...
    }

    // Delete action
    public function deleteAction()
    {
        ...
    }

    // Protected method (not action)
    protected function _protectedMethod()
    {
        ...
    }

    // Private method (not action)
    private function _privateMethod()
    {
        ...
    }    
}

Action is a part of an application that responsible for generating a certain portion of the resulting HTML code. It may be either entire web page or just a part of it. An action is always called from within a controller. When processing a user request, a controller determines which actions must be called for generation of a specific web page and proceeds to calling them one by one. Very often, when there is only one dynamically generated fragment on a page (i.e. the result of execution of a certain action), a controller has only one task: to call the single available action. The framework offers the so-called default controller which is executed, if no specific controller for a user request has been found, yet the action class matching the routing system instructions exists.

Example of actions:
Users/Edit, Pages/View, Pages/Edit, etc.

Implementation of the Action logic

The general operating logic of most simple actions works according to the following scheme: retrieve data from the model and pass them to the template engine to be displayed in the user browser.

Some types of HTTP requests may contain instructions for execution of actions which are not expected to generate any useful content (web page). Execution of such actions may result in redirecting a user to another URL; e.g., after deletion of a record or when data submitted via a web form have been saved on the server. In such cases the entire logic is implemented in a controller and no individual actions are necessary.

Receiving parameters in Action

Since version 0.1.0, ApPHP Framework supports automatic action parameter binding. That means a controller action method can define named parameters which values will be automatically populated from global $_GET array.

To demostrate how this works, let's assume that we need to create some action for PagesController. The action requires two parameters: We may use the following code for the purpose of retrieving the needed parameter values from $_GET:
<?php
class PostsController extends CController
{
    public function actionCreate()
    {
        $category = '';
        if(isset($_GET['category'])){
            $category = (int)$_GET['category'];
        }
		
        $language = '';
        if(isset($_GET['language'])){
            $language = $_GET['language'];
        }
		
        // Other code starts here ...
    }
}
We can also use framework Request component to get the code more readable:
<?php
class PostsController extends CController
{
    public function actionCreate()
    {
        $category = A::app()->getRequest()->getQuery('category');
        $language = A::app()->getRequest()->getQuery('language');
		
        // Short format
        $category = A::app()->request()->post('category');
        $language = A::app()->request()->get('language');
		
        // Other code starts here ...
    }
}
Now using the action parameter feature, we can achieve this task in a more pleasant way:
<?php
class PostsController extends CController
{
    /**
    * URL: post/action/category/1/language/en
    */
    public function actionCreate($category, $language='en')
    {
        $category = (int)$category;
		
        // Other code starts here ...
    }
}
Notice that we add two parameters to the action method actionCreate. The recommended way is to use the exactly names for parameters in $_GET. The $language parameter takes a default value en in case the request does not include such a parameter. Because $category does not have a default value, if the request does not include a category parameter, so nothing will be passed to this action.

Remember: you may define/re-define URL (incliding parameters) with routing feature of application and modules.


Rendering in Controllers - passing data to the View (template engine)

When generating the HTML code of the resulting web page, the view (template engine) processes the data received from the action. Data should be passed to the template engine using the following syntax:
$this->_view->text = 'some text here...';
/* sample 1 - full syntax */
$this->_view->render('controller/view');
/* sample 2 - short syntax, missing controller means use current controller name */
$this->_view->render('view'); 
/* sample 3 partial rendering - for AJAX calls (2nd parameter $isPartial = true) */
$this->_view->render('view', true); 
/* sample 4 partial rendering with returned content - for some specific methods (2nd parameter $isPartial = true, 3rd parameter $return = true) */
$result = $this->_view->render('view', true, true);
Read more...

Example of an action class showing how data received from the model are passed to the view file:
<?php
class AccountController extends CController
{
    public function indexAction()
    {
        // Create a model instance to retrieve data from the DB
        $model = new Accounts();
        
        // Retrieve all records from DB
        $records = $model->fetchAll();
        
        // Assign data to the view variables
        $this->_view->records = $records;
        $this->_view->title = 'Account Management';
        
        // Pass prepared data to the view file
        $this->_view->render('accounts/index');
    }

    public function viewAction()
    {
        // Example of working with active record
        $model = Accounts::model()->findByPk(10);
        $model->fieldA = 'new name';
        $model->fieldB = 'new type';    
        $model->save();

        // Pass prepared data to the view file
        $this->_view->render('accounts/view');
    }
}    

Redirecting to another URL

Browser redirection to a different URL is carried out in controller source code using the following syntax:
/* sample 1 */
$this->redirect('controller/action');
/* sample 2 */
$this->redirect('controller/action/param1/value1');
$this->redirect('controller/action/?sort_by=code&sort_dir=asc&page=1');
/* sample 3 - short syntax, missing controller means use current controller name */
$this->redirect('index'); 
/* sample 4 - full path */
$this->redirect('https://www.apphp.com', true); 
/* sample 5 - full path with redirect code */
$this->redirect('https://www.apphp.com', true, 301);