Pages

Wednesday, November 4, 2009

How to use Ajax with CakePHP using jQuery

With this post, I am going to explain how to make AJAX calls using jQuery in cake php. Effective posting related to this topic is very rare. Cake php is a PHP framework designed using MVC pattern. You have model, controller and view. In the cake php layout page, normally you add the fallowing code somewhere in the page.

<?php echo $content_for_layout; ?>

After executing the controller funcitons, the relavent view *.ctp file will be rendered where you have placed above code inside the layout page. If you consider blog example, after executing PostsController's view() action, view.ctp file will be rendered. This is the default behaviour of cake php. But, you may need more control when rendering view files in cake php than the above default behaviour. Sometime, you may need to load the relavent view file's contents into a spacific DIV tag in currently rendered page without refreshing the page. In this case, you may need AJAX support with cake php. I will explain how to do this with cake php. And also, I am going to explain how to get JSON response from cake php's controller function and update some contents of the page with javascript function. I am going to use jQuery support for this functionality. To have jQuery support for my cake php project, I include the fallowing in my laytout page.(This layout may be default.ctp or your own another layout page).

<?php echo $javascript->link('jquery-1.3.2');?>

The above line links 'jquery-1.3.2.js' file to my layout page.
Now, you we have jQuery support for our business. But not all. jQuery has sevaral UI supports. Next, we will start the real task. I am going to create the client side javascript function that makes AJAX request to cake php controller's action.

I am going to create Post data view function(Think about cake php blog example) with ajax support. Post title will be listed as links, and when user clicks on link, the relavent post information will display in a DIV tag somewhere in the same page.

<?php foreach($posts as $post): ?>
<a href="#" onclick="viewPost(<?php echo $post['Post']['id'];?>)">
<?php echo $post['Post']['title'];?><br/>
</a>
<?php endforeach; ?>


$posts is a variable defined in a controller before rendering this page.
The above code shows how to create list of links using cake php. When click on each link, viewPost() function will be called and relavent post id will be passed into that function. The viewPost() function will take the responsibilities to make the AJAX request to cake php action and subsequently update the page contents.

The javascript code for the viewPost() function as fallows.
function viewPost(postId){
       var data = "id="+ postId;
       $.ajax({
             type: "post",  // Request method: post, get
             url: "/cake/index.php/posts/view/", // URL to request
             data: data,  // post data
             success: function(response) {
                                  document.getElementById("post-view").innerHTML = response;
                           },
                           error:function (XMLHttpRequest, textStatus, errorThrown) {
                                  alert(textStatus);
                           }
          });
          return false;
}

$.ajax() is a jquery function that suports to make AJAX request to the server. You have to specify the path to your cake php action for url option. "success" and "error" are tow callback functions suporting by $.ajax() function. "success" callback function will be fired with the succesfull response from the server. If some error occured at the server "error" callback function will be fired. The code for updating client side with successfull response should take place inside the "success" callback function.

The "success" callback function will be passed the contents of the response page if the server response type is 'text/html'. In this case, response will be the contents of /app/views/posts/view.ctp file. In this case the server response type will be 'text/html'. "success" callback function will receive the reponse data and set as the inner html of the DIV tag with id "post-view". You can do what ever with the response data. Keep in mind, this response data is just text or html. Using data option, we can pass some data into the server as http post data.
Next, we will explore the server side code for the view action in cake php controller. The code as fallows.

function view($id = null) {
Configure::write('debug', 0);
if($id == null){
$id = $_POST["id"];
}
$this->pageTitle = 'View Post';
if($this->RequestHandler->isAjax()) {
$post = $this->Post->findById($id);
$this->set('post', $post);
$this->layout = 'ajax';
$this->render('view');
} else {
$post = $this->Post->findById($id);
$this->set('post', $post);
$this->layout = 'post_layout';
$this->render('view');
}
}

This function supports for both behaviours. The cake php default behaviours and also this function responses for AJAX request. Function checks the request type for ajax or normal request and then proceed based on that. Cake php's RequestHandler component helps to check the request type.If the request is AJAX request, the layout is being changed to 'ajax' to support for a ajax response.
When the request is ajax, /app/views/posts/view.ctp file's content will be passed to the client as normal text/html. Then from the client side, "success" callback function will get this response and update the client page. If the request is normal request,/app/views/posts/view.ctp file will render in default cake php's behaviour, where you have placed <?php echo $content_for_layout; ?>.

Next we will consider,how to get the response as JSON object from cake php's controller action. Some time you may need to get JSON object as the response from the php controller's action. With slight changes to above code, we can have a JSON response. The javascript function making the AJAX request as fallows.

function viewPost(postId){
        var data = "id="+ postId;
        $.ajax({
                  type: "post",  // Request method: post, get
                  url: "/cake/index.php/posts/view/", // URL to request
                  data: data,  // Form variables
                  dataType: "json", // Expected response type
                  success: function(response) {
                                        var sb = [];
                                        sb[sb.length] = "Title :" + response.data.title + "";
                                        sb[sb.length] = "Body :" + response.data.body;
                                        document.getElementById("post-view").innerHTML = sb.join("");
                                },
                  error:function (XMLHttpRequest, textStatus, errorThrown) {
                                alert(textStatus);
                         }
             });
      return false;
}

Note that, I have specified the dataType option as "json". This is important, if we want to get JSON response from the server. JSON object from the server will automatically converted into a javascript object. Then we can traverse the object and update the page with some dynamic html.

Next we will see the server side code, which makes the json response.
function view($id = null) {    
if($id == null){
$id = $_POST["id"];
}                   
$post = $this->Post->findById($id);
$this->set('post', $post);
$this->pageTitle = 'View Post';
if($this->RequestHandler->isAjax()) {
Configure::write('debug', 0);
$post = $this->Post->findById($id);
$this->layout = 'ajax';
$this->autoLayout = false;
$this->autoRender = false;
$this->header('Content-Type: application/json');
$data = array();
$result = array();
$data = array('title' => $post['Post']['title'],
'body' => $post['Post']['body']);
$result['status'] = "success";
$result['data'] = $data;
echo json_encode($result);
return;     
} else {
$this->layout = 'post_layout';       
$this->render('view');
}               
}

This function also supports for both the behaviours, ie: cake php's default behaviour which renders /app/views/posts/view.ctp file after executing the action method. If the request is ajax request, it creates a JSON object and send it back to the browser as the response. PHP json_encode() function makes PHP array into JSON object. Note that, I have set change the layout to 'ajax' and autoRender to false. Content type should be set to 'application/json' for JSON response. Like this you can response any of data as JSON objects back to the browser. In the client side, use some javascript to create the dynamic html from these data. You can do nice ajax with JSON. Personally, I like JSON. I hope you too. Go ahead guys ...

Sunday, February 8, 2009

Standard injection into Servlet/Filter - Spring

I understood that spring dependency injection for beans which
implements standard servlet 2.3 Filter innterface, declared in applicationContext.xml is not working in usual manner. For this one, I had to involve DelegatingFilterProxy class which is delegating to a Spring-managed bean that implements the Filter interface. I have a filter class called 'PDAFilter' which implements standard Filter interface and have a bean entry in the applicationContext.xml file fallows.
<bean id="pdaFilter" class="com.axiohelix.pda.main.PDAFilter">
   <property name="hibernateUtil" ref="hibernateUtil" />
</bean>

PDAFilter implements Filter.init(), Filter.doFilter() and Filter.destroy() methods. While creating PDAFilter instance,I needed to inject instance of HibernateUtil class into it. Usual injection always results in null HibernateUtil object. Spring's DelegatingFilterProxy class can be used for this kind of injection. We have to add the fallowing entry into web.xml file.

<filter>
  <filter-name>pdaFilter</filter-name>
     <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
  <init-param>
     <param-name>targetFilterLifecycle</param-name>
     <param-value>true</param-value>
</init-param>
</filter>
All calls to the filter proxy will then be delegated to that bean in the Spring context, which is required to implement the standard Servlet 2.3 Filter interface. The lifecycle methods defined by Filter interface will not be invoked by this kind of implementation. The servlet api expects in from spring context. But spring context is not handling licecyle methods. To force servlet api to invoke the licecyle methods, I have set 'targetFilterLifecyle' to 'true'.
Share

Widgets