Thursday, July 3, 2008

Using a view for css so you can use variables in .css files

Oftentimes, you are developing in an environment that has a different base uri (e.g. http://localhost/projects/myproject) than the final project will have (http://www.projectdomain.com).

This isn't a problem in normal views/templates in FUSE, because you have the SITE_BASE_URI variable available to you. However, regular CSS files aren't parsed by the templating engine, so you have to explicitly specify the url for, say, background images. Let's take a look at how we can use FUSE to get our SITE_BASE_URI and other variables embedded in the css file so they work in any environment.

Here is our CSS file, mystylesheet.css

body {
/* this image only loads if our base uri is / */
background: url(/images/body_bg.jpg);
}

The problem is that some developers may be working at a base uri of /projects or /projectname, etc, so linking to / will cause the image not to load. We can get around this in Fuse by creating a controller for CSS files and having them parsed as templates. Here's how:

1. Create the controller

controllers/CSSController.class.php
<?php

FUSE::Require_class('AppControl/FuseApplicationController');

class CSSController extends FuseApplicationController {

public $skip_pre_action = true;
public $header_auto_render = false;
public $footer_auto_render = false;

public $template_directory = 'css';

function load() {

if ( $this->filename ) {


$this->set_template_filename( "{$this->template_directory}/$this->filename", array( 'append_extension' => false ) );

header('Content-Type: text/css');
$this->render();

}

}

}
?>

In the controller above, we're explicitly setting the template file to be "css/$this->filename", where $this->filename will be the .css filename from the route itself, as we'll see below.

2. Add the route

adding our route for css/load/{filename} to config/routes.conf.php:

FuseURIRouter::route_connect( 'css/load/:filename', array(
'action' => 'load',
'controller' => 'CSS',
'requirements' => array( 'filename' => '/.+/' )
,
'static_cache' => array( 'always' => true, 'id' => 'filename', 'headers' => 'Content-type: text/css' ),
)
);

3. Move the css file to views/css and add variables

views/css/mystylesheet.css

body {
background: url(<{SITE_BASE_URI}>/images/body_bg.jpg);
}

4. Link the CSS file in the header

We created our route for loading CSS templates as css/load/{cssfilename}, so let's now link our new css template in the header:

adding to views/Layout/default/default-header.tmpl:

<head>

<!-- Other head elements -->

<link rel="stylesheet" type="text/css" media="all" href="<{SITE_BASE_URI}>/css/load/mystylesheet.css" />

</head>



And there you have it. You may notice that in the route the we are statically caching the css file. This is because it takes server overhead to load Fuse for each CSS file, but if we statically cache the file (which saves it as a regular css file), Fuse doesn't have to load, so our overhead is kept to a minimum. You can read more about Fuse's static cache at http://phpfuse.net/wiki/index.php?title=Static_Page_Caching

More information on the Fuse MVC Framework for PHP can be found at http://www.phpfuse.net