Implementing a loose MVC structure within WordPress
If you’ve spent any time on the WordPress Codex, this kind of code is probably very familiar to you:
<!-- Start the Loop. -->
<!--?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?-->
<!-- The following tests if the current post is in category 3. -->
<!-- If it is, the div box is given the CSS class "post-cat-three". -->
<!-- Otherwise, the div box will be given the CSS class "post". -->
<!--?php if ( in_category('3') ) { ?-->
<!--?php } else { ?-->
<!--?php } ?-->
<!-- Display the Title as a link to the Post's permalink. --></pre>
<h2></h2>
<pre>
<!-- Display the date (November 16th, 2009 format) and a link to other posts by this posts author. --></pre>
<small><!--?php the_time('F jS, Y') ?--> by <!--?php the_author_posts_link() ?--></small>
<pre>
<!-- Display the Post's Content in a div box. --></pre>
<div></div>
<pre>
<!-- Display a comma separated list of the Post's Categories. -->
Posted in <!--?php the_category(', '); ?-->
<!-- closes the first div box -->
<!-- Stop The Loop (but note the "else:" - see next line). -->
<!--?php endwhile; else: ?-->This style is so common in the WordPress community that it’s essentially standard practice. It’s also difficult to read, frightening to an HTML developer who isn’t familiar with PHP and WordPress coding standards, and very conducive to duplication.
At Streetwise Media we’re shipping alot of code every week, and doing things with our WordPress installation that are far from out-of-the-box. With a complex and rapidly growing codebase, we need to ensure our code is clean, DRY, and easily maintainable.
Enter MVC.
While there are a few existing frameworks that attempt to implement this in a familiar way via WP plugins, we chose to leave things a little bit looser with our codebase, to allow some flexibility, and avoid introducing unnecessary dependencies.
Essentially, our custom WordPress code tends to break down into the following elements:
Models: Whenever a feature or product will interact with a specific set of data, we build classes that provide all the necessary methods to store and retreive the data involved. This ensures we can work with the dataset from anywhere in our codebase, in a consistent manner. For example here’s how we might add data to a custom table that stores data responsible for tracking the “likes” you see on our comments:
<?php $likeable = array( /* data properly formatted for model */ ); $like_model = new BInnoLike(); $like_model->add($likeable); ?>
This makes it very easy to store new data from anywhere in the codebase, and the model does the job of validating the data passed to it, so we always know that any data stored is stored consistently.
Views: We forego the WordPress custom of mashing together HTML and PHP in one big pile, in favor of a more readable and re-usable templating option. Our views consist of only valid HTML, thanks to Gabor De Mooijs Stamp. A typical view might look like a longer version of this:
<!-- item_region --></pre> <h4><!-- title --><!-- /title --></h4> <img src="<!-- src --><!-- /src -->" alt="" /> <!-- subtext --><!-- /subtext --> <!-- /item_region -->
Typically longer views will be stored in their own files, with .tpl as an extension.
Helpers: These are classes which provide the methods necessary to populate our views using appropriate data retreived from our models. For smaller products, such as our channel directory, a custom page template may contain nothing more than a helper function, which retieves data from a model and populates a view. In larger projects, they will generally be stored alongside the model classes. A helper function to populate the above view example might look like this:
<!--?php $model = new dataModel(); $data = $model--->retrieve(); //populate an array of data to work with
$template = new Stamp(Stamp::load('full/path/to/tpl/file.tpl'));
$output = '';
foreach($data as $item)
{
$output .= $template->copy('item_region')->replace('title', $item['title'])->replace('src', $item['src'])->replace('subtext', $item['subtext']);
}
return $output;
?>Controllers: In our bigger products, these classes are solely responsible for managing the current state of the application, specifically what the end user is requesting. For example, our channel partners all have access to a management interface that allows them to manage the members of their channel, appearance of their channel page, and content that gets published to their channel. For a product this size, a custom page template acts only as the controller, determining the what is being requested and the authorization level of the current user, then implementing the correct helper to render the appropriate view with the appropriate data. A short snippet of a controller might look like this:
if ($_GET['display'] == 'normal') and $user_is_cleared)
{
$helper = new HelperClass();
echo $helper->generate_normal_output();
}WordPress is an awesome platform for rapid development of a wide variety of content management functionality, and it’s super friendly to beginners (it’s how I got into web development.) The ease with which it allows you to create is both a blessing and a curse, since as you build larger and larger scale projects, skipping over certain (seemingly unnecessary) best practices early on leads to unmaintainable code later. I’m grateful to Michael Weichert and Elia Sarti, the great developers I was fortunate to learn from during my days at Photocrati, and glad to have brought some of their knowledge to Streetwise. We’ve found this strategy thus far to be invaluable in allowing us to build complex features in such a way that enhancements and fixes are generally possible in less than 20 lines of code, which means we can get done with maintenance quickly and focus on the fun part, new stuff.
Got your own tricks for taming your WordPress code? Let us know in the comments.
from BostInno http://bostinno.com/all-series/implementing-a-loose-mvc-structure-within-word...