CakePHP Plugins

Thursday, 14 February 2008

Motivation

This paper is intended to try to improve the next version of Cake. I think one of the most important features of a framework is its capacity to be expanded. Plugins achieve that gracefully. But there is a problem; plugins, as read on tempdocs, are meant to be packages. I think they can be much more. This is mainly a software design problem.

Where they fail

Point #1

Cake creates conventions for everything turning controllers, models and views universal. Same happens to plugins, but because of namespace concerns it is recommended to developers to include plugins name in controllers, models and views names. So a blog plugin would have BlogPost and BlogComment as models, BlogPosts and BlogComments as controllers. That’s fine to me (and recommended) but the Router should be smart so it won’t be necessary URI’s like /blog/blogPosts.

Point #2

Second is its usefulness. I think plugins can be more than just isolated modules. Imagine a plugin that is mutable, just by receiving some variables on runtime. I think every experienced developer has come to a day where he finds himself coding same thing twice. With MVC architecture that is a problem which is 90% of the time solved, but here are exceptions.

This kind of problem appears on relative big websites. Imagine you have Users profile page, which can receive Comments. You also have the Groups page which can also receive Comments. Code can be reused by creating an element and invoking it on the view, passing the right data. That’s a simple example.

But, what if Users and Groups could have Images. The Images logic would be obviously be present in the Model, but what about all the actions? Could be achieved by /images/view/type:group/23. Still ok to me. But images now have Categories, Tags and Comments. That becomes kind of hard to maintain. Developer is responsible to persist the type and id, for the operations to succeed. A good solution to this problem would be packing all has a plugin, to get all things organized, but the persistent problem would still exist.

A Diagram

Solution

On the diagram there is an example of a common plugin: blog. Only controllers and their actions are represented, but it illustrates what I’m trying to say. Both Users and Clans have Blogs. Blog is a plugin. The only difference is that plugin is acting inside other controllers, by receiving simple data on runtime. That data will make the plugin mutable to various sources.

This is also the solution for the problem #2, on the previous chapter.

Some code, just for example

// plugins/blog/models/blog_post.php

BlogPost extends BlogModel
{
   $belongsTo = array('User'); // User is a global scope Model
   $hasMany = array('BlogCategory', 'BlogTag');
}

// plugins/blog/controllers/blog_posts_controller.php

BlogPostsController extends BlogController
{
   protected $sourceType;
   protected $sourceID;
}




// controllers/user.php

UsersController extends AppController
{
   public $extends = array(
       'profile' => array('blog', array('sourceType' => 'user', 'sourceID' => '#id')),
       'gallery' => array('gallery', array('sourceID' => '#id')));

}

// In this example, profile and gallery actions, extend to two different plugins.

My only concern with the design of this plugins is just one. The overhead of having to load the Users controller and then redirect to Blogs plugin is a waste. This can be avoided with smart extension, which would read (or cache) the extensions of a controller, before loading it.

Conclusion

There are still some code details which need to be improved, but still, the theory is here. I think Cake would win a lot by adding this simple feature. It would turn plugins mutable which would make extensibility easy.