Подтвердить что ты не робот

Грайль связывает привязку данных объекта

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. Существует ли более простой способ заполнения ассоциаций объекта команды?

4b9b3361

Ответ 1

Вам действительно нужны подкоманды для свойств 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
}

Если вы do нуждаетесь в фактических ассоциациях подкоманд, то я сделал нечто похожее на то, что предложил @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
    //...
}

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

Ответ 2

То, что я видел в некоторых проектах, было использование классов коллекции 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 оценивает, имеет ли список уже элемент в этой позиции, а если нет, список будет автоматически расти в размере и возвращать новый объект из указанного factory.

Обратите внимание, что вы также можете использовать 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

Update:

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

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

Update:

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

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

Ответ 3

У меня возникла та же проблема с вложенными командами, поэтому я сделал следующее обходное решение:

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

Чтобы проиллюстрировать, вот пример псевдокода:

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 value using cos command object instance, and call the save on you
 //command object and every thing should go smoothly from there
   .
   .
   .

}
.
.
.

  • Надежды будущих выпусков grails могут исправить эту проблему и, возможно, позволить разработчику добавлять необязательные параметры или параметры, которые должны быть назначены каждому объекту команды, также может быть полезно:)

Ответ 4

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

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

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

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

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

}