1. Overview

Previously, controllers (e.g., OrdersController) contained hundreds of actions and functions in a single file. This made the codebase difficult to read, maintain, and scale.

To improve structure, we have refactored the controller format.
Now, each controller:

Contains only core/major logic.

Delegates Actions and Functions to separate files inside their respective folders.

2. New Folder Structure
Controllers

├── OrdersController.php # Main controller file

├── Functions/
│ └── Orders/ # Folder for Orders-related Functions
│ ├── calculateInvoice.php
│ ├── validateOrder.php
│ └── …

└── Actions/
└── Orders/ # Folder for Orders-related Actions
├── createOrderAction.php
├── cancelOrderAction.php
└── …

2.1 Example Of  File Code

Standard Code For Controller :

<?php
class TestController extends CController
{
    /**
     * @var string the default layout for the views. Defaults to ‘column2’, meaning
     * using two-column layout. See ‘protected/views/layouts/column2.php’.
     */
    //public $layout = ‘application.views.layouts.main’;
    /**
     * @var CActiveRecord the currently loaded data model instance.
     */
    private $_model;
    protected function beforeAction($action)
    {
        // $this->layout = Yii::app()->settings->get(‘theme’, ‘layoutMain’, ‘application.views.layouts.main’);
        // $_SESSION[“theme-layoutMain-” . Yii::app()->controller->id] = $this->layout;
       // Dynamically include all action files from the OrdersController folder
 return true;
    }
    /**
     * @return array action filters
     */
    public function filters()
    {
        return array(
            ‘accessControl’, // perform access control for CRUD operations
        );
    }
    /**
     * Specifies the access control rules.
     * This method is used by the ‘accessControl’ filter.
     * @return array access control rules
     */
    public function accessRules()
    {
        return array(/*
array(‘allow’, // allow authenticated user to perform ‘create’ and ‘update’ actions
‘users’=>array(‘@’),
),
array(‘deny’ ),
array(‘allow’,  // allow all users to perform ‘index’ and ‘view’ actions
‘actions’=>array(‘index’,’view’),
‘users’=>array(‘*’),
),
array(‘allow’, // allow authenticated user to perform ‘create’ and ‘update’ actions
‘actions’=>array(‘create’,’update’),
‘users’=>array(‘@’),
),
array(‘allow’, // allow admin user to perform ‘admin’ and ‘delete’ actions
‘actions’=>array(‘admin’,’delete’),
‘users’=>array(‘admin’),
),
array(‘deny’,  // deny all users
‘users’=>array(‘*’),
),  .
*/
        );
    }
public function actions()
{
    $controllerName = str_replace(‘Controller’, ”, get_class($this));
    $cacheKey = strtolower($controllerName) . ‘_actions’;
    $cache = Yii::app()->cache;
    $actions = $cache->get($cacheKey);
    if ($actions === false) {
        $actions = array();
        $basePath = Yii::getPathOfAlias(‘application.controllers.actions.’ . $controllerName);
        foreach (glob($basePath . ‘/*.php’) as $file) {
            $className = basename($file, ‘.php’);
            $actionId  = str_replace(‘Action’, ”, $className);
            $actions[strtolower($actionId)] =
                ‘application.controllers.actions.’ . $controllerName . ‘.’ . $className;
        }
        $cache->set($cacheKey, $actions, 3600, new CDirectoryCacheDependency($basePath));
    }
    return $actions;
}
public function createAction($actionID)
{
    $actions = $this->actions();
    $lookupID = strtolower($actionID);
    if (isset($actions[$lookupID])) {
        $classPath = $actions[$lookupID];
        $className = substr($classPath, strrpos($classPath, ‘.’) + 1);
        Yii::import($classPath);
        return new CaseInsensitiveAction($this, $actionID, $className);
    }
    return parent::createAction($actionID);
}
public function functions()
{
    $controllerName = str_replace(‘Controller’, ”, get_class($this));
    $cacheKey = strtolower($controllerName) . ‘_functions’;
    $cache = Yii::app()->cache;
    $functions = $cache->get($cacheKey);
    if ($functions === false) {
        $functions = array();
        $basePath = Yii::getPathOfAlias(‘application.controllers.functions.’ . $controllerName);
        foreach (glob($basePath . ‘/*.php’) as $file) {
            $className = basename($file, ‘.php’);
            // Always store lowercase key
            $functions[strtolower($className)] =
                ‘application.controllers.functions.’ . $controllerName . ‘.’ . $className;
        }
        $cache->set($cacheKey, $functions, 3600, new CDirectoryCacheDependency($basePath));
    }
    return $functions;
}
public function runFunction($name, $params = array())
{
    static $loaded = array(); // memory-level cache
    $functions = $this->functions();
    $lookupName = strtolower($name);
    if (!isset($functions[$lookupName])) {
        throw new CException(“Function ‘$name’ not mapped.”);
    }
    $alias = $functions[$lookupName];
    $classPath = Yii::getPathOfAlias($alias) . ‘.php’;
    if (!file_exists($classPath)) {
        throw new CException(“Function file not found: $classPath”);
    }
    $cacheKey = ‘function_class_’ . md5($classPath);
    $fileModified = filemtime($classPath);
    $cached = Yii::app()->cache->get($cacheKey);
    if ($cached && $cached[‘mtime’] === $fileModified) {
        $className = $cached[‘class’];
        if (!class_exists($className, false)) {
            include_once($classPath);
        }
    } else {
        include_once($classPath);
        $className = basename($classPath, ‘.php’);
        Yii::app()->cache->set($cacheKey, array(‘class’ => $className, ‘mtime’ => $fileModified), 3600);
    }
    //  Instantiate and assign controller
    $obj = new $className();
    if (property_exists($obj, ‘controller’)) {
        $obj->controller = $this;  //assign controller reference
    }
    return call_user_func_array(array($obj, ‘run’), $params);
}
}

Sample Code For Action : 

<?php
/**
* Sample custom action for Yii 1.x
*
* Usage:
* – Create file: protected/controllers/actions/Orders/TestAction.php
* – Access via URL: index.php?r=orders/test
*/
class TestAction extends CAction
{
/**
* This method is executed when the action is called.
*/
public function run()
{
echo “This is a test action running from ” . get_class($this->controller);
}
}

Sample Code For Function :

<?php
/**
* Sample function class for Yii 1.x
*
* Usage:
* – File: protected/controllers/functions/Orders/Test.php
* – Call: $this->runFunction(‘test’, [$model]);
*/
class Test
{
public $controller; // auto assigned by runFunction()

/**
* Executes custom logic
* @param mixed $model
*/
public function run($model)
{
echo “This is a test function running with model: ” . get_class($model);
}
}

 

3. Naming Conventions

To keep files consistent and easily identifiable:

Action Files → must end with Action

Example: createOrderAction.php, cancelOrderAction.php

Function Files → must use the exact function name only (without adding “Function” at the end).

Example:

  • calculateInvoice.php (instead of calculateInvoiceFunction.php)

  • validateOrder.php (instead of validateOrderFunction.php)

4. Responsibilities
4.1 Controller File (e.g., OrdersController.php)

Contains only the core logic of the controller.

Delegates execution to Actions and Functions.

Works as an entry point for handling the request.

4.2 Functions Folder

Path: /Controllers/Functions/{ControllerName}

Contains helper or utility functions used by actions.

Functions should be reusable across multiple actions.

Example:

calculateInvoice.php

validateOrder.php

4.3 Actions Folder

Path: /Controllers/Actions/{ControllerName}

Contains individual actions previously written inside the controller.

Each action is now in a separate file for better clarity and maintainability.

Example:

createOrderAction.php

cancelOrderAction.php

5. Benefits of New Format

✅ Readability – Each action/function is in its own file.

✅ Maintainability – Avoids controllers with hundreds of methods.

✅ Reusability – Functions can be reused across different actions.

✅ Scalability – Easy to add new features without bloating controllers.

✅ Clear Separation of Concerns –

Controller = Core logic

Actions = Request handling

Functions = Reusable helpers

6. Example Workflow

Request comes to OrdersController.

Controller decides which Action to trigger.

That Action file may call one or more Functions for processing.

Response is returned through the controller.

Example Flow:

User places an order → createOrderAction.php executes.

Inside action → calls validateOrderFunction.php and calculateInvoiceFunction.php.

Controller finalizes response.

📌 Important Note:
Whenever you create a new Action or Function, always follow the naming convention:

[ActionName]Action.php

[FunctionName].php

⚡ This is the standardized structure that must be followed across all controllers going forward

Leave a Reply

Your email address will not be published. Required fields are marked *