Themeable View Class for CakePHP
This is my implementation of a themeable view class for CakePHP. This class will allow your site to use themes or skins. Themes or skins are a great feature because they allow for division of labor between the developer and the designer (although the designer in this case has to know a bit of PHP). They also allow the website owner to quickly change the look and feel of the website.
The ThemeableView class here is part of the CMS that I am currently working on.
Updated to Version 1.0.6
See this usage notes for CakePHP 1.2.x.x users.
Download the package here:
Yes, I am aware of another themeable view class for CakePHP which can be found here. However there are two problems that I have with that implementation which forced me to write my own class:
The class file is licensed under the OPPL. As far as I can tell, the OPPL seems to prohibit commercial use (see OPPL Section 3b and Section 4a).
It is also unclear whether or not the OPPL is viral when OPPL licensed code is used to create a “Combined Work”. It is also unclear how the OPPL interacts with the GPL, LGPL and other licenses.
I have written to Larry E. Masters about this but I have not gotten any response from him so far.
Theme elements are spread out in separate directories under app/views. This makes it less than ideal for large sites with a lot of controllers and actions.
For instance, if you have a theme named ‘MyTheme’ you will have to create the following directory structure in app/views:
app/views/elements/MyTheme
app/views/errors/MyTheme
app/views/layouts/MyTheme
app/views/controller_name/MyTheme
The first three directories present very little problems. However, with several controllers it will be taxing to have to create a MyTheme folder for each controller. Things get even messier when you use plugins:
app/plugins/MyPlugin/views/elements/MyTheme
app/plugins/MyPlugin/views/errors/MyTheme
app/plugins/MyPlugin/views/layouts/MyTheme
app/plugins/MyPlugin/views/controller_name/MyTheme
This can be a nightmare for theme maintainers since there can be a lot of themeable elements to keep track of and they are all over the place.
My implementation solves problem #1 by licensing ThemeableView under the MIT License. This is the same license used by CakePHP itself. Which means you can do pretty much anything with this code: use it in commercial apps, sublicense it (change the license terms), use it to bring world peace…
The only thing the MIT License disallows you to do is to remove the copyright notice from the code.
If you look at the code for the class itself, you will notice that it sort of resembles phpnut’s code. Before you go off and yell profanities at me, you should know that I contributed to phpnut’s ThemeView as well. Another thing is that my code is based on the View class itself, being that ThemeView and View are written by the same person, then there is bound to be similarities. This is aside from the fact that with the way CakePHP is structured, it is very hard to produce truly unique code.
For problem #2, all themes are placed in a themes directory under the app/ directory. Each theme is in its own directory. So using the MyTheme example above:
app/themes/MyTheme/views/elements
app/themes/MyTheme/views/errors
app/themes/MyTheme/views/layouts
app/themes/MyTheme/views/controller_name
Even with plugins:
app/themes/MyTheme/plugins/MyPlugin/views/elements
app/themes/MyTheme/plugins/MyPlugin/views/errors
app/themes/MyTheme/plugins/MyPlugin/views/layouts
app/themes/MyTheme/plugins/MyPlugin/views/controller_name
Everything that the theme requires to render all the views in your app
is placed inside the MyTheme directory. In addition, you can also include
other resources with the theme such as images, icons, Javascript, and
CSS stylesheets. These need to be placed in a location that is accessible
on your webserver. This means you should place theme resources under
the themes/MyTheme directory in your webroot directory. For example, if
your webroot is under your app (as in the default setup):
app/webroot/themes/MyTheme/css
app/webroot/themes/MyTheme/js
app/webroot/themes/MyTheme/images
app/webroot/themes/MyTheme/icons
Then from your templates, simply do:
<link href="<? $this->getThemeablePath('css', 'mystyle.css') ?>" type="text/css" rel="stylesheet" />
<script language="Javascript" type="text/javascript" src="<? $this->getThemeablePath('js', 'myscripts.js') ?>" />
<img src="<? $this-/>getThemeablePath('images', 'myimage.gif') ?>" />
<img src="<? $this-/>getThemeablePath('icons', 'myicon.gif') ?>" />
New in 1.0.4
As of version 1.0.4, a new ThemeHelper class is available. This class contains theme-aware versions of the css() and image() methods found in the HtmlHelper class. You can use this instead of having to write the tags by hand as above. See the ThemeHelper PHPDocs for details.
To use this class, place the file named themeable.php inside app/views.
Then, from your controller set view to "Themeable". Then set
the current theme as a property themeName:
var $view = 'Themeable';
var $themeName = 'MyTheme';
Or you can create your own app_controller class and define the
view and themeName properties so that all
controllers in your application will be using them.
There are two constants that point to the location of your themes and theme resources.
THEMES - point to the absolute path on your webserver's filesystem where your theme templates are stored.
Default: APP . 'themes' . DS
THEME_RESOURCES - point to the relative path on your website's URL where your theme resources are stored.
Default: DS . 'themes' . DS
You can define these to point elsewhere depending on your setup.
I hope someone finds this useful. If you have any questions or comments please leave them on this post. My apologies for the slight inconvenience of required registration. There has been a string of spambot activity on my site recently.
If you have patches to this code, they are very much welcome. Send them to:
nimrod.abing+blog@gmail.com
Please use the subject line:
PATCH: ThemeableView class
Notes For CakePHP 1.2.x.x
This one comes from PJ Hile. Some API changes have taken place in CakePHP 1.2.x.x and naturally this equates to some changes in the themable view class as well. However, at the time of this update (13 Jun 2007) CakePHP 1.2.x.x is still in Alpha stage. That pretty much means that the API is still subject to change so this may or may not work for you depending on your current 1.2.x.x snapshot. Here's the full text of the email I got from PJ:
On 6/13/07, PJ Hile wrote:
Just some updates for those using CakePHP 1.2.x.x
- $this->_viewVars has been changed to $this->viewVars (lines 242 and 260)
- strip_plugin() has been moved to Router::stripPlugin() (line 183)
Hopefully that will save some people some time.
Regards, PJ
Internal ChangeLog
21 April 2007 - Version 1.0.6
Download: ThemeableView-1.0.6.zip
Match themeable view locations between website pages and phpdoc. (Peppe Bergqvist)
Fix filename mangling when falling back to 'system' theme in
Themeable::themeElement(). (Peppe Bergqvist)Change behavior of Themeable::themeElement() to match new behavior in View::element(). Client code that relied on the old behavior should now use Themeable::_getThemeableElement() instead.
19 April 2007 - Version 1.0.5
Download: ThemeableView-1.0.5.zip
- Fixed recursive method invocation for View::element() whose behavior was changed in revision 4050. Special thanks to Peppe Bergqvist for discovering, reporting and investigating this bug.
07 October 2006 - Version 1.0.4
Download: ThemeableView-1.0.4.zip
Use / instead of DS in getThemeablePath() and THEME_RESOURCES. (thanks to Ryan Clark)
Fix viewPath resolution logic in _getViewFileName to include 'system' theme.
Provide documentation in the PHPDoc comments.
Added new ThemeHelper class to provide theme-aware analogs to css() and image() methods in HtmlHelper.
06 October 2006 - Version 1.0.3
Download: ThemeableView-1.0.3.zip
Fix _getLayoutFilename() logic when the view attempts to search for a usable view layout file. (thanks to Ryan Clark)
Added absoluteURL() method and use it for getThemeablePath() so that it returns the correct path to a themeable resource regardless of where CakePHP is currently installed.
25 September 2006 - Version 1.0.2
Download: ThemeableView-1.0.2.zip
Move theme resources to webroot to allow for setups where the app dir is in a location that is not accessible through the webserver.
Conditionally define THEMES constant to allow user to define an alternate location.
Add a new conditionally defined constant THEME_RESOURCES to point to the relative URL of resources provided by the current theme.
15 September 2006 - Version 1.0.1
Download: ThemeableView-1.0.1.zip
Fix theme fallback mechanisms. If a theme resource is not found in the current theme, it will try to search in themes/system.
Fix bugs in plugin and element theme resource searching.
11 September 2006 - Version 1.0.0
- Initial release.

