GRAILSメモ

コントローラまわり

  • Controllerはリクエストごとにインスタンスが生成される。Controllerのフィールドを使用してもスレッドセーフということ。でもフィールド使うニーズが今のところわかってない。
  • 入力は params プロパティで受け取る。
  • モデルが指定されない場合はVIEWからコントローラのプロパティが参照できる。
  • render
    • コントローラがVIEWを介さずに直接短い応答を返すときは render "msg"
    • コントローラがVIEWを明示的に指定する場合 render(view:"view", model:map) ※コントローラから指定する場合は render(view:"controller/view", model:map)
    • XMLJSONをさくっと返す。
      • render Book.list() as XML または render Book.list().encodeAsXML()
      • render Book.list() as JSON または render Book.list().encodeAsJSON()
  • redirect
    • 同じコントローラの別のアクションにリダイレクトする場合は redirect(action:"xxxxx")
    • 別のコントローラにリダイレクトする場合は redirect(controller:"xxxx", action:"xxxx")
    • URI指定でリダイレクトする場合は redirect(uri:"/xxxxx")
    • URL指定でリダイレクトする場合は redirect(url:"http://xxxxxxx")
    • 入力(params)は params 引数で渡せる redirect(action:"xxxx", params:params)
    • 現在のparamsをそのまま渡さなくても良い redirect(action:"xxxx", params:[newparam:"newparam"])
  • chain
    • 現在のアクションの結果(モデル)とチェーンで呼び出したアクションの結果(モデル)をマージする chain(action:"second", model:map)
    • chainで呼び出されたアクションではそれまでの結果(モデル)を chainModel として参照できる
    • chainの場合も params で入力を渡せる。
  • beforeInterceptor (アクション実行前に介入できるアクション)
    • ログインの強制に使える。
    • 全コントローラで強制する方法はあるのかな?→Filter
    • メソッドをクロージャとして扱うように .& 演算子が使われている。 auth をアクションにすると公開されてしまうのでメソッドにしているようだ。
    • 除外は except で指定している。リストにして except:["login", "register"] などにしてもよい。
    • 逆に特定アクションにのみ介入する場合は only:"action" や only:["action1", "action2"] になる。
def beforeInterceptor = [action:this.&auth,except:'login']
// defined as a regular method so its private
def auth() {
    if(!session.user) {
        redirect(action:'login')
        return false
    }
}
def login = {
    // display login page
}
  • afterInterceptor(ビューに制御が移る前に介入できるアクション)
    • ビューを変更してしまうことができる
def afterInterceptor = { model, modelAndView ->
    println "Current view is ${modelAndView.viewName}"
    if(model.someVar) modelAndView.viewName = "/mycontroller/someotherview"
    println "View is now ${modelAndView.viewName}"
}
  • URLマッピングgrails-app/conf/UrlMappings.groovy に追加する。
    • static mappings = { "/product/$id" (controller:"product") } の場合、params.id に $id 部分のURIが入る。
  • filter
    • grails-app/conf に *Filters の名前でクラスを配置する。
      • ログイン強制はこんなのになる。
class SecurityFilters {
   def filters = {
       loginCheck(controller:'*', action:'*') {
           before = {
              if(!session.user && !actionName.equals('login')) {
                  redirect(action:'login')
                  return false
               }
           }
       }
   }
}

サービスまわり

  • grails-app/services に *Service.groovy の名前で保存する。
  • ビジネスロジックを処理するメソッドを持たせる。
  • コントローラはリクエストごとにインスタンス化されるが、サービスはシングルトン。サービスはステートレスにするか、注意深く同期をしなければならない。
    • この動作を変更するにはクラス内で static scope = "request" などを設定する(request, singleton のほかにも設定値がある)。
  • コントローラは使いたいサービスをフィールドで宣言しておく。GRAILSがサービスのインスタンスをセットしてくれる。

モデルまわり

  • Book( String title, String author ) が実際 HSQLDB 上でどう定義されているか。
    • 列IDとVERSIONはGRAILSが追加した管理用の列と思われる。
CREATE MEMORY TABLE BOOK(
    ID BIGINT
        GENERATED BY DEFAULT AS IDENTITY(START WITH 1)
        NOT NULL
        PRIMARY KEY,
    VERSION BIGINT NOT NULL,
    AUTHOR VARCHAR(255) NOT NULL,
    TITLE VARCHAR(255) NOT NULL
);
  • レコード更新時にプロパティを絞る方法(意図しないプロパティを更新されないように・・・) ※pはレコード
    • p.properties['firstname', 'lastname'] = params
    • bindData(p, params)
    • bindData(p, params, [exclude:"dateOfBirth"])
    • bindData(p, params, [include:["firstname", "lastname"]])