Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grails command object data binding

Tags:

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?

like image 375
Dónal Avatar asked Apr 15 '11 13:04

Dónal


1 Answers

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/

like image 156
Andre Steingress Avatar answered Oct 12 '22 23:10

Andre Steingress