Привязка данных объекта команды Grails - PullRequest
24 голосов
/ 15 апреля 2011

Grails имеет очень хорошую поддержку для привязки параметров запроса к объекту домена и его ассоциациям.Это в значительной степени зависит от обнаружения параметров запроса, которые заканчиваются на .id, и автоматической загрузки их из базы данных.

Однако неясно, как заполнить ассоциации объекта команды.Возьмем следующий пример:

class ProductCommand {

    String name
    Collection<AttributeTypeCommand> attributeTypes 
    ProductTypeCommand productType
}

Этот объект имеет одностороннюю ассоциацию с ProductTypeCommand и многогранную ассоциацию с AttributeTypeCommand.Список всех типов атрибутов и типов продуктов доступен в реализации этого интерфейса

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

Я использую этот интерфейс для заполнения списков выбора продуктов и типов атрибутов в GSP.Я также внедряю этот интерфейс в объект команды и использую его для «имитации» attributeTypes и productType свойств объекта команды

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}
    }
}

. Что действительно происходит, так это то, что attributeTypeIds иСвойства productTypeId связаны с соответствующими параметрами запроса, а методы получения / установки "имитируют" свойства productType и attributeTypes.Есть ли более простой способ заполнить ассоциации объекта команды?

Ответы [ 4 ]

14 голосов
/ 19 апреля 2011

В некоторых проектах я видел использование классов коллекции Lazy * из коллекций Apache Commons. Такой код использовался для ленивой инициализации ассоциации команд:

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 {
  // ...
}

В приведенном выше примере GSP может ссылаться на индексы ассоциации

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

Это работает даже для несуществующих индексов, поскольку каждый вызов get (index) для LazyList оценивает, есть ли в списке уже элемент на этой позиции, и если нет, список автоматически увеличивается в размере и возвращает новый объект из указанного завод.

Обратите внимание, что вы также можете использовать LazyMap для создания аналогичного кода с ленивыми картами:

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

Обновление:

Groovy 2.0 (который еще не является частью дистрибутива Grails) будет иметь встроенную поддержку для отложенных и активных списков. Я написал сообщение в блоге на эту тему:

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

Обновление:

С выходом Grails 2.2.0 Groovy 2.0 является частью дистрибутива.

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

7 голосов
/ 20 апреля 2011

Вам действительно нужны подкоманды для свойств attributeTypes и productType?По какой причине вы не используете привязку PropertyEditorSupport?Например:

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        
    }
}

(и что-то похожее для объекта attributeType), и зарегистрируйте их в редакторе-регистраторе:

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

И зарегистрируйтесь в своих ресурсах.groovy:

beans =
{
    customEditorRegistrar(CustomEditorRegistrar)
}

тогда в вашем Cmd у вас просто есть:

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

Если вам нужно нужны фактические ассоциации подкоманд, тогда я сделал нечто похожее на то, что предложил @Andre Steingress, в сочетании с привязкой PropertyEditorSupport:

// 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
    //...
}

Надеюсь, я не понял, что вы пытаетесь достичь:)

5 голосов
/ 19 июня 2012

Я столкнулся с той же проблемой с вложенными объектами команд, поэтому я сделал следующий обходной путь:

  1. Я явно добавил другие доменные объекты в качестве параметров в действие моего контроллера
  2. Кроме того, явно вызывается bindData () для вложенных объектов команды (обычно объект команды, который оборачивает другие объекты команды, успешно связывает свои данные без необходимости явного связывания, это зависит от вашего соглашения об именах представлений)
  3. Затем я вызвал .Validate () для этих командных объектов
  4. Используйте эти объекты для проверки ошибок с помощью .hasErrors ()
  5. Чтобы сохранить свой объект Domain, назначьте явно также каждое вложенное свойство с егосоответствующий объект команды

Для иллюстрации приведен пример псевдокода:

class CommandObjectBig{

    String name
    CommandObjectSmall details

    static constraints = {
      name (blank: false)
    }

}


class CommandObjectSmall{

    String address

    static constraints = {
      address (blank: false)
    }

}

В контроллере:

.
.
.

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
   .
   .
   .

}
.
.
.

  • Надеюсь, что в будущих выпусках Grails есть решение этой проблемы, и, возможно, разрешит разработчику добавить необязательный параметр или область действия параметров, которые будут назначеныКроме того, для каждого командного объекта это может быть полезно:)
1 голос
/ 04 декабря 2014

Объект команды в Grails

В Grails объекты команды похожи на классы домена, но не сохраняют данные.Использование командных объектов в Grails - это простой способ связывания и проверки данных, когда нет необходимости создавать объект домена.

Первое, что вам нужно сделать, это описать свой командный объект.Это хорошо сделать в том же файле, который содержит контроллер, который будет его использовать.Если объект команды будет использоваться более чем одним контроллером, опишите его в исходном каталоге groovy.

Объявление объектов команды

@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)
    }
}

Использование объектов команды

Следующее, что вы, вероятно, захотите сделать, - это связать данные, полученные действием вашего контроллера, с объектом команды и проверить их.

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

}
...