January 31st, 2007 at 8:46 am
Very nice work… !! Going to try it out now…
January 31st, 2007 at 9:33 am
I have followed all the instructions but for some reason, I am seeing the default Cake layouts when i try to use ‘mytheme’.
January 31st, 2007 at 10:14 am
@Max
Did you specify the THEMES constant to point to the absolute path that contains your theme folders?
For example, your theme is in:
/myhome/website/app/themes/MyThemeTHEMES should be set to
/myhome/website/app/themes/.Note that you have to include the trailing directory separator when you specify THEMES.
I suggest you try it with the defaults first, they are confirmed to work out of the box with a default CakePHP setup. Place your theme inside a
themesfolder in your CakePHPappfolder.Theme resources should be placed in a
themesfolder inside your webroot.You should also note that on Unix systems, filenames are case sensitive. If you named your themes folder
MyThemesyour$themeNameshould also be set to'MyThemes'May 23rd, 2007 at 2:37 am
Hi,
This is a great thing! I was just wondering though… In the past, for a simple CMS (and later blog,) project, I made a theme system that still allowed me to change what information was going to a view.
For example, this type of system in your article only looks like it can change the css/js/views. But is there a way in CakePHP to have multiple controllers (for example, one per one theme, etc.)?
I’m not sure if you check this anymore, but I wanted to ask because I’m in process of moving my old project to CakePHP and am very interested in knowing.
Andy
May 23rd, 2007 at 9:08 am
@Andy
I am not sure what you mean by “multiple controllers” and why you would want that but you can have a different theme for each action in your controller.
June 12th, 2007 at 4:16 am
@Max
I had the same problem because my view files were still using the *.thtml format. Try changing them to *.ctp
October 5th, 2007 at 2:47 pm
Hi,
I love what you have done.
I have a question, when I try to overload the view files it doesn’t seem to work.
Eg, I have: app/views/users/add.thtml
I put it under app/themes/MyTheme/views/users/add.thtml
It doesn’t seem to work. I had a look at the themeable file and at _getViewFileName on line 327, I noticed that there is a foreach which goes through all the $paths->viewPaths as $path. The $path unfortunately is ‘home/app/views/’ which means I would have to put all my view files under: ‘app/views/themes/MyTheme/’
If the foreach loop is deleted, it works as it should, I’m not sure how $paths->viewPaths plays in cake so I didn’t want to mess with it too much.
I was wondering if this is correct, as the docs said they should all be under ‘app/themes/MyTheme’
The layout file however works fabulously.
I am using cake 1.1.10
October 5th, 2007 at 7:49 pm
If you deviate from documented behavior then that would be “incorrect”. The main reason for frameworks like CakePHP or Ruby on Rails is not only to provide you with a set of classes and functions to use in your app. It also provides you with a set of “rules” that you must adhere to if you expect your app to work. Apart from breakage, there is nothing stopping you from “doing it your way”. But I really suggest that you stick with documented behavior and conventions and resort to deviating from them as a last resort (e.g. to work around limitations of your server/hosting setup).
I’m not really sure why you’re getting the wrong path. It must have something to do with your configuration. I haven’t really touched CakePHP in a very long time (see here). But if I recall correctly, $paths->viewPaths is generated by the framework from your configuration settings.
December 22nd, 2007 at 12:12 am
Would be alot easier if there was some installation instructions, other than in the middle of the changelog. Like place it at the top of the readmefile or something.
I spent alot of time figuring out where to place themable.php
December 22nd, 2007 at 7:01 am
What are you talking about? The instructions are right in the middle of the blog entry. Right about here:
I can’t see how you could have missed that, unless you’re one of those people who throw out the instruction manual and just try to “wing it”.
January 6th, 2008 at 5:41 pm
To make ThemeableView work with Cake version 1.2.0.6311-beta, you need to change method _getViewFileName(), because the calling param in the original View class has changed from $action to $name. Add the following lines and it will work again:
function _getViewFileName( $name = null ) { $action = $this->action; …
This is just a quick fix. One may modify the method to process param $name when determining the template filename.
November 4th, 2008 at 12:28 am
[...] CakePHP themes made easy Theming your CakePHP apps (V1.2) Layout your cake with jOOmla templates Themeable View Class for CakePHP Merci d’avance pour vos commentaires et vos contributions Partager et découvrir : Ces icônes [...]