Front controller best way to link parameters to application logic?

435 views Asked by At

I am building an app using Front Controller design pattern and there is just one page index.php through which all user requests pass as parameters (versus different pages/controllers in regular design).

How can I connect these parameters to application logic?

e.g. I have two different actions:

index.php?action=userLogin&username=admin&password=qwerty //process user login

index.php?action=displayUsersTable //show registered users

Currently I have an array with all actions the system accepts (along with expected arguments) and I compare action param from URL to the key of this array and then check the required arguments for this action.

//1 = optional, 2=required
$systemActions = [
      "userLogin" => [
            "login" => 2,
            "password" => 2
                     ],
      "displayUsersTable" => []
                 ];

Obviously this going to become a monster array as the system grows.

Is there better approach to bind parameters sent to front controller to system actions?

1

There are 1 answers

0
Robbie On BEST ANSWER

As the code is "fixed" (i.e. not driven from a database) then there is no need to pump into an array (and all the processing/memory overhead that it requires. So yes, it can be improved.

But there are many options depending on how much the project will grow.

Simplest

The simplest would be simple "if" statements, or a switch. I'd start there to keep it simple.

More Complex

You say other projects have different pages / controllers - but there is a reason. And as you're asking for improvements, especially if you're expecting the project to grow to such as extent that you're looking for optimizations, then you really should consider these reasons (and split into files).

At the other end of the scale, you can split all the calls into files/classes and auto-load the files/classes.

This way you only execute the code you need (smaller file sizes), is very modular and easy to work on collaboratively. And if you add a new action, you don't need to modify the index or array - you only modify the action file you're working on.

Example (vastly simplified from a project I'm currently working on with this approach):

1) Create a "baseAction" base class the all actions will extend from. You can add common features such as cleaning/pre-processing parameters, logging, validating headers etc.

abstract class baseAction {
    protected $aExpectedParams = [];
    protected $aParams = [];
    protected $validParams = true;

    function __construct() {
        foreach (self::$aExpectedParams as $name=>$aParam) {
            if (isset($_GET[$name]))
                if ($aParam['type'] == 'string') {
                    self::$aParams[$name] = $_GET[$name];
                } elseif ($aParam['type'] == 'int') {
                    self::$aParams[$name] = (int)$_GET[$name];
                }
            } elseif ($aParam['required']) {
                self::$validParams = false;                
            }
        }
    }

    // This is the called function
    abstract function execute();

}

2) Create the "action" classes, by extending the base Action. Save these in individual files (so others can collaborate on the project without interfering).

// put in 'actions/userLogin.php
class userLogin extends baseAction {
    protected $aExpectedParams = [
            'login' => ['type' => 'string', 'required' => true]
            'password' => ['type' => 'string', 'required' => true]  // NOTE: you should never actually pass password unencrypted through "get" as they'll get stuck in user logs!
                    ];

    public function execute() {
        // Do Whatever
    }
}

.

// put in 'actions/displayUsersTable.php
class displayUsersTable extends baseAction {
    public function execute() {
        // Do Whatever
    }
}

3) Create an autoloader to pull in those individual files.

function myAutoloader($className) {
    if (file_exists(__DIR__ . '/actions/' . $className . '.php')) {
        require_once(__DIR__ . '/actions/' . $className . '.php');
    }
}
spl_autoload_register ('myAutoloader');

4) Then your index.php is as clean as

$action = $_GET['action'] ?? '';
if (strlen($action) > 0 && class_exists($action) && method_exists($action, 'execute')) {
    $oAction = new $action();
    $oAction->execute();
} else {
    // Oopsie
}

(Notes on this last snippet: the "class_exists" triggers the auto-loader. the "method_exists" is to check someone hasn't requested a common php class such as "object"; if you're being safer you should namespace this or add extra validation. This is just an example!)