This post is part 2 of a series focusing on CakePHP 1.2.

There have been a couple posts lately on the CakePHP Google Groups asking how themes work. I responded to one with the some quick basic steps. There is also a post by Tarique Sani that give a pretty good starting point.

This is basically a rehash of my response with a bonus tip to simplify theme management.

The Basics

In your AppController you turn on CakePHP theming by adding the following variables:

var $view = 'Theme';
var $theme = 'default';

I like to set the theme right off the bat to some default theme, then in the beforeRender callback reset the correct theme. How you store the current active them is up to you. It could be a config value, session value or a record in the database. Once you have this you can set it with:

$this->theme = $themeName;

Where do all the files go?

If you left all the CakePHP defaults alone you would put your layouts and views in /app/views/themed/themeName. For example the directory structure would look like this:

/app/views/themed/themeName
/app/views/themed/themeName/layouts/
/app/views/themed/themeName/layouts/default.ctp
/app/views/themed/themeName/views/
/app/views/themed/themeName/views/action/
/app/views/themed/themeName/views/action/index.ctp

Then for your css, js and images your would make similar directories under /app/webroot/themed:

/app/webroot/themed/themeName/js/
/app/webroot/themed/themeName/js/jquery.js
/app/webroot/themed/themeName/css/
/app/webroot/themed/themeName/css/style.css
/app/webroot/themed/themeName/img/
/app/webroot/themed/themeName/img/logo.jpg

I wasn’t particularly fond of this approach since it meant maintaining a theme in two locations. I couldn’t just “drop it in”.
Instead I chose to merge the two locations and have the views and layout located in /app/webroot/themed with the js, css and images. I’ll get to the security issues with this in a moment, but first how do I tell Cake to look in webroot for views? In your /app/config/bootstrap.php simply add:

$viewPaths = array(WWW_ROOT);

Now cake will look in both /app/views and /app/webroot/ for any views and themes.

The issue now is that all my views and layout are directly readable. If someone were to type in the path to one of these files in their browser they see all the raw PHP code. To stop this simply create a .htaccess file in /app/webroot/themed with:


DENY FROM ALL

Wait! My App Is Now Super Slow!

See the first comment for clarification on this issue.
Here’s what’s happening. The controller renderer has to load theme.php for outputting a themed view. It does this using the new App::import functinality, part of which is a recursive directory search through all the view paths, which after our change above looks like this:

/app/views
/app/webroot/
/cake/libs/view/

The theme.php files lives in the last location, but to find it, Cake has to search through the first two locations. Now if you have a ton of folders in your webroot, a bunch of folders for product images or something like FCKeditor (467 folders!), the search is going to take awhile.

To fix this I re-ordered the viewPath list in my AppController->beforeRender.

$viewPaths = Configure::read('viewPaths');
array_unshift($viewPaths, array_pop($viewPaths));
Configure::write('viewPaths', $viewPaths);

The list now looks like this:

/cake/libs/view/
/app/views
/app/webroot/

This probably isn’t the best solution, so if you find something better, please let me know.

Some Final Tips

The great thing about CakePHP theming is that if any of the views/css/js/whatever files don’t exist in the theme, Cake will automatically fall back to the default locations. This is nice since it allows you to only create the files you need and use the defaults for the rest of the site. In many cases you really only need theme files for the layout, css and images. I like to include two css files in each layout. A default.css which is in /app/webroot/css/ and has all the base styles for the site. Then a second style.css in /app/webroot/themed/themeName/css/ which has theme specific styles.

Elements work as normal with themed layout, so if you make the html code in them really generic (for example: a ul/li menu) it’s super easy to share the element, but make it look completely different with the theme’s css.

Popularity: 100% [?]