Yii 1.1: Alternative folder structure for a standard Yii app

10 followers

I found a standard Yii app's protected folder structure nearly perfect. With a few simple moves and a little bit change to the code, I managed to bring it to the level, which I found as fully perfect. I want to share my point of view, in case someone would like to use this structure as well.

Some remarks

Note, that this is purely organisational approach. I don't think, that changing your application protected folder structure will influence security or performance (in both negative or positive meanings) as well as any other aspect except code organisation.

Since some folders are separated into subfolders, I was able to resign from adding something to class names. For example, contact form model was renamed from ContactForm to just Contact, because it is separated from data models in models/form subfolder. This means purity and simplicity for me, but my lead to classes names' conflicts more often. Keep that in mind.

components folder

In default Yii application, virtually anything that looks similar to the component is kept all together in the protected/components folder. I found this less useful, so I proposed to separate this folder into four subfolders:

  • behaviours -- contains all the behaviours,
  • extenders -- all classes, that extends framework's base classes, for example Controller,
  • functions -- classes, that acts like common function repositories or helpers,
  • widgets -- for all the visual widgets.

Note: Some propose naming functions folder with better suiting helpers name.

Changing structure this way, you have to also change small part of your app's configuration. I.e. change:

'import'=>array
(
    'application.components.*',

to:

'import'=>array
(
    'application.components.extenders.*',
    'application.components.functions.*',
    'application.components.widgets.*',

Using this structure, I was able to resign from adding for example E to class names to distinct them from base classes (with C in the beginning of a name). So base controller (but extended from CController) remained named as Controller (just like in auto-generated app) and CWebUser, extended by me, remained just WebUser, without need to rename it to EWebUser.

Since, behaviours are usually not automatically included/loaded, you don't have to change anything for them. Just remember, to include `.behaviours' subfolder, in path-alias, format, when providing path to their classes, whenever you're using them.

models folder

Similar change goes for models. Default Yii code organisation keeps all models together and distinguish form models from data models by adding Form into form models class name. That was something, that I didn't like from the very beginning, so I split protected/models folder structure into two subfolders:

  • data -- all data related models`,
  • form -- models for all forms.

Similar change to application's configuration must be taken here. Change:

'import'=>array
(
    'application.models.*',

to:

'import'=>array
(
    'application.models.data.*',
    'application.models.form.*',

And also take all side-effects (name simplification as advantage and possible name conflicts as disadvantage) into consideration.

views folder

There were some forum rummors a year or two ago, that putting layouts in the same structure level as all other controller-related views might be sometimes confusing. I fully agree with that. Situation gets even worse, if you're using widgets, that renders some views. So, to cleanup protected/views folder structure, I've separated it into three subfolders:

  • controllers -- moved all other views' folder here,
  • layouts -- remains untouched, as in original, auto-generated app,
  • widgets -- views for possible widgets.

Views that are general or application-wide (for example error.php) are kept in original place, i.e. directly under protected/views folder, without further spearation to some folder. But, you may consider creating general subfolder and keep them there.

To make your application working with new views folder strucuture, this time you don't change nothing in app's config. Instead, you have to modify your base controller (protected/components/Controller.php in original Yii structure or protected/components/extenders/Controller.php in above presented), i.e. the one from which all your app's controllers extends from.

All you have to do, is to copy there getViewPath function's code, taken from Yii sources and modify path returned in it:

public function getViewPath()
{
    if(($module = $this->getModule()) === null) $module=Yii::app();
 
    return $module->getViewPath().DIRECTORY_SEPARATOR.'controllers'.DIRECTORY_SEPARATOR.$this->getId();
}

This will force all your controllers (all extending from this base controller) to look for their views in protected/views/controllers subfolder.

You have to do similar operation for widgets -- i.e. create own, base, master Widget (or EWidget or anything else) components, with overridden getViewPath returning path with widgets subfolder in views folder. And then you have to change all your widgets class definitions to extend from your own Widget (or EWidget or anything else) instead of original, base CWidget.

As for widgets, situation gets a little bit complicated, as getViewPath implementation in base CWidget is much more complex and uses additional checkTheme switch. Since I'm lazy and I'm not using themes at all, I have reduced this code to just simple:

class Widget extends CWidget
{
    public function getViewPath()
    {
       return Yii::app()->getViewPath().DIRECTORY_SEPARATOR.'widgets';
    }
}

But, if you're using themes, then you're at the beginning of probably long and probably painful journey, since original CWidget.getViewPath() implementation contains references to private variables (like $_viewPaths), which are inaccessible in your extended class.

Summary

Changes introduced here requires only a few minutes of work, but might result in cleaner (better in my opinion) folder structure. If you like my approach, go ahead and feel free to implement it in your own application. Good luck! :]

Total 4 comments

#12890 report it
seb7 at 2013/04/18 04:21am
widgets views dir and themes
But, if you're using themes, then you're at the beginning of probably long and
probably painful journey, since original CWidget.getViewPath() implementation
contains references to private variables (like $_viewPaths), which are inaccessible in your extended class.

You're right, that's seems painfull :( If someone do the job it could be nice to share ...

(thanks for that wiki page)

#12887 report it
Chris Backhouse at 2013/04/18 02:08am
Very Useful

Great article - very useful.

I've been looking for a good Admin implementation aside from the module approach and have wondered about splitting models such that the admin model extends the base user (business) model and keeping it in a separate directory would also help ... thanks!

#12834 report it
mbala at 2013/04/15 06:43am
Good one

It is useful to handle the classes inside components like widgets, functions etc

#12805 report it
futureking at 2013/04/12 04:53pm
Very useful article

Thanks buddy! Very useful article.

Leave a comment

Please to leave your comment.

Write new article