3

grailsアプリケーションでコマンドオブジェクトを使用する際に好ましいパターンがあるのではないかと思います。特に、オブジェクトを保存するための別のメソッドを定義する必要がある場合、またはフォームの表示と保存に同じコントローラーメソッドを使用する必要がある場合はどうなりますか?

フォームを表示してオブジェクトを保存するための別々のメソッドの例を示しましょう

def user(int id) {} // shows the edit user form - user.gsp. Submit takes us to saveUser method
def saveUser(UserCommand cmd) {} // actually saves the user, then redirects somewehre else

すべてが機能するはずですが:

検証エラーが発生した場合、saveUserメソッドはメソッドが実行したロジック全体を実行する必要がありuserます。ユーザーフォームを表示する前に、データベースから追​​加のオブジェクトをロードしたり、計算を実行したりする必要がある場合は、フォームを再度表示して検証エラーを含める必要があるため、もう一度実行する必要があります。これは不必要なコードの重複につながります。userコマンドオブジェクトとそれに関連するエラーが失われるため、検証が失敗したときにメソッドにリダイレクトできません。したがって、検証エラーを表示できません。

フォームの保存と表示に同じ方法を使用した別の例

def user(UserCommand cmd, Integer id) {
   def u=User.load(id)
   if (request.method=="POST"&&cmd.validate()) {
      // populate u with command object values and save in database
      // then redirect somewhere
   }
}

この例では、コードを複製する必要がありません。ユーザーの保存は、POSTリクエストがあった場合にのみ実行されます。それ以外の場合は、フォームのみが表示されます。検証エラーの場合、それらはアクセス可能であり、gspページに表示できます。

主な問題はGET、ユーザーページへのリクエストがある場合(つまり、user.gspフォームが表示されている場合でも)、空のコマンドオブジェクトインスタンスが作成および検証されることです(同じコントローラーで定義されているため)。したがって、フォームが表示されるたびに、提供された値が空であるという検証エラーが発生します(コマンドオブジェクトが空であるため)

どちらのシナリオも正しく機能するように簡単に変更できます(たとえば、シナリオ1のセッションでリダイレクトする前にコマンドオブジェクトを保存しPOST、シナリオ2でリクエストがあった場合にのみ検証エラーを表示します)が、追加のコードが必要で、あまり洗練されていないようです。

この問題に対するより単純な、より多くの「grails」ソリューションはありますか?

4

3 に答える 3

1

問題: コントローラー アクションでのモデル作成ロジックの複製。たとえば、著者のリストを複数のアクションでモデルに挿入します。

解決策私は時々エレガントだと思います:

def afterInterceptor = { model ->
    model.authors = authorService.list()
}

詳細については、Grails のドキュメントを確認してください: http://grails.org/doc/latest/ref/Controllers/afterInterceptor.html

于 2014-07-31T18:43:55.820 に答える
1

この場合、私が通常行うことのサンプルを次に示します。

def create(MeetingCommand cmdMtn) {
    switch (request.method) {
    case 'GET':
        cmdMtn = new MeetingCommand() 
        bindData cmdMtn, params, [include: boundProperties]
        createEditModel(cmdMtn: cmdMtn)
        break
    case 'POST':
            def meetingInstance
            Meeting.withTransaction { status ->
                try{
                    Boolean okValidated = (cmdMtn.validate() && !cmdMtn.hasErrors())
                    if (okValidated) {
                        meetingInstance = meetingService.bindInstance(cmdMtn)
                        flashMessage 'meeting.created', FLASH_CLASS_SUCCESS, [meetingInstance.id], 'Meeting creato'  
                        redirect action: ACTION_SHOW, id: meetingInstance.id
                    }
                    else {
                        log.error cmdMtn
                        log.error cmdMtn.errors
                        status.setRollbackOnly()
                        createEditModel(cmdMtn: cmdMtn)
                    }
                } catch (e){
                    status.setRollbackOnly()
                    log.error e
                    createEditModel(cmdMtn: cmdMtn)
                }
            }
        break
    }
}
protected createEditModel(mdl = [:]) {
    mdl
}

http://blog.freeside.co/post/41774629876/semi-restful-scaffolded-controllersからインスピレーションを得て、ドメイン永続化ロジックをサービス層に移動しました。

于 2013-03-21T16:39:35.990 に答える
0

isBlankコマンド オブジェクトに getter を追加し始めました。

class UserCommand {
    String username
    String password

    boolean isBlank() { !username && !password }
}

次に、コントローラーで空のコマンドを再作成するため、検証は実行されません。

def createUser(UserCommand cmd) {
    if (cmd.blank) cmd = new UserCommand()
    //...render form, there will be no errors on inital view
}

def saveUser(UserCommand cmd) {
    if (cmd.hasErrors()) {
        return createUser(cmd)
    } else {
    //...and so on
}

唯一の欠点は、ユーザーが空白のフォームを送信した場合、検証エラーが発生しないことですが、一方で、空白のフォームに何かを入力する必要があることは明らかなはずですが、そうでなければなぜそこにあるのでしょうか?

于 2014-12-10T14:39:09.437 に答える