Showing posts with label Recordshelf. Show all posts
Showing posts with label Recordshelf. Show all posts

Wednesday, 14 March 2007

Extending the Zend_Log layout

Due to the solid updates and improvements of the Zend_Log component in the Beta 0.9.2 release, this article is outdated. Please have a look on the follow-up article "Zend_Log revisited" on this blog.

As the current Zend_Log component doesn't provide very meaningful log content per default I wanted to extend the information logged and also the logfile layout. In the article of Stefan Koopmanschap this issue is solved by using the setMessagePrefix method of the Zend_Log class, which pushs the additional log information and layout unlovely to the Bootstrap file.

Another way to improve the content and layout being logged is writing a custom Zend_Log_Adapter by extending for instance Zend_Log_Adapter_File and overwriting its _parseLogLine metod and redefining its _options array.

The possible adapter is located under ./libary/Recordshelf/Log/Adapter and must be in your PHP include_path.

<?php

class Recordshelf_Log_Adapter_File extends Zend_Log_Adapter_File {

private $_options = array('buffer' => false,
'bufferLines' => 20,
'keepOpen' => false,
'format' => '[%timestamp%] [%pid%] [%level%] %message%');

protected function _parseLogLine($fields) {

$output = $this->_options['format'];

$fields['timestamp'] = date('d.m.Y H:i:s');
$fields['pid'] = getmypid();

foreach($fields as $name => $value) {

$output = str_replace('%$name%', $value, $output);

}

return $output;

}

}
At last the custom adapter must be registered in the Bootstrap file,
...
Zend_Log::registerLogger(new Recordshelf_Log_Adapter_File('../logs/development.log'));
...
and now can be used by your application.
...
$dump = print_r($object, true);
Zend_Log::log(__METHOD__." Object: ${dump}", Zend_Log::LEVEL_INFO);
...
As the rewrite of Zend_Log is an offical approved proposal this whole issue might be solved in one of the following versions of the Zend Framework.

Saturday, 10 March 2007

Using Phing to automate the generation of class skeletons for the Zend Framework

As I'm not very challenged by doing monotonic and repetitive work, as the building of custom controller and model classes of the Zend Framework is, I crafted an automation solution for these two tasks using Phing.

The tasks I wanted to automate were the creation of custom controller and model class by calling a build file. So I wrote two templates deriving from Zend_Controller_Action and Zend_Db_Table including some common methods used in all my controller/model classes like i.e. getModelInstance() in any custom Zend_Db_Table derivation. I could have also build my own extended base classes and let my custom controllers/models derive from them, but for the sport of it I didn't.

The following code snippet shows one off the templates which are located in the ./application/templates directory and are used by the build file to generate the skeleton classes as they are needed.

<?php

/**
*
* @@Classname@@Contoller class.
*
* @author Raphael Stolt <[email protected]>
* @package
* @version $Id$
*/

class @@Classname@@Controller extends Zend_Controller_Action {

public function indexAction() {
...
}

public function init() {
...
}

/**
* Retrieve a model instance to keep/delegate business
* logic/work out of the controller and provide the model
* only to actions that need it.
*/
private function getModelInstance() {
...
}

/**
* Retrieve a view instance holding the default view
* directory for this controller.
*/
private function getViewInstance() {
...
}

/**
* Check if ajax-action is called in an ajax context.
*/
private function isAjaxActionRequest() {
...
}

}
So here is the Phing build file which will generate the custom class skeletons and can be steered by several options. You can pass the class names by calling it on the command line like model={Name,Name2} for 2 models or controller={Name,Name2,Name3} for 3 custom controllers.
<?xml version="1.0" ?>
<project name="recordshelf" basedir="." default="generate" >

<property name="template.directory" value="./templates"/>
<property name="build.directory" value="./builds/"/>
<property name="controller.template" value="${template.directory}/Template_Custom_Action_Controller.php"/>
<property name="controller" value="" override="true"/>

<property name="model.template" value="${template.directory}/Template_Model.php"/>
<property name="model" value="" override="true"/>

<target name="check-and-unravle" description="Checks if all class templates are available">

<!-- Check user input -->
<if>
<and>
<equals arg1="${controller}" arg2="" />
<equals arg1="${model}" arg2="" />
</and>
<then>
<fail message="No custom controller or model name provided!" />
</then>
</if>

<if>
<not>
<equals arg1="${controller}" arg2="" />
</not>
<then>
<property name="controller" value="${controller}" override="true"/>
</then>
</if>

<if>
<not>
<equals arg1="${model}" arg2="" />
</not>
<then>
<property name="model" value="${model}" override="true"/>
</then>
</if>

<!-- Check for existence of class templates -->
<if>
<available file="${controller.template}" />
<then>
<echo message="Using ${controller.template} as template." />
</then>
<else>
<fail message="${controller.template} is not available!" />
</else>
</if>

<if>
<available file="${model.template}" />
<then>
<echo message="Using ${model.template} as template." />
</then>
<else>
<fail message="${model.template} is not available!" />
</else>
</if>

<!-- Unravle user passed properties and call generation targets -->

