

With MVC frameworks being the fad of the land these days, you're probably familiar with the MVC pattern, and the generalities of how it works. Maybe you've even written an app or two in one such framework. However, it's common for PHP developers to apply typical PHP practices, such as page-oriented design, even when working with MVC, prohibiting them from utilizing the pattern to its fullest potential.
With MVC frameworks being the fad of the land these days, you're probably familiar with the MVC pattern, and the generalities of how it works. Maybe you've even written an app or two in one such framework. However, it's common for PHP developers to apply typical PHP practices, such as page-oriented design, even when working with MVC, prohibiting them from utilizing the pattern to its fullest potential.
Today, we're going to use CakePHP to look at a few simple code examples, in order to better understand MVC and its design philosophies. Keep in mind that MVC is a design pattern, and as with any design pattern, there is no One True Implementation. MVC was originally conceived in 1979, in conjunction with the development of Smalltalk, well before the “web” was even a glimmer in Sir Tim's eye. There are several notable differences between the original implementation and what we see in web frameworks today, but we won't concern ourselves with those. The important thing to remember is that there is no one right way to implement a pattern (the fact that there are countless more wrong ways notwithstanding).
Within CakePHP, as within most web frameworks, the layout consists of a controller which responds to requests and interacts with and/or gets data from a model. The controller then passes this data to the view, where it is rendered for the user.
Given that the (arguably) biggest problem in the PHP world is spaghetti code, particularly mixing view logic and business logic, the separation we're primarily concerned with is the one between the controller and view tiers. This is all well and good, but it's often focused on to the detriment of the separation between the model and the controller. The result is bloated controllers, anemic models, and harder-to-maintain code. Consider the following:
Ignoring for the moment the several things wrong with this code, let's take a look at what it actually does. The /posts/index (or /posts) URL serves up a page with the 10 most recent posts: it first grabs the records using the query parameters specified (“limit” => 10, “order” => “Post.created DESC”), and fetches them into the $posts variable. This variable is then sent off to the view using the set() method.
The /posts/feed URL does much the same thing, though we can presume by the method name that this will instead render an RSS feed. (For the sake of simplicity, we're going to try to avoid dealing with any actual view code today, so just imagine it). The feed will show the 50 most recent posts.
Now, this is a simple and obvious example, but it frames a philosophy which will help you avoid subtler forms of logic duplication in your own apps: your models aren't dumb data stores, they're the domain entities of your application. Taking advantage of the plumbing models provide in order to interact with your tables is amazingly helpful, but if that's all you're using them for, you're missing out. In a good MVC design, models are first-class citizens, and treated as such, while controllers simply act as glue, shoveling data back and forth from the model to the view. Controllers should be, by far, the most unglamorous part of your application.
When deciding what logic to put where, here's a good rule of thumb: anything that can go in the model, should (at least between the model and the controller. I've seen people take this too far and actually put view logic in the model... hideous...). Anything that doesn't deal in some way with your application's core business logic (i.e. session management, request/response handling, certain aspects of security and access control) should stay in the controller; just about everything else can go in the model.
So how can we rewrite the above to better reflect that philosophy? Let's try the following:
This is a decent start. As we can see above, CakePHP allows us to set a default $order property to our model objects, which allows us to remove that common element from our queries.
However, we still have two methods performing essentially the same operation. It may not be apparent at first take, but our old nemesis has returned: we're mixing view logic with business logic. The reason is, both methods are performing essentially the same operation; they're both providing a listing of posts. The rendering and query details are different, but the basic operation is the same.
Fortunately, CakePHP provides us a way to serve multiple content types from the same controller action using file extensions. We can enable file extensions by adding the following line to our routes.php config file:
We'll examine what this does in more detail in a later tutorial. For now we just need to know that it enables us to access that same index() action in the controller using /posts.rss (or /posts/index.rss). That gives us:
Much better. We've consolidated two similar controller actions into one, and delegated the template switching outside our application code by including the RequestHandler component to our controller. Again, the inner workings of this component will be detailed in a later tutorial, but what it essentially does is allow us to have multiple templates for the same action, and switch based on content type. Let's see if we can take that a step further:
Excellent. Our controller code is not only more compact and readable, but also more expressive. However, having a blog index page that displays the same data as your RSS feed seems a little redundant. What if instead, we wanted the regular HTML view to display our most popular articles instead?
There are a lot of different ways we could solve this problem without reverting to the switch statement, but right now we're going delve into custom find methods. CakePHP 1.2 beta introduced a new custom find syntax, where instead of calling methods like findAll(), you call methods like find(“all”). This allows for a great deal more flexibility and code reuse. It also goes a long way towards promoting encapsulation of business logic in your domain entities. So let's adjust our Post model per the following:
As you can see, we've created a custom find “type” just by setting some query parameter defaults (note that these defaults can be overridden by any keys you set in $options). With a bit of creativity, this can be applied to many situations to reduce your app's code. We can now keep our simplified controller structure, provided a few modifications:
Perfect. Our plumbing code is kept sleek and minimal, our primary business logic is encapsulated in our domain objects, and we now have a far more descriptive syntax for calling methods.
I hope you've seen how applying a few simple principles (and a few cool CakePHP tricks) can help you greatly simplify your application, and the volume of code you need to maintain. In future tutorials, we'll cover extending some of these MVC tiers to reuse code in other ways.
Hi Nate!
Excellent article! Very helpful indeed.
Got two examples I don't know how to solve though, with regards to putting the logic in the model.
1. Fetching the records of the current user, authorized with the AuthComponent. Possible some other way than by sending the id from the controller?
2. The PaginationHelper is a bit hard to work with this way, since I have to specify a scope, but since it can't take input from a findAll I have to provide the scope directly in the controller. Is there a way around this? Doing something like this would be nice:
$this->set('posts', $this->paginate($this->Post->find("popular", $options)));

