uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

PHPのようにGrailsを使う方法

GrailsアプリケーションではGSPのビューは以下の方法で表示できます(他にもあるかも)。

  • コントローラーアクションの結果として表示
    • renderで指定したビュー
    • コントローラ名とアクション名から定まるデフォルトのビュー
  • ビューを直接表示する
    • URLMappingでURLパターンからビューを指定する

しかし、「ビューだけ」の、GSPの集合だけで機能するサイトは作れません。JSPで言うと、JSPだけのServlet無しのWebサイト、あるいはPHPの埋め込みページだけで構成するようなサイトが作れないのです。

そうなのはおそらく意図的であり、それがWeb開発の黎明期にアンチパターンとしてみなされてきたので排除されているのでしょう。上記のような制約「GSPはコントローラからの画面遷移を経て表示される」があることのメリットは、GSP内で使用できるデータの由来を明確化できること、そして、画面遷移のリンク情報をページの中にばらまくのではなく、コントローラのコードで指定することでしょう。

ただ、そうだとしても、以下のような開発手順をとりたいときがあります。

  • 開発時に、まずHTMLでのモックアップがある(デザイナーや企画者が作成するなど)
  • それをGSPに一括変換する
  • 適宜以下を実施する
    • FORMの部分をGSPタグ<g:form>でおきかえる
    • レイアウトをSiteMeshでおきかえる
    • 静的リソースはアセットにして…

現状のGrailsでは、上記の手順にはなりません。ページの持つ機能をコントローラ・アクションの階層に分割し、そしてコントローラアクションの結果としてのページ群に対応させ…、というトップダウンの機能設計をせざるを得ず、直接画面確認に行けないのです。

本記事では、この問題を解決するために、ページ間遷移について個別のコントローラ無しGSPの集合としてアプリを作る、つまり「PHPのようにGrailsを使う」方法を紹介します。

DispatchController

showPageというアクションを1つだけ持った以下のコントローラDispatchControllerを用意します。

package common

class DispatchController {

    /** grails-app/view/pages配下に配置された、コントローラに所属しない
     * (コントローラのアクション名と一致していることを期待せず、リンク
     * をたどることやメニュー選択の結果として表示されることを想定して作
     * 成されている)ページを表示する。
     */

    def showPage() {
        render view:"/pages/${params.pageName}"
    }
   
}

URLMappingに以下を追加します。

        "/pages/$pageName"(controller: "dispatch", action: "showPage")
        "/"{
            controller = "dispatch"
            action = "showPage"
            pageName = "index"
        }


これで準備は終わり。あとは

配下に、たとえば

というGSP(htmlの拡張子をgspにリネーム)を置けば、

      <a href="${request.contextPath}/pages/a">..</a>

というようなリンクで相互にGSPビューを表示できるようになります。views/pages/index.gspがトップページになります。
あもちろん、<g:link>とか使っても良いです。

まとめ

コントローラ・アクションの編成の方法論というのは、Grails初学者にとっては難しいところです。

Scaffoldではドメイン/アクションをベースにしたビューを生成してくれますが、Webサイトというものが一般には「DBテーブルエディタ」ではない以上、この構造は必ずしも一般的ではなく、参考にならないときもあります。

テーブル単位のCRUDに限らない、コントローラ・アクションの階層をどう編成すべきか。この問いに、なんとか解を出さないと、index.gsp以外の1ページを表示することすらままならないかもしれないわけです。デフォルトのindex.gspのようにページごとにURLMapping追加していってもいいんですが、面倒だし不毛ですよね。

多数のページから呼ばれ得る多数のコントローラの関係があるとき、むしろコントローラ/アクションの階層をページ集合とは独立した機能群として設計する方がやりやすいときもあるでしょう。

このPHPぽいアプローチならば、動くサイトを拡張しながら、コントローラを都度考えていくことができます。また、それとは別に、Grailsの初心者にとってのとっつきは、はるかに良くなると思います。

もちろん、この技法と通常のコントローラベースのアプローチを組合せても全く問題ありません。

問題もありえます。この方法だけで、大規模サイトとか業務システムのサイトの全体をつくるべきではないでしょう。単機能の1ページサイトでも不要でしょう。でも、ヘルプページとか、社長のご挨拶ページとか、サイトマップとか、そういうページを含むサイト全体を作るために、この技法を織り交ぜて適用するのが有用なことがあるでしょう。

ということで、選択肢の一つとして心にとめておいておくと便利なのではないかと思い、紹介させてもらいました。