<foreach list="${controller}" param="controller" target="generate-controllers" />
<foreach list="${model}" param="model" target="generate-models" />

</target>

<target name="generate-controllers" description="Generates a custom controller class from the template class.">

<copy file="${controller.template}" tofile="${build.directory}/${controller}Controller.php"
overwrite="false" >

<filterchain>
<replacetokens begintoken="@@" endtoken="@@">
<token key="Classname" value="${controller}" />
</replacetokens>
</filterchain>

</copy>

</target>

<target name="generate-models" description="Generates a custom model class from the template class.">

<copy file="${model.template}" tofile="${build.directory}/${model}.php" overwrite="false" >

<filterchain>
<replacetokens begintoken="@@" endtoken="@@">
<token key="Classname" value="${model}" />
</replacetokens>
</filterchain>

</copy>

</target>

<target name="generate" depends="check-and-unravle">
<echo message="Class generation to ${build.directory} finished." />
</target>

</project>
If this build file is stored in your applications base directory and Phing is installed you can call it like this on the console.

phing -Dcontroller Login,Shelf -Dmodel Record,User

This will generate the two controller skeleton classes Login and Shelf and further the two model skeleton classes Record and User to the directory stated in the build file.

One outstanding issue of the current build file is that it is possible to pass lowercase Contoller and Model names. So that you might end up with a custom model skeleton like user.php instead of User.php which is against the convention of class naming. But like Unix it currently assumes that you are an responsible user.

Tuesday, 6 March 2007

Gluing Ajax with the Zend Framework

As I moved forward with the implementation of the recordshelf application I wanted to use Ajax in interaction with the Zend Framework. It all started with a typical XMLHttpRequest to one of the defined methods of the customized Zend_Controller_Action objects, like in the following code snippet using the Prototype 1.5 libary.

var id = element.getAttribute('id');

new Ajax.Request('/recordshelf/foo' + id, {

method: 'post',

onSuccess: function(transport, json) {
//use and handle foo response data
},
on500: function(transport) {
//handle error, inform user
},
...
});
This all comes down to a XMLHttpRequest to the recordshelf controller and its hosted foo action by passing one parameter id and handling the response data or HTTP status error code of the called action.

In the requested action of the contoller the provided functionality of the model is used, which migth return a resultset or in the worst case throw an exception, which gets translated into a HTTP status error code of 500. The data or error can be responded to the requesting client in several formats like Json, Xml or just Raw Text. Therefor a Zend_Controller_Response_Http object is build in the request handling action containing all the infomation in the specified response format for the client.

As a image is more worth than thousand words here's an UML sequence diagramm showing the flow and involved components.

UML sequence diagram

The following code outlines how to build an Ajax response for the action foo of the Recordshelf_Controller using Json and Xml as response format. The used PHPDoc tag @ajaxaction is not mandatory, it's just used here to stress that foo action is handling an Ajax request and is used further for examination of the source file via the PHP Reflection API.

class Recordshelf_Controller extends Zend_Controller_Action {

...

/**
* @ajaxaction
*/
public function fooAction() {

$this->_helper->viewRenderer->setNoRender();

$model = $this->getModelInstance();

/* fetch id from request, value validation omitted */
$id = $this->getRequest()->getParam('id');

/* [Json response] */

$responseData = $model->getResponseDataForClient($id);

try {

$responseDataJsonEncoded = Zend_Json::encode($responseData);
$this->getResponse()->setHeader('Content-Type', 'application/json')
->setBody($responseDataJsonEncoded);

} catch(Zend_Json_Exception $e) {
// handle and generate HTTP error code response, see below
}

/* [Xml response] */

$responseDataXmlEncoded = $model->getResponseDataForClientAsXml($id);
$this->getResponse()->setHeader('Content-Type', 'text/xml')
->setBody($responseDataXmlEncoded);

/* [HTTP error code response] */

try {

$responseData = $model->getResponseDataFailing();
...

} catch(Exception $e) {

$this->getResponse()->setHttpResponseCode(500)
->setBody('Internal Server Error');
}
}

...

}
The following image shows the Json server response in the Firebug console.

Json response in Firebug

Friday, 23 February 2007

Developing a recordshelf application with the Zend Framework

Yesterday the Zend Framework Preview 0.8.0 was released, so I finally set it up on my box and made my first steps. Besides there was a lot of disquietness this week regarding new PHP frameworks like Stubbles and their need for the php community, I have to stress that I like the Annotation feature of Stubbles and hope this language feature will find its way into the core of php.

The following image provides an coarse overview of the main Zend Framework components to get a grip on.

Zend Framework Map

I decided to use Zend Framework because it looks very promising and until now it's a joy to use it(already implemented the basic custom MVC application). Primary to gain a solid knowledge of it I started to develop a recordshelf application, inspired by Ken Pugh's "Prefactoring" DVD store example, which might enable me to keep an overview of my Drum and Bass records, generate printable playlists and store fresh upcoming tunes for tracking. I will use this blog to comment the pitfalls, tools used/build and insights gained and finally provide the application as an living guide.