public
Created

Some thoughts about Controller/Action patterns in PHP - please feel free to post comments!

  • Download Gist
notes.md
Markdown

Does the Controller/Action pattern make sense in PHP?

I find myself wondering lately, why do we use the Controller/Action pattern in PHP?

Are there any real benefits to this?

class UserController extends Controller
{
    public function create() {
        // ...
    }

    public function edit($user_id) {
        // ...
    }

    public function update($user_id) {
        // ...
    }

    public function delete($user_id) {
        // ...
    }
}

Since, during any given web-request, only one of these methods are going to be executed, why do we roll them all into a single class?

If the Controller as such is simply a means of grouping Actions together in a meaningful way, why not group the actions into a namespace and start with an Action class instead?

namespace controllers\users;

use framework\BaseAction;

abstract class Action extends BaseAction
{
    // things all User Actions have in common
}

class Create extends UserAction
{
    public function run() {
        // ...
    }
}

class Edit extends UserAction
{
    public function run($user_id) {
        // ...
    }
}

// and so on...

The actions can auto-load from separate files, so we're no longer loading a ton of actions for every request - just the one being executed.

It seems like this would make for more free-form composition, because you can extend or override methods on individual actions, and collect actions of different types in the same namespace. For example, if you start with base classes for Create, Edit, Update and Delete actions, you can choose to extend the Create-class and override/extend it's behavior, without affecting any of the other actions.

Most Controller/Action abstractions do provide a means of implementing an action as a stand-alone class, but it doesn't tend to be the default or most widely used approach.

It almost seems like Controllers, by definition, violate the single responsibility principle, and their responsibilities tend to grow over time - a UserController may start out with just CRUD, but usually grows to include login, logout, registration, e-mail activation, password recovery, and so forth. Unless you put some of those things in separate Controllers - but since they are User-related activities, they really should be grouped somehow, otherwise, what is the point of grouping things under Controllers in the first place, if you have to break out into more Controllers anyway?

Having just Actions and no higher-order concept above that (beside namespaces) would mean you always have the freedom to group whatever actions you see fit, however you want, without having to worry about performance, and without amassing unreadable Controllers with thousands of lines of code over time, or having to refactor an Action-method into a class because you realized that Action needs to be available in multiple Controllers, or breaking a Controller into multiple Controllers because it grew too large.

If Controllers don't really provide anything we need, wouldn't it be better not to have Controllers at all?

The Action base-class could have class-methods corresponding to HTTP-methods with default implementations:

class BaseAction
{
    public function get()
    {
        throw new HttpException(400);
    }

    public function head() { ... }
    public function post() { ... }
    public function put() { ... }
    public function delete() { ... }
}

Opening one class from a namespace-folder when drilling through a directory-tree would be so much faster than looking through hundreds or thousands of lines to find that one action-method - and you would see only the code that actually matters to that action. With Controllers, the more actions you add, the more each individual action drowns out in irrelevant noise that has nothing to do with that action...

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.