25

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「シミュレート」することです。コマンドオブジェクトの関連付けを設定する簡単な方法はありますか?productTypeattributeTypes

4

4 に答える 4

14

私がいくつかのプロジェクトで目にしたのは、Apache Commons Collections の Lazy* コレクション クラスの使用です。次のようなコードを使用して、コマンドの関連付けを遅延初期化しました。

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

これは、LazyList のすべての get(index) 呼び出しが、リストがその位置に要素を既に持っているかどうかを評価するため、存在しないインデックスでも機能します。そうでない場合は、リストのサイズが自動的に大きくなり、指定されたファクトリから新しいオブジェクトが返されます。

遅延マップで同様のコードを作成するために、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/

于 2011-04-19T09:29:13.340 に答える
8

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

そして、resources.groovy に登録します。

beans =
{
    customEditorRegistrar(CustomEditorRegistrar)
}

あなたのCmdには次のものがあります:

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

実際のサブコマンドの関連付けが必要な場合は、PropertyEditorSupport バインディングと組み合わせて、@Andre Steinggress が提案したものと同様のことを行いました。

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

うまくいけば、私はあなたが達成しようとしていることを誤解していません:)

于 2011-04-20T11:21:30.163 に答える
5

ネストされたコマンドオブジェクトでも同じ問題が発生したため、次の回避策を実行しました。

  1. 他のドメインオブジェクトをパラメーターとしてコントローラーアクションに明示的に追加しました
  2. また、ネストされたコマンドオブジェクトに対して明示的にbindData()を呼び出します(通常、他のコマンドオブジェクトをラップするコマンドオブジェクトは、明示的にバインドする必要なしにデータを正常にバインドします。これは、ビューの命名規則によって異なります)
  3. 次に、それらのコマンドオブジェクトで.Validate()を呼び出しました
  4. これらのオブジェクトを使用して、.hasErrors()でエラーをチェックします
  5. ドメインオブジェクトを保存するには、ネストされた各プロパティに対応するコマンドオブジェクトも明示的に割り当てます。

説明のために、以下にサンプルの擬似コードを示します。

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の将来のリリースでこの問題が修正され、開発者がオプションのparamsまたはparamsスコープを追加して各コマンドオブジェクトに割り当てることができるようになることを願っています。これは便利です:)
于 2012-06-19T12:51:00.120 に答える