Grails command object data binding

Dónal picture Dónal · Apr 15, 2011 · Viewed 17.5k times · Source

Grails has very good support for binding request parameters to a domain object and it's associations. This largely relies on detecting request parameters that end with .id and automatically loading those from the database.

However, it's not clear how to populate the associations of a command object. Take the following example:

class ProductCommand {

    String name
    Collection<AttributeTypeCommand> attributeTypes 
    ProductTypeCommand productType
}

This object has a single-ended association with ProductTypeCommand and a many-ended association with AttributeTypeCommand. The list of all attribute types and product types are available from an implementation of this interface

interface ProductAdminService {
    Collection<AttributeTypeCommand> listAttributeTypes();
    Collection<ProductTypeCommand> getProductTypes();
}

I use this interface to populate the product and attribute type selection lists in a GSP. I also dependency-inject this interface into the command object, and use it to "simulate" attributeTypes and productType properties on the command object

class ProductCommand {

    ProductAdminService productAdminService

    String name   

    List<Integer> attributeTypeIds = []
    Integer productTypeId

    void setProductType(ProductTypeCommand productType) {
        this.productTypeId = productType.id
    }

    ProductTypeCommand getProductType() {
        productAdminService.productTypes.find {it.id == productTypeId}        
    }

    Collection<AttributeTypeCommand> getAttributeTypes() {

        attributeTypeIds.collect {id ->
            productAdminService.getAttributeType(id)
        }
    }

    void setAttributeTypes(Collection<AttributeTypeCommand> attributeTypes) {
        this.attributeTypeIds = attributeTypes.collect {it.id}
    }
}

What actually happens is that the attributeTypeIds and productTypeId properties are bound to the relevant request parameters and the getters/setters "simulate" productType and attributeTypes properties. Is there a simpler way to populate the associations of a command object?

Answer

Andre Steingress picture Andre Steingress · Apr 19, 2011

What I've seen in some projects was the use of the Lazy* collection classes from Apache Commons Collections. It used code like this to lazily initialize a command association:

class ProductCommand {

  String name
  String type

  List<AttributeTypeCommand> attributes = org.apache.commons.collections.list.LazyList.decorate(new ArrayList(), new org.apache.commons.collections.functors.InstantiateFactory(AttributeTypeCommand.class))
}

class AttributeTypeCommand {
  // ...
}

With the example given above, the GSP could reference association indices

<g:textField name="attributes[0].someProperty" ...

This works even for non-existent indices since every get(index) call on LazyList evaluates whether the list already has an element on that position and if not, the list will automatically grow in size and return a new object from the specified factory.

Note that you could also use LazyMap in order to create the similar code with lazy maps:

http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LazyMap.html

http://commons.apache.org/collections/apidocs/org/apache/commons/collections/list/LazyList.html

Update:

Groovy 2.0 (which is not yet part of the Grails distribution) will come with embedded support for lazy and eager lists. I wrote a blog post on this topic:

http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/

Update:

With the release of Grails 2.2.0, Groovy 2.0 is part of the distribution.

http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/