GrailsコントローラのDRYを増やすには?

Grailsのコントローラコードをクリーンアップする方法を探しています。さまざまなコントローラでは、多かれ少なかれ同じロジックを持っています。

  • オブジェクトを取得する
  • 存在するかどうかを確認する
  • など。

コントローラーアクションを共通コードを再利用するための推奨される方法はありますか?

---ソリューション---

質問に対するすべての回答は、私たちが実装した解決策に貢献しています。

私たちはMixinの手法を使ってコントローラで使用されるクラスを作成しました。ミックスインが公開するメソッドの1つに、withObjectメソッドがあります。このメソッドは、コントローラからドメイン名を取得し、このメソッドのベースを使用します。もちろん、この動作はオーバーライドすることができます!

def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) {
    assert object
    def obj =  grailsApplication.classLoader.loadClass(object).get(params[id])
    if(obj) {
        c.call obj
    } else {
        flash.message = "The object was not found"
        redirect action: "list"
    }
}

だからすべての答えが解決に貢献しました!どうもありがとう!

11
質問を編集してコードの例を追加してください。現時点で問題は非常に曖昧です。
追加された 著者 ordnungswidrig,

3 答え

私はこの質問が出てくると、いつもこのブログ記事を出します:

http://mrpaulwoods.wordpress.com/ 2011/01/23/a-pattern-to-simplify-grails-controllers/

基本的には、コントローラ内のさまざまなドメインのプライベートヘルパーがあります。

private def withPerson(id="id", Closure c) {
    def person = Person.get(params[id])
    if(person) {
        c.call person
    } else {
        flash.message = "The person was not found."
        redirect action:"list"
    }
}

ゲッターをコード化する方法は非常に柔軟で、私の典型的な使用方法(ブログでは扱われていません)は編集などです

私は通常、この方法でコードを作成します(私は明確な分割と可読性のパターンが好きです):

 def editIssue() {
    withIssue { Issue issue ->
        def issueTypes = IssueTypeEnum.values().collect {it.text }
        [issueTypes:issueTypes,activePage:"issue", issue: issue]
    }
}

 def doEditIssue(IssueCommand cmd) {
    if(cmd.validate()) {
        withIssue { Issue issue ->
            issue.updateIssue(cmd)
            redirect(action: "show", id: issue.id)
        }
    }
    else {
        def issueTypes = IssueTypeEnum.values().collect {it.text }
        render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
    }
}

私のゲッターヘルパーは:

private def withIssue( Closure c) {
    def issue = Issue.get(params.id)
    if(issue) {
        c.call issue
    }
    else {
        response.sendError(404)
    }
}

私はmixinメソッド( '共通の抽象コントローラを拡張する'と非常に似ています)も素晴らしいと思いますが、この方法では2つの利点があります。

  1. STS/IDEA(テストされていないNetbeansではありません)のメソッドなどにアクセスできるように、クロージャで行ったようにヘルパーを入力できます。
  2. 繰り返しはあまり高くなく、ゲッターを変更する機能(BarDomain.findByFoo(params.id)などを使用するなど)

In the view I bind to edit() I just put an id="${issue.id}" in the and it works seamlessly.

8
追加された
あなたのコメントをありがとう私は多かれ少なかれ私の側の作業ソリューションにこのスレッドでいくつかの答えを組み合わせた。私はmixin戦略と何らかの汎用のwithObjectメソッドを併用します。私は今使用しているコードで質問を更新します。
追加された 著者 Marco,

ジェネリックメソッドをいくつかのスーパークラスに普及させることはできないので、継承を推奨しません。抽象クラスは、コントローラがたくさんある場合、すぐに乱雑になります。 response render params へのアクセス権がないため、コンポジションを使用することはできません(サービスを使用するなど)。そこ。

私が使用するアプローチは、ミックスインを介して一般的なメソッドを注入することです。

@Mixin(ControllerGenericActions)
@Mixin(ControllerUtil)
class BookController {
  def show = &genericShow.curry(Book)

  def exists = {
    render(idExists(Book))
  }
}

最初のアクション show は、 ControllerGenericActions.groovy の汎用メソッドを引数にバインドして使用します。 mixin idExists メソッドの2番目の使用方法は、コントローラのアクション内にあります。

Here is an example code for src/groovy/ControllerGenericActions.groovy

class ControllerGeneric {
  def genericShow(Class clazz) {
    render clazz.get(params.id) as XML
  }
}

and in src/groovy/ControllerUtil.groovy

class ControllerUtil {
  def idExists (Class clazz) {
    return clazz.get(params.id) != null
  }

この場合はあまり役に立ちませんが、あなたはそのアイデアを得ます。

6
追加された
私はクロージャの使用に本当にではないので、私が間違いを犯している場合はご容赦ください。しかし、メソッドが 'genericShow(Class clazz)'のように作成された場合、そのメソッドをカレー化できますか?私は方法がカレー化できないが、クロージャーができるという印象を常に得ていた。
追加された 著者 Marco,
これがメソッド名の前に&を使用する理由です。メソッドをクロージャに変換します。
追加された 著者 Antoine,
私はこの解決策について質問があります:ControllerGeneric mixinユニットはテスト可能ですか?レンダリングはGrailsによって提供されるメソッドであり、コントローラを強化するためです
追加された 著者 Stefan Schubert-Peters,

一般的な方法で抽象コントローラを実装する( 'protected'ディレクティブを使用する)、それから実際のコントローラを拡張する。このメソッドの名前の先頭に「get」と「set」という単語を使用しないでください。うまくない、しかしそれは働く。

0
追加された
このメソッドの名前の冒頭に 'get'と 'set'という単語を使用しないことをお勧めするのはなぜですか?
追加された 著者 gotomanners,
それらを「ゲッター」または「セッター」として識別しないようにします。
追加された 著者 jenk,