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
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 :)
I've faced the same problem with nested command objects, so I did the following workaround:
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
.
.
.
}
.
.
.
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/
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
}
}