Friday, December 28, 2012

Using custom models in routes in Play framework Java 2.x, aka PathBindable.

If you want to reduce clutter in your controllers you can use PathBindables to automatically convert URL parameters to your model objects or other custom objects. For example you can get rid of the code that takes an id, looks up the user in a database and checks for null etc by wrapping into a custom PathBindable implementation. This will also make it easier to reuse code among your controllers.

You will need to do 3 things to get this to work,

1. Create a controller and action, such as
public static userName(User user)
{
   return ok(user.getName());
}

Note that user is always non null and is safe to use. An exception is thrown internally if the lookup fails and Play will return a bad request response to the client.

2. Create a route, such as
GET /user/:user   MyController.userName(user : models.User)

Here we create an actual callable URL, for example, /user/124. In this example we use the id in the route but it can be anything that you can map to your model, for example unique email addresses.

3. Implement the PathBindable interface

You can add the PathBindable code to your existing model,

package models;

import javax.persistence.Entity;
import javax.persistence.Id;

import play.Logger;
import play.db.ebean.Model;
import play.db.ebean.Model.Finder;
import play.mvc.PathBindable;

@Entity
public class User extends Model implements PathBindable
{
    @Id
    Long id;

    String name;

    public String getName()
    {
        return name;
    }

    @Override
    public User bind(String key, String id)
    {
        // key is "user"
        // id is the ":user" part of the route, i.e. 1 if you have /user/1
        User user = find.byId(new Long(id));
        if (user == null)
            throw new IllegalArgumentException("User not found");
        return user;
    }

    @Override
    public String javascriptUnbind()
    {
        return null;
    }

    @Override
    public String unbind(String arg0)
    {
        return name;
    }

    public static Finder find = new Finder(Long.class, User.class);
}
References, https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/java/play/mvc/PathBindable.java http://julien.richard-foy.fr/blog/2012/04/09/how-to-implement-a-custom-pathbindable-with-play-2/