WordPress might not use the MVC pattern but following it in your plugins is easy with just a minor change in perspective.
The idea of implementing the MVC pattern in your plugins is not new, but it’s something that many people feel is yet to be done well. Frameworks like WP MVC and, more recently, Herbert have made some progress in this field, but the former seems more interested in using WP Core as an application framework and the latter is very young and doesn’t play well with itself.
Regardless of how you start (framework, boilerplate or plain PHP) WordPress is event-driven (hooks for actions and filters, etc.) so doesn’t lend itself to the client -> controller -> model -> view paradigm.
EMVC: Events to MVC
In your plugin you’ll need a src
folder with three subfolders: Controllers
, Models
and Views
. Fairly standard. Nothing special.
Now create src/hooks.php
and treat it as you would treat a routes file (with a smidgen of bootstrapping, e.g. initialising an Options
object that’s used throughout), but use it to specify actions, filters, shortcodes, admin pages and anything that triggers something to happen. This file may grow a bit so you’ll want to delegate to a controller as soon as possible.
Does it scale?
If your plugin will be massive, you could create a subfolder in src
for each module, each with its own bootstrapping file that’s called by the hooks file when appropriate. This is a particularly good pattern for site plugins which accompany a theme and enable several discrete features (e.g. a post type and a widget for a staff
module or an admin page, set of options and a shortcode for a contact-details
module).
You could even scale into an HMVC pattern, but when working with pre-defined object types like Post Types and Widgets I’ve found the module approach requires the least amount of effort and brain-power to scale.
Code example
composer.json
{ "autoload": { "psr-4": { "YourName\\PluginName\\": "src/" }, "files": [ "src/hooks.php" ] } }
index.php
<?php // Silence is golden...
plugin-name.php
<?php /** * Plugin name: Plugin Name */ require 'vendor/autoload.php';
src/hooks.php
<?php namespace YourName\PluginName; use \YourName\PluginName\Controllers\AdminController; add_action( 'admin_menu', function () { new AdminController; } ); // add_action( 'hook_name', $callback ); etc.
src/Controllers/AdminController.php
<?php namespace YourName\PluginName\Controllers; use \YourName\PluginName\Models\Admin; class AdminController { public function __construct() { } }
Once the request hits a controller you can do what you like: the important part is to think of routing as something you do with events rather than URLs. Once you’ve done that, your golden.