Pages

Saturday, July 2, 2011

How to delete child elements in Parent/Child relationship - JPA

Orphans are the elements removed from a child collection in a parent/child relation ship. If You expect the deletions of child records from the database with by removing child elements from the child collection or with the deletion of parent record in a parent/child relationship with javax.persistence.CascadeType.All , it will not delete the child records from the database. But simple value typed child elements like a collection of strings, will be removed with the removal of parent object or with removal of elements from the child collection. When the parent is saved, the value-typed child objects are saved as well, when the parent is deleted, the children will be deleted. 

If the child objects being entities, not value-types, those have their own life cycle, like "Items" in a "Order". For this kind of scenario, If You want to delete child records from the database with the removal of child elements from the child collection or with the deletion of parent record, you will have to use
org.hibernate.annotations.CascadeType.DELETE_ORPHAN

public class Department {

    @OneToMany(cascade = {javax.persistence.CascadeType.ALL})
    List<Location> locations;

    @OneToMany(cascade = {CascadeType.ALL})
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)   
    List<Employee> employees;
}


Since "Location" is a simple value typed objects, javax.persistence.CascadeType.ALL will perfectly work when deleting orphans. But "Employee" is not a simple value typed objects. So We have to use hibernate's @Cascade annotation with value org.hibernate.annotations.CascadeType.DELETE_ORPHAN in order to remove orphans with removal of parent.

  • It doesn't usually make sense to enable cascade on a <many-to-one> or <many-to-many> association. Cascade is often useful for <one-to-one> and <one-to-many> associations.
  • If the child object's lifespan is bounded by the lifespan of the of the parent object make it a lifecycle object by specifying cascade="all,delete-orphan"

Friday, October 22, 2010

Struts2 exception flow interceptor

The purpose of this post is to explain, How we can use struts 2 interceptor to bring exception messages into the view screen of your web application and be aware the user about those in meaning full way. There may be several ways of implementing this kind of functionality. My major purpose of this technique is to keep developer significantly free for taking exception messages to the view screen, and let the interceptor to do this instead, once We have added this feature into our system.

This is very straight forward method. Each struts 2 action is invoked from a interceptor's intercept method and catch the exception with in intercept method and send the message to the view. I am going to explain this technique for struts 2 action invocation via AJAX request. I named the interceptor as ExceptionFlowInterceptor. Let's see, How configure the interceptor in
struts.xml file.

<package name="autoac" extends="struts-default json-default" namespace="/secure">
<interceptors>
     <interceptor name="exceptionFlowInterceptor" class="com.semika.autoac.web.interceptors.ExceptionFlowInterceptor"></interceptor>
     <interceptor-stack name="autoac-stack">
           <interceptor-ref name="exceptionFlowInterceptor"></interceptor-ref
           <interceptor-ref name="defaultStack"></interceptor-ref>
     </interceptor-stack>
</interceptors>

<default-interceptor-ref name="autoac-stack"></default-interceptor-ref>

</package>

Better to give little explanation about the above configuration. I have declared a package called "autoac" which extends struts-default and json-default packages. Since I want to invoke actions in autoac package with ajax request, I have extended autoac package from json-default package. To know further about that, please read about struts 2 json plugin.

I have configured the interceptor so that each action in /secure name space is undergone interceptor's pre-processing and post processing.

Next, We will see the code for our interceptor. I am not going to put the whole source here. I just put, How intercept method behaves.

@Override
public String intercept(ActionInvocation invocation) throws Exception {
   String result = null;
   try{
       result = invocation.invoke();
   } catch(Exception e) {
       ValueStack vs = invocation.getStack();
       if (e.getMessage() == null) {
          vs.setValue("errorMessage", "Internal system faliure.Please contact system administrator.");
       } else {
          vs.setValue("errorMessage", e.getMessage());
       }
       vs.setValue("status", "fail");
       result = "json";
 }

 return result;
}


I guess, You have already got the point. I have invoked the action from intercept method and within in try catch block. Any of the exceptions raised after the action invocation, will be caught here. In case of an exception, You can catch the exception and send it into the view in nice way. In my case, I am handling status field at action's model and update that field accordingly. That is up to you to handle this point.

I have obtained the value stack and update the status field as an error. Struts 2 json plugin serializes the whole action and resulting json object which can be accessed with Javascript.

Thursday, October 14, 2010

CSS extend

This is very simple thing that we do not care too much. When we write CSS file, have you knew that, we can extend CSS attributes from one CSS class to a another CSS class. I will explain this using a very simple example.

Suppose you want to have some customized border styles for cells of a table. For some table cells You need only the top border only, and some other cell You need only the top and bottom border only. For this kind of scenario's, We can use CSS extends feature rather adding repeated CSS attributes.

This is the CSS class for table border top.
.border-top {
        border-top: 1px solid #CCCCCC;
}

Next, You want to have another CSS class for table cells with top and bottom border. If You do not consider about CSS extends, You have to write a CSS class as fallows.
border-top-bottom {
      border-top:1px solid #CCCCCC
      border-bottom:1px solid #CCCCCC;
}

You can see, You have repeatedly added "border-top: 1px solid #CCCCCC;" line for top-bottom CSS class as well. If you extends 'border-top-bottom' CSS class from 'border-top' CSS class, You do not need to do so.

Now I am going to modify 'border-top' CSS class as fallows.
.border-top,  .border-top-bottom {
      border-top: 1px solid #CCCCCC;
}

The above CSS class declarations simply says that, 'border-top-bottom' CSS class inherits all the CSS attributes from 'border-top' CSS class. Then You can write 'border-top-bottom' CSS class as fallows.
.border-top-bottom {
      border-bottom: 1px solid #CCCCCC;
}

You do not need to add 'border-top: 1px solid #CCCCCC;' attribute repeatedly. Further if You want to write CSS class for table cells with border top and right, You can modify 'border-top' CSS class as fallows.
.border-top, .border-top-bottom  .border-top-right{
     border-top: 1px solid #CCCCCC;
}

See carefully, I have added a comma after 'border-top' CSS class and not after 'border-top-bottom'. The above CSS declaration says that, 'border-top-bottom' and 'border-top-right' both CSS classes inherits CSS attributes from 'border-top' CSS class.

Saturday, July 17, 2010

How to implement JBoss Login Module

Recently I started use Jboss as an application server. I picked up one interesting feature of it, which are security utilities comes with Jboss. JBoss provides very rich security components. I was able to implement simple login authentication module using Jboss. This may be a advance step in case of authentication. Even you are not a authentication module developer, it is safe to keep some understanding, at least to develop a simple authentication module using Jboss authentication utility. Here, I have explained step by step how to implement simple login module with jboss security utilitis.
I am going to use DatabaseServerLoginModule which is a JDBC base login module comes with Jboss. DatabaseServerLoginModule is based on tow logical tables, Principals and Roles. In my case, I have created tow tables AUTO_AC_USER and AUTO_AC_ROLE, probably in your case, User and Role tables.

The relevant create table statements are as fallows.
CREATE TABLE AUTO_AC_USER (
ID int(10) unsigned NOT
    NULL AUTO_INCREMENT,
    USER_ID varchar(45) NOT NULL,
    USER_NAME varchar(45) NOT NULL,
    PASSWORD varchar(45) NOT NULL,
    PRIMARY KEY (ID)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

CREATE TABLE AUTO_AC_ROLE (
   ROLE_ID int(10) unsigned NOT NULL AUTO_INCREMENT,
   ROLE_NAME varchar(45) NOT NULL,
   PRIMARY KEY (ROLE_ID) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

One user can be assigned several roles, so I have created intermediate table to store user assigned roles. The create table statement for AUTO_AC_USER_ASSIGNED_ROLE table is as fallows.

CREATE TABLE AUTO_AC_USER_ASSIGNED_ROLE (
   ID int(10) unsigned NOT NULL AUTO_INCREMENT,
   USER_ID varchar(45) NOT NULL,
   ROLE_ID int(10) unsigned NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

Now, we have create relation database tables required for the login module. Next, we have to add an application policy into the login-config.xml file located in ...\jboss-5.0.0.GA\server\default\conf folder.
The login-config.xml file entry for our sample login module is as fallows.
<application-policy name="AutoACLogin">
   <authentication>
      <login-module code="com.semika.autoac.security.auth.JAASLoginModule" flag="required">
          <module-option name="dsJndiName">java:/autoac</module-option>
          <module-option name="principalsQuery">
              SELECT u.PASSWORD as password FROM AUTO_AC_USER u WHERE u.USER_ID =?
          </module-option>
          <module-option name="rolesQuery">
              SELECT r.ROLE_NAME as role, 'Roles' 
              FROM AUTO_AC_ROLE r WHERE r.ROLE_ID IN (SELECT ur.ROLE_ID 
                                           FROM AUTO_AC_USER_ASSIGNED_ROLE ur,AUTO_AC_USER u 
                                           WHERE ur.USER_ID=u.USER_ID AND u.USER_ID=?)
          </module-option>
      </login-module>
   </authentication>
</application-policy>

The supported login module configuration options include the following:

dsJndiName: The JNDI name for the DataSource of the database containing the logical Principals and Roles tables. If not specified this defaults to java:/DefaultDS

principalsQuery: The prepared statement query to fetch the user principals, like user name, user id, password etc.

rolesQuery: The prepared statement query to fetch the roles assigned to user

Also note that login-module's code attribute which is com.semika.autoac.security.auth.JAASLoginModule which is my custom login module class.

Now we have finished some, but remaining a lot. Next we will see from where, login is instantiated. In my application, I am using struts2 as a front end framework. I have LoginAction.java class, which is a struts2 action class. User login request will invoke the login() method of LoginAction class, username and password as login parameters.
public String login() {
    try {
         System.setProperty("java.security.auth.login.config", "login-config.xml");
         LoginContext lc = new LoginContext("AutoACLogin", new JAASCallbackHandler(getUserName(), getPassword()));
         lc.login();
         Subject subject = lc.getSubject();
         Set principleSet = subject.getPrincipals();
    } catch (LoginException e) {
         e.printStackTrace();
         return INPUT;
    }
     return SUCCESS;
}

If login is success, it returns the login() method of LoginContext, if login is fail, it throws LoginException.

Now the time to show full code for JAASLoginModule and JAASCallbackHandler classes. This is my custom login module class. There are tow important classes of JAAS when implementing login module. That is Subject and Principal.

A Subject represents group of information for a single entity, like login User's information. Such information includes the Subject's identities like userId, userName as well as its security-related attributes like password and also the user assigned roles. In my sample application USER_ID is the subject identity. This subject identity should be represented as a Principal with in the subject.

I implemented a UserPrincipal.java class for this purpose.
package com.semika.autoac.security.auth;

import java.security.Principal;
public class UserPrincipal implements Principal {

   private final String name;

   public UserPrincipal(String name) {
      super();
      this.name = name;
   }
   @Override
   public String getName() {
      return name;
   }
   @Override
   public boolean equals(Object obj) {
      if (this==obj) return true;
      if (obj instanceof UserPrincipal) {
      UserPrincipal up = (UserPrincipal) obj;
          if (this.name!=null && this.name.equals(up.getName())) {
              return true;
          }
      }
      return false;
   }
   @Override
      public int hashCode() {
      return 31 + ((this.name != null ? this.name.hashCode() : 0) * 3 / 2);
   }
   @Override
      public String toString() {
      return "UserPrincipal : {" + this.name +"}";
   }
}

JAASLoginModule.java
package com.semika.autoac.security.auth;

import java.security.Principal;
import java.security.acl.Group;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.login.LoginException;
import javax.sql.DataSource;

import org.jboss.security.SimpleGroup;
import org.jboss.security.auth.spi.DatabaseServerLoginModule;

public class JAASLoginModule extends DatabaseServerLoginModule  {

    private static final String QRY_FIELD_PASSWORD = "password";
    private UserPrincipal caller;

    public JAASLoginModule(){
        System.out.println("Login Module Called");
    }
    public boolean login() throws LoginException {
         System.out.println("Login Authentication Started");
         try {
            if (super.login()) {
                String userName = getIdentity().getName().trim();
                if (userName == null || userName.length() == 0) {
                    throw new LoginException("NULL or Empty user name   while       attempting to login");
                }
               caller = new UserPrincipal(userName);
               System.out.println("User Authenticated " + userName);
               return true;
             }
            return false;
         } catch (LoginException e) {
                System.out.println("User Authentication Failed");
                e.printStackTrace();
                throw e;
          }
   }  
   @Override
   protected String getUsersPassword() throws LoginException {
       String sql = (String)options.get("principalsQuery");
       String password = null;
       Connection conn = null;
       PreparedStatement stmt = null;
       ResultSet rs = null;
       try {
            conn = getDataSource().getConnection();
            stmt = conn.prepareStatement(sql);
            stmt.setString(1, getUsername().trim());
            rs = stmt.executeQuery();
            if (rs.next()) {
                 password = rs.getString(QRY_FIELD_PASSWORD);
            }
       } catch (SQLException e) {
             e.printStackTrace();
       } catch (NamingException e) {
             e.printStackTrace();
       } finally {
             try {
                 rs.close();
             } catch (SQLException e) {
                 System.out.println("Error when closing result set.");
                 e.printStackTrace();
             }
             try {
                 stmt.close();
             } catch (SQLException e) {
                 System.out.println("Error when closing statement.");
                 e.printStackTrace();
             }
            try {
                conn.close();
            } catch (SQLException e) {
                System.out.println("Error when closing connection.");
                e.printStackTrace();
            }
       }
       return password;
   }
   @Override
   protected Group[] getRoleSets() throws LoginException {
         String sql = (String)options.get("rolesQuery");
         Connection conn = null;
         PreparedStatement stmt = null;
         ResultSet rs = null;
         Group roles = null;
         try {
              conn = getDataSource().getConnection();
              stmt = conn.prepareStatement(sql);
              stmt.setString(1, getUsername().trim());
              rs = stmt.executeQuery();
              while(rs.next()){
                  roles = new SimpleGroup(rs.getString("role"));
                  roles.addMember(caller);
              }
         } catch (SQLException e) {
                e.printStackTrace();
         } catch (NamingException e) {
                e.printStackTrace();
         } finally {
               try {
                   rs.close();
               } catch (SQLException e) {
                   System.out.println("Error when closing result set.");
                   e.printStackTrace();
               }
              try {
                   stmt.close();
              } catch (SQLException e) {
                   System.out.println("Error when closing statement.");
                   e.printStackTrace();
              }
              try {
                   conn.close();
              } catch (SQLException e) {
                   System.out.println("Error when closing connection.");
                   e.printStackTrace();
              }
          }
          return new Group[]{roles};
    }

    @Override
    protected Principal getIdentity() {
         return (caller != null) ? caller : super.getIdentity();
    }

    protected DataSource getDataSource() throws NamingException {
         InitialContext ctx = null;
         Properties jndiEnv = new Properties();
         // Add Env Property : Initial Context Factory if given as module option
         if (options.get(Context.INITIAL_CONTEXT_FACTORY) != null) {
              jndiEnv.put(Context.INITIAL_CONTEXT_FACTORY, options.get(Context.INITIAL_CONTEXT_FACTORY));
         }
         // Add Env Property : Provider URL(s) if given as module option
         if (options.get(Context.PROVIDER_URL) != null) {
              jndiEnv.put(Context.PROVIDER_URL, options.get(Context.PROVIDER_URL));
         }
         // Initialize Context with or without env props
         if (options.size() > 0) {
              System.out.println("Initializing InitialContext for JBoss Login Module with JNDI Environment : " + jndiEnv.toString());
              ctx = new InitialContext(jndiEnv);
         } else {
               System.out.println("Initializing InitialContext for JBoss Login Module with default JNDI Environment");
               ctx = new InitialContext();
         }
         // Lookup and return DS
         return (DataSource) ctx.lookup(dsJndiName);
    }
}

Since, I extended from DatabaseServerLoginModule class, I should implement tow methods necessarily. Those are getUserPassword() and getRoleSets() method.

JAASCallbackHandler.java
package com.semika.autoac.security.auth;

import java.io.IOException;
import java.security.Principal;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.jboss.security.auth.callback.SecurityAssociationHandler;

public class JAASCallbackHandler implements CallbackHandler {

   private String username;
   private String password;

   public JAASCallbackHandler (String username, String password) {
     this.username = username;
     this.password = password;
     SecurityAssociationHandler handler = new SecurityAssociationHandler();
     Principal principal = new UserPrincipal(username);
     handler.setSecurityInfo(principal, password.toCharArray());
   }
   @Override
   public void handle(Callback[] callbacks) throws IOException,
     UnsupportedCallbackException {
     System.out.println("Callback Handler - handle called");
     for (int i = 0; i < callbacks.length; i++) {
       if (callbacks[i] instanceof NameCallback) {
          NameCallback nameCallback = (NameCallback) callbacks[i];
          nameCallback.setName(username);
       } else if (callbacks[i] instanceof PasswordCallback) {
          PasswordCallback passwordCallback = (PasswordCallback) callbacks[i];
          passwordCallback.setPassword(password.toCharArray());
       } else {
          throw new UnsupportedCallbackException(callbacks[i], "The submitted Callback is unsupported");
       }
     }
   }
}
Next, we will see how to instantiate LoginContext. Before creating an instance of LoginContext,I have to set the login configuration setting. One way of doing this is, setting system property, java.security.auth.login.config.
System.setProperty("java.security.auth.login.config", "login-config.xml");  
Since I am using Jboss login utilities, login-config.xml file is where I have configure the login configuration.

LoginContext lc = new LoginContext("AutoACLogin", new JAASCallbackHandler(getUserName(), getPassword())); 
When instantiating LoginContext, I have to pass application policy name which I have declared in login-config.xml file and call back handler instance. In this case application policy name is "AutoACLogin" which helps to load the login module which is "JAASLoginModule". When creating new instance of call back handler, I have passed in user name and password required for user authentication. When invoking LoginContext.login() method, it invokes the loaded LoginModule and subsequently handle() method of callback handler. The required information for authentication are passed into the handle() method as an array of callbacks. In this case, login module passes NameCallback and PasswordCallback. The handle() method of callback handler populates those callbacks with actual information and allow to get it by login module for the sake of authentication. After the authentication process, LoginContext returns the status to the application.Success is represented by a return from the login method. Failure is represented through a LoginException being thrown by the login method.

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 ...
Share

Widgets