Adam Jenson on the Zend_MVC list was talking about the difficulties in MVC of making a thin controller. The discussion is an interesting example of the ineffectiveness MVC as the only pattern holding a web application together. Since I've already complained about MVC in the past, I think Adam touched on something that I struggle with as well, which is a thin Controller.
My solution stems from a layer between what the 37Signals would refer to as "the Model" and the Controller, namely a Repository which acts as a broker between a collection of Model objects and the Controller. By using a Repository and Zend_Form, the action of a Controller in an Application can go from being several hundred lines to a couple dozen.
In my last project, my fattest controller looked like this (NOTE: I've included the init() method for completeness):
public function init() { $this->currentModelId = intval($this->_request->getParam("modelId")); } public function postsAction() { if (PostRepository::Exists($this->currentModelId)) { //edit if ($this->_request->getParam("state") == "edit") $this->view->form = new forms_Post("edit", PostRepository::Find($this->currentModelId)); //delete elseif ($this->_request->getParam("state") == "delete") { PostRepository::Delete($this->currentModelId); $this->_forward('viewposts'); } } else { if (! $this->getRequest()->isPost()) $this->view->form = new forms_Post("create", PostRepository::NewPost()); else $this->view->form = new forms_Post("create", PostRepository::Find($this->_request->getParam("id"))); } //postback if ($this->getRequest()->isPost()) { if ($this->PostBack()) $this->_helper->Redirector->gotoUrl('/admin/viewposts'); } } private function PostBack() { $post = array_merge($_POST, $_FILES); if ($this->view->form->isValid($post)) { $this->view->form->persistData(); return true; } else $this->view->errorElements = $this->view->form->getMessages(); return false; }
Because all my forms are attached in the same way and they use the same pattern, I can use the same PostBack() method on different forms.
To be honest, I don't like making calls to a repository statically, and in retrospect, I wouldn't recommend doing this again. This lead me to searching for a better Repository implementation in PHP.
As I was hunting through the the mighty Google, I found an article by Travis Swice in late 2007. While he's concerned with the 'getting' part of the pattern, I'm more interested in the 'setting' (persistence) part of the pattern. In Travis' example, he simple creates a 'new Repository();'. I have a problem with this, since its often times an over-abstraction of an early Domain.
In a simple application, or even during development, you don't want to take the complexities of a querying language out of SQL, into an object-relational mapping tool AND THEN into a Repository pattern! You're just bubbling up the problem. A Repository should simplify querying and persisting data by providing a relatively flat layer for your UI to make simple calls to. If you create an abstract base class called Repository, you can then collect these flat method calls into logical groupings.
In the example above, the PostRepository would inherit from a Repository class like the one below (along with all other Repositories) and it would handle the creation of Post objects as well as their object-relationship mappings to other models. While at first, "new Post();" seems like an equivalent call to "$postRepository->NewPost();", the latter can input default bits of content based on who is creating the Posting. While I know that I'm stretching the definition of a Repository, I do so because often times the pattern need not be completely implemented before its usefulness becomes apparent. As the project grows, you already have then necessary naming conventions in your codebase to implement a full blown domain. Think of it as picking a hiking trail and enjoying the mid-way summit instead of being stuck making your own road with a machete.
I'm still working through a full architecture, however I think with Doctrine, a bit of Reflection and the abstract base class below, I can come up with a half-way Repository pattern with a Unit of Work that will simplify persisting collections of objects with proper transactionality.
abstract class Repository { /** * Unit of Work for each Repository Instantiated * * @var UnitOfWork */ protected $unitOfWork; public function __construct() { $this->unitOfWork = new UnitOfWork(); //Doctrine Session initialization code here... } /** * Add a Create object to the unit of work * * @param Doctrine_Record $model */ public function Create($model) { $this->unitOfWork->RegisterModelForCreate($model); } /** * Add a Update object to the unit of work * * @param Doctrine_Record $model */ public function Update($model) { $this->unitOfWork->RegisterModelForUpdate($model); } /** * Add a Delete object to the unit of work * * @param Doctrine_Record $model */ public function Delete($model) { $this->unitOfWork->RegisterModelForDelete($model); } public function Commit() { $this->unitOfWork->CommitAll(); } private function clearUnitOfWork() { $this->unitOfWork->ClearAll(); } }
{ 1 trackback }
{ 1 comment… read it below or add one }
Hi Jon,
Thanks for taking the time to blog so much. Last year your video tutorials were invaluable to me as I was attempting to learn the Zend Framework. Once I learned it well enough I had no real need to keep visiting your blog. Now, as I try to develop my system architecture and design pattern skills, I find your site sitting atop Google
There are so few people out there that know this stuff and are willing to share it. Keep up the great work man! I appreciate it.