Ajax, ZF and Smarty feed reader: part 2

In part 1 of this series you could download the whole source which this tutorial is based on. This part and subsequent parts will make use of that source.

Let’s begin with index.php. The file simply loads initiate.php and then renders index.tpl. This will be the only page render we do, from now on it’s ajax only.

I will not explain initiate.php, the IBM ZF tutorial already does a good job of doing that. This tutorial is actually based on that one but here we take the ajax to insane levels.

We move on to head.tpl and index.tpl. In head.tpl all the javascript gets loaded:

<script src="js/prototype.js" type="text/javascript"></script>
<script src="js/scriptaculous.js?load=effects,dragdrop" type="text/javascript"></script>
<script src="js/jspanserializer.js" type="text/javascript"></script>

The above loads prototype which is needed for scriptaculous which is needed in order to be able to drag windows around. However, as you might recall from my description of HTML Ajax we also need jspanserializer to be able to serialize to a format that php understands. JSON just doesn’t cut it for us, at least not the version I tried before I found jspan. If you try the newest version of HTML Ajax don’t hesitate to comment and tell me if the built in php serialize function works with prototype nowadays. If it does then jspan is not needed anymore.

<script type='text/javascript' src="ajax_server.php?client=all"></script>
<script type='text/javascript' src="ajax_server.php?stub=AjaxRss"></script>
<script type='text/javascript' src="js/common.js"></script>

Here we load our HTML Ajax and our own script which is called common.js because it’s filled with commonly needed functionality. Exactly how the loading works you can check in the documentation that comes with HTML Ajax. I’m sure there must be some tutorials and info on The Creator’s blog too.

Let’s move on to index.tpl, the main feature is the window loading:

<input type="button" value="register" onclick="createInsertWindow('parent-template', 'list-template','default_window','dbuser', 'dbuser-insert', 'dbuser-insert-dragger', '', '', false)"/>

Obviously the first two parameters refer to the two empty divs you see at the end of the file, these two divs will be used to create windows. To find out exactly what the others are about requires us to follow the trail, so let’s take a look at common.js and createInsertWindow(). Aha, so we call createWindow() straight away, let’s map the parameters:

parent_id = parent-template (a div element)
origin_id = list-template (a div element)
new_class = default_window (a css style)
target_id = dbuser-insert (the id of the window to create)
focus_id = dbuser-insert-dragger (the id of the area used to drag our future window)

function createWindow(parent_id, origin_id, new_class, target_id, focus_id){
	var target_el = document.getElementById(target_id);
	if(target_el == null){
		var newNode = document.getElementById(origin_id).cloneNode(true);
		newNode.id = target_id;
		if(new_class != ''){
			newNode.className = new_class;
		}else{
			newNode.style.display = 'block';
		}
		document.getElementById(parent_id).appendChild(newNode);
	}
}

Armed with our parameter mapping above we are now ready to make sense of the createWindow function. First we try to actually get the window we are trying to create! The reason is that this window is a singleton, only one of a kind is allowed. If we already have one we can’t create a new one. So if the window doesn’t exist we create it by cloning the list-template div and set it’s id to dbuser-insert in this case.

We assign the default_window style to the window, lastly we append this new window to the parent-template div. I’m not an expert with DOM manipulation in javascript but I think I can recall that I weren’t able to position or control the window before I appended it to something. Since we append the window to the parent-template div we will also know how to remove it by calling parent.removeChild().

default_window looks like this:

.default_window{
	opacity:1;
	position:absolute;
	background-color:#335588;
	border-color:#dddddd;
	border-style:solid;
	border-width:1px;
	padding:0;
    margin:0;
	left:100px;
	top:100px;
}

Note that the window will appear at x:100 and y:100.

createInsertWindow() also executes ajax_rss.getInsertForm(). Let’s do some parameter mapping again:

table = dbuser (the name of the table in the MySQL database that we want to work with)
target_id = dbuser-insert (the id of the window to create)
focus_id = dbuser-insert-dragger (the id of the area used to drag our future window)
template = null
topstyle = null
update = false

We open AjaXRss.class.php and check, aha it’s an extension of AjaxForm, so we open AjaxForm.class.php instead:

function getInsertForm($table, $target_id, $focus_id = '', $template = '', $topstyle = '', $type = false){
	$obj = AjaxCommon::loadModelWithLabels($table);
	switch ($type) {
		case 'update':
			$this->smarty->assign('prepop', $obj->prePop());
			$this->smarty->assign('update_id', $obj->session->id);
			$this->insertFormCommon($table, $target_id, $template, $obj);
			break;
		default:
			$this->insertFormCommon($table, $target_id, $template, $obj, $type);
			break;
	}
	$this->response->assignAttr($target_id, 'onclick', "ajax_rss.setFocus('$target_id', '$focus_id')");
	$this->response->combineActions($this->setFocus($target_id, $focus_id, $topstyle));
	return $this->response;
}

So we load a dbuser model object with the AjaxCommon::loadModelWithLabels() factory method. Since we pass false as type the default switch executes which is a simple call to insertFormCommon():

function insertFormCommon($table, $target_id, $template, &$obj, $type = false){
	$tpl = $template == '' ? 'insert_form.tpl' : $template;
	$this->smarty->assign('table', $table);
	$this->smarty->assign('form_type', $type);
	$this->smarty->assign('parent_id', $target_id);
	$this->smarty->assign('fields', $obj->fetchForm($type));
	$inner_html = $this->smarty->fetch($tpl);
	$this->response->assignAttr($target_id,'innerHTML',$inner_html);
}

Since we didn’t pass any template information the default insert_form.tpl will be used. We assign some variables and call fetch(), the result is made to fill the new window div. Finally we try to set the focus to the new window, I say try because I think this function is not really working properly at the moment. Finding out why is definitely your homework!

Let’s open insert_form.tpl. So we include top_win_bar.tpl at the top, let’s go there then. Apparently the scriptaculous object called Draggable takes two parameters. The div to drag and the div to drag with. In our case the div to drag would be our parent window and the dragger the div that basically top_win_bar is all about. The info of who is who has been passed along all along. But isn’t this amazing, we only create the Draggable object with these two parameters and voila we drag, so easy!

Back to insert_form.tpl. Nothing really strange here, this is basically the same thing we would draw even if we weren’t using ajax. Note that we have to keep track of everything though, all important stuff need their own unique ids. How else could we keep track of them from withing the php code? The conditionals at the bottom keeps track of what kind of form we are dealing with, update, insert or login? In our case it’s insert and we call this:

ajax_rss.submitInsertForm(serializeForm(‘{$table}-form’),'{$table}’)

So the form gets serialized, back to common.js:

function serializeForm(form_id){
	var form_element = document.getElementById(form_id);
	var rarr = new Array();
	for(var i=0; i < form_element.elements.length; i++){
		var fieldInfo = new Array();
		var el_name = form_element.elements[i].name;
		fieldInfo['id'] = form_element.elements[i].id;
		fieldInfo['value'] = form_element.elements[i].value;
		rarr[el_name] = fieldInfo;
	}
	return serialize(rarr);
}

So we loop through all the elements in the form and create a 2D array with the info and the table field names as key for each sub array. The serialize function at the end is actually jspanserializer doing it’s magic. Let’s move on to submitInsertForm in the AjaxForm class. It’s quite big so I will not post it here as I’m afraid of the wordpress 404 bug, I will reference it instead.

First we unserialize with the standard php unserialize function which means that jspanserializer did a good job of hacking the form into something that php can easily understand. Then we validate the form, I leave that part for you to explore on your own. Next we filter out bullshit that we can’t insert into the database. I just love array_intersect_key() for this, it’s an awesome little piece of heaven. Finally we insert the posted data and have HTML Ajax return some kind of appropriate display.

That was all for this time, In the next part we will look at our other windows and what can be done in them.

Related Posts

Tags: , , ,