Grails command object data binding
Asked Answered
G

4

25

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?

Grecism answered 15/4, 2011 at 13:38 Comment(1)
I don't the answer for your question, but personally I think this is a great material for a plugin or even a feature in future Grails releases.Tropicalize
T
8

Do you actually need to have sub-commands for attributeTypes and productType properties? Any reason you're not using PropertyEditorSupport binding? E.g.:

public class ProductTypeEditor extends PropertyEditorSupport
{
    ProductAdminService productAdminService // inject somewhow
    void setAsText(String s)
    {
        if (s) value = productAdminService.productTypes.find { it.id == s.toLong() }
    }

    public String getAsText()
    {
        value?.id        
    }
}

(and something similar for attributeType object), and register these in a editor registrar:

import java.beans.PropertyEditorSupport
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry reg) {
        reg.registerCustomEditor(ProductType, new ProductTypeEditor())
        reg.registerCustomEditor(AttributeType, new AttributeTypeEditor())
    }
}

And register in your resources.groovy:

beans =
{
    customEditorRegistrar(CustomEditorRegistrar)
}

then in your Cmd you just have:

class ProductCommand {
    String name
    List<AttributeType> attributeTypes = []
    ProductType productType
}

If you do need actual sub-command associations then I've done something similar to what @Andre Steingress has suggested, in combination with PropertyEditorSupport binding:

// parent cmd
import org.apache.commons.collections.ListUtils
import org.apache.commons.collections.FactoryUtils
public class DefineItemConstraintsCmd implements Serializable
{
    List allItemConstraints = ListUtils.lazyList([], FactoryUtils.instantiateFactory(ItemConstraintsCmd))
    //...
}    
// sub cmd
@Validateable
class ItemConstraintsCmd implements Serializable
{
    Item item // this has an ItemEditor for binding
    //...
}

Hopefully I've not misunderstood what you're trying to achieve :)

Toddtoddie answered 20/4, 2011 at 11:21 Comment(1)
Yep, you would have a field in your gsp corresponding to the attribute. So for example with a hidden input: <input type="hidden" value="4" name="productType"> for the single association, and you would have several inputs with the same name for the 1-M one. In my sub-command version the input would be something like <input type="text" value="123" name="allItemConstraints[0].item"> where the array notation maps it to positions in the lazy list (I don't think you need to do it this way though).Toddtoddie
A
14

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/

Aletaaletha answered 19/4, 2011 at 9:29 Comment(4)
"every get(index) call on LazyList evaluates whether the list already has an element on that position" Surely what you want is for the list to automatically grow whenever an item is added to the list, because this is what happens when the request parameters are bound to the command object?Moradabad
for example, if "attriutes[0]" is referenced, but the attributes list is empty, the InstantiateFactory will use the default constructor of the given class and create a new instance and add it at index 0. this instance is than used to bind request parameters with "attributes[0].xxx"Aletaaletha
but it seems to me that the first thing Grails will try to do is set an item into the List, whereas the docs for LazyList clearly state that the list grows only when an item is retrieved from the listMoradabad
Grails will have to execute "attributes[0]" first, which leads to a List.get(index) call, which in the case of LazyList/InstantiateFactory will create a new instance of the specified class. if you initialize the list with objects this mechanism does not apply.Aletaaletha
T
8

Do you actually need to have sub-commands for attributeTypes and productType properties? Any reason you're not using PropertyEditorSupport binding? E.g.:

public class ProductTypeEditor extends PropertyEditorSupport
{
    ProductAdminService productAdminService // inject somewhow
    void setAsText(String s)
    {
        if (s) value = productAdminService.productTypes.find { it.id == s.toLong() }
    }

    public String getAsText()
    {
        value?.id        
    }
}

(and something similar for attributeType object), and register these in a editor registrar:

import java.beans.PropertyEditorSupport
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry reg) {
        reg.registerCustomEditor(ProductType, new ProductTypeEditor())
        reg.registerCustomEditor(AttributeType, new AttributeTypeEditor())
    }
}

And register in your resources.groovy:

beans =
{
    customEditorRegistrar(CustomEditorRegistrar)
}

then in your Cmd you just have:

class ProductCommand {
    String name
    List<AttributeType> attributeTypes = []
    ProductType productType
}

If you do need actual sub-command associations then I've done something similar to what @Andre Steingress has suggested, in combination with PropertyEditorSupport binding:

// parent cmd
import org.apache.commons.collections.ListUtils
import org.apache.commons.collections.FactoryUtils
public class DefineItemConstraintsCmd implements Serializable
{
    List allItemConstraints = ListUtils.lazyList([], FactoryUtils.instantiateFactory(ItemConstraintsCmd))
    //...
}    
// sub cmd
@Validateable
class ItemConstraintsCmd implements Serializable
{
    Item item // this has an ItemEditor for binding
    //...
}

Hopefully I've not misunderstood what you're trying to achieve :)

Toddtoddie answered 20/4, 2011 at 11:21 Comment(1)
Yep, you would have a field in your gsp corresponding to the attribute. So for example with a hidden input: <input type="hidden" value="4" name="productType"> for the single association, and you would have several inputs with the same name for the 1-M one. In my sub-command version the input would be something like <input type="text" value="123" name="allItemConstraints[0].item"> where the array notation maps it to positions in the lazy list (I don't think you need to do it this way though).Toddtoddie
L
5

I've faced the same problem with nested command objects, so I did the following workaround:

  1. I explicitly added the other domain objects as parameters to my controller action
  2. Also, explicitly called bindData() for the nested command objects (usually, the command object that wraps the other command objects will bind its data successfully without the need to bind it explicitly, this depends on your view naming convention)
  3. Then I called .Validate() on those command objects
  4. Use these objects to check for errors with .hasErrors()
  5. To save your Domain object, assign explicitly also each nested property with it's corresponding command object

To illustrate, here is a sample pseudo code:

class CommandObjectBig{

    String name
    CommandObjectSmall details

    static constraints = {
      name (blank: false)
    }

}


class CommandObjectSmall{

    String address

    static constraints = {
      address (blank: false)
    }

}

In the controller:

.
.
.

def save = { CommandObjectBig cob, CommandObjectSmall cos ->

//assuming cob is bounded successfully by grails, and we only need to handle cos

bindData(cos, params.details)
cos.validate()

//then do you code logic depending on if cos or cob has errors

if(cob.hasErrors() || cos.hasErrors())
render(view: "create", model: [bigInstance: cob, smallInstance: cos])
}
else
{
 //create the Domain object using your wrapper command object, and assign its details
 //property it's value using cos command object instance, and call the save on you
 //command object and every thing should go smoothly from there
   .
   .
   .

}
.
.
.

  • Hope future releases of grails have a fix for this problem, and maybe allow the developer to add an optional params or params scope to be assigned to each command object also, it could be useful :)
Lever answered 19/6, 2012 at 12:51 Comment(0)
S
1

Command Object in Grails

In Grails, command objects are like domain classes, but don’t persist data. Using command objects in Grails is a simple way to perform data binding and validation when there is no need to create domain object.

First thing you‘ll need to do is describe your command object. It is fine to do in the same file that contains controller that will use it. If command object will be used by more than one controller, describe it in groovy source directory.

Declaring Command Objects

@Validateable
class UserProfileInfoCO {
    String name
    String addressLine1
    String addressLine2
    String city
    String state
    String zip
    String contactNo

    static constraints = {
        name(nullable: false, blank: false)
        addressLine1(nullable: true, blank: true)
        addressLine2(nullable: true, blank: true)
        city(nullable: true, blank: true)
        state(nullable: true, blank: true)
        zip(nullable: true, blank: true, maxSize: 6, matches: "[0-9]+")
        contactNo(blank: true, nullable: true)
    }
}

Using Command Objects

Next thing you’ll probably want to do is bind the data, that is being received by action in your controller to the command object and validate it.

 def updateUserProfile(UserProfileInfoCO userProfileInfo) {     
  // data binding and validation
   if (!userProfileInfo.hasErrors()) {
      //do something
   } 

}
Sarasvati answered 4/12, 2014 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.