Archive for the ‘PHP’ Category.

Implementing JSON templates for Ajax in Kohana 3.1

Over the last few days, I’ve been toying around with the Kohana PHP Framework.  I’ve attempted to learn various PHP frameworks on many occasions, but I always seemed to lose interest because of too much documentation, or a lack of documentation, or just being forced to add too much complexity to an otherwise simple web application.  For the first time, however, I’ve managed to get the hang of one, and as a learning exercise, I turned my personal website into a Kohana application.

One of the features I wanted was to have a single controller that was able to send the full HTML page (with header, navigation, content view and footer) to the client on regular (non-AJAX) requests, or switch to a simple JSON response containing just the new content for AJAX requests.  This is just a short example of how I was able to implement template switching in my main controller so my alternate JSON template would be used when a request was done via AJAX.

PLEASE READ THIS FIRST! We’re going to assume you understand about Kohana template controllers.

My two template views

First we’ll have a look at the HTML template with the full header, navigation & footer.  This is the one I will use when responding to all normal non-AJAX requests.  I simply named it template.php, and it’s located in application/views/miracleblue.

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title><?php echo $title ?></title>
		<?php foreach ($styles as $file => $type) echo HTML::style($file, array('media' => $type)), PHP_EOL ?>
		<?php foreach ($scripts as $file) echo HTML::script($file), PHP_EOL ?>
    </head>
    <body id="body" class="bgimage">
		<div style="width: 1px; height: 1px; position:absolute; display: block; overflow: auto;" id="preload">
			<img src="/blue_eyes_fade_title.jpg" onload="$(function(){imagesLoaded();});" />
		</div>
		<div id="loader" style="text-align: center;margin:auto;margin-top:300px;display:none;">
			<img src="/ajax-loader.gif" />
		</div>
		<script type="text/javascript">$("#loader").css("display","block");</script>
		<div style="width: 900px; margin: auto; height: 500px;" id="wrapper">
			<div id="nav" class="bgshadow">
				<?php foreach ($nav as $key=>$link) : ?>
					<a href="/<?php echo $key; ?>" class="<?php echo $link['class']; ?>"><?php echo $link['title']; ?></a>
				<?php endforeach; ?>
			</div>
			<div id="content" class="bgshadow">
				<div id="inner-content">
					<?php echo $content; ?>
				</div>
			</div>
			<div id="info">
				<a href="http://www.soundcloud.com/miracleblue" tip="Soundcloud"><img src="/orange_white_24.png" /></a>
				<a href="http://www.myspace.com/miracleblue" tip="Myspace"><img src="/myspace-icon24.png" /></a>
				<a href="http://www.facebook.com/miraclebluemusic" tip="Facebook"><img src="/facebook_icon.png" /></a>
				<a href="http://www.beatport.com/artists/miracleblue" tip="Beatport"><img src="/beatport_icon_24.png" /></a>
				<a href="http://itunes.apple.com/artist/miracleblue/id409867809" tip="iTunes"><img src="/itunes_icon_24.png" /></a>
			</div>
		</div>
		<script type="text/javascript">$("#wrapper, #content").css("display","none");</script>
    </body>
</html>

As you can see, it’s the full page.  It includes the header, footer, and everything in-between, renders it, and sends it to the client.  This is what gets sent when any normal request comes in.  However, when a request is received that contains the “Ajax” header (X-Requested-With: XMLHttpRequest), the main HTML template is swapped out for a really basic JSON template, that includes only the new page “title”, a “name” of the page (that corresponds with the relevant link in the navigation bar), and the new content html (which is a rendered view) to be put in the “inner-content” element.  Here’s an example of a fully rendered JSON packet.

{
	"name": "about",
	"title": "MiracleBlue - About Me",
	"content": "<p>This is some body content, to go in the page.<\/p>"
}

And here’s the template file, simply named ajax_template.php, in the same folder as the main template file.

<?php
    header("Content-Type: application/json");
    echo json_encode(array("name" => $name, "title" => $title, "content" => $content));
?>

That should be pretty self-explanatory.  The javascript that makes the AJAX request also processes the returned JSON accordingly.  You would, naturally, implement your own processing routines, so we won’t be going into that here.

Switching the template

The way to do this conditional template-switching is actually very simple, but I haven’t seen this technique documented anywhere, so someone who is still new to the Kohana framework (like me) may be hesitant to try figuring it out on their own, just in case it goes horribly wrong and – you know – accidentally brings on the apocalypse and the end of mankind (hey, it could happen!).

So, to avoid that, let me show you exactly how it’s done.  In your template controller class (the one that extends Controller_Template), you should have defined a public variable $template that points to your main HTML template view file.  This will be the default template view for all normal requests.  In your before() method, at the very start of the method before any other code, is where the template switch happens.  Then in your after() method, you need to manually call render() on your content view.  Here’s an example of what mine looks like.

<?php
	defined('SYSPATH') or die('No direct script access.');

	class Controller_Miracleblue extends Controller_Template
	{

		// Define our default HTML template
		public $template = 'miracleblue/template';

		public function before()
		{
			// Switch template to JSON version if request is AJAX
			if ($this->request->is_ajax())
			{
				$this->template = 'miracleblue/ajax_template';
			}
			// NOW we can call the parent method
			parent::before();
			// And do other stuff
			if ($this->auto_render)
			{
				// Do stuff here
			}
		}

		public function after()
		{
			// Manually render the string of the inline view on AJAX requests
			if ($this->request->is_ajax()) {
				$this->template->content = $this->template->content->render();
			}
			// Otherwise, continue as normal
			else {
				if ($this->auto_render)
				{
					// Do stuff here
				}
			}
			parent::after();
		}
	}
?>

There you go.  The paths to your template files should be modified accordingly to your own filenames and paths, of course.  It is very important to note that you NEED to call render() manually in your after() method.  This will convert the HTML view for your inline content into a string, which is then fed to the json_encode() function in your AJAX template.  Leaving this out could result in the end of the world, so don’t forget ;)