( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

scalaのWebフレームワーク liftで遊ぶ(10) - ModelさんでCRUD画面その2

目次はこちら。
scalaのWebフレームワーク liftで遊ぶ 目次 - ゆろよろ日記


前回からの続きでCRUDする画面を作ってみるんだぜ。
Listは作ったので、残りはCreate,Update,Deleteだ。
一気に行くぞぉ。

次にCreate

じゃ、新規にレコードをCreateする画面をこんな感じで作りますよ。

f:id:yuroyoro:20081003160345p:image


src/main/webapp/user/add.html

<lift:surround with="default" at="content">
<table>
<lift:snippet type="UserCrud:add" form="POST"/>
</table>
</lift:surround>

えー、おなじみの<lift:snippet>タグでフォームを出力するってわけです。
じゃ、前回作ったUserCrudクラスにaddメソッドを追加でっせ。


src/main/scala/com/yuroyoro/lift/crudsample/snippet/UserCrud.scala

  private def saveUser(user: User) = user.validate match {
    case Nil => user.save; redirectTo("/user/index.html")
    case x => error(x); selectedUser(Full(user))
  }

  def add(xhtml: Group): NodeSeq =
    selectedUser.is.openOr(new User).toForm(Empty, saveUser _) ++ <tr>
    <td><a href="/user/index.html">Cancel</a></td>
    <td><input type="submit" value="Create"/></td>
    </tr>


解説です。

  private def saveUser(user: User) = user.validate match {
    case Nil => user.save; redirectTo("/user/index.html")
    case x => error(x); selectedUser(Full(user))
  }


Create,Update共通で使用されるメソッド。パターンマッチを使って、userモデルのvalidateメソッドがNilを返す場合はuserオブジェクトをsaveでDBに保存し、index.htmlにリダイレクトしている。
Nil以外を戻すようなら、入力チェックでエラーなのでerrorメソッドでエラー処理。

  def add(xhtml: Group): NodeSeq =
    selectedUser.is.openOr(new User).toForm(Empty, saveUser _) ++ <tr>
    <td><a href="/user/index.html">Cancel</a></td>
    <td><input type="submit" value="Create"/></td>
    </tr>


これはフォームを作るsnippet。selectedUser(RequestVarでリクエストをまたいで保持しているUserオブジェクト)をOpenOrで開いて(Canなので)、空だったら新しいUserオブジェクトを作成。
で、toFormメソッドでフォームとして出力するHTMLを吐き出している。


toFormはXHTMLのフォームを作り、ボタンがおされるとf: (A)=>Anyを実行。


toFormメソッドも、MetaMapperで定義されていて、デフォルトではtdタグとカラムのデータを出力するようになっている。
カスタマイズについても、同様にMapperRulesオブジェクトをいじればよし。

それでUpdate

つぎはUpdateです。

f:id:yuroyoro:20081003154540p:image

src/main/webapp/user/edit.html

<lift:surround with="default" at="content">
<table>
<lift:snippet type="UserCrud:edit" form="POST"/>
</table>
</lift:surround>


基本的にはCreateと同じで<lift:snippet>タグでフォームを出力する仕組みですよ。
またまたUserCrudクラスにeditメソッドを追加で。


src/main/scala/com/yuroyoro/lift/crudsample/snippet/UserCrud.scala

  def edit(xhtml: Group): NodeSeq =
    selectedUser.map(_.
         toForm(Empty, saveUser _) ++ <tr>
           <td><a href="/user/index">Cancel</a></td>
           <td><input type="submit" value="Save"/></td>
         </tr>
     ) openOr {error("User not found"); redirectTo("/user/index")}


解説です。


Listの画面を作るときに、各レコードの<tr>タグを出力するときに、
こんなコードを書きました。

    User.findAll(OrderBy(User.id, true)).flatMap(u => <tr>{u.htmlLine}
           <td>{link("/user/edit", () => selectedUser(Full(u)), Text("Edit"))}</td>
           <td>{link("/user/delete", () => selectedUser(Full(u)), Text("Delete"))}</td>
           </tr>)

これは、<td>タグ内のlink("/user/edit",() => selectedUser(Full(u)), Text("Edit"))で
linkをクリックしたときには/user/editに飛ばして、第2引数の関数オブジェクトを実行するようになってます。

ここで実行される処理では、UserCrudオブジェクトのメンバ変数のselectedUserに選択されたレコードのUserオブジェクトを設定します。


で、edit()のなかで、selectedUser変数に格納されているUserオブジェクトに対して、toFormで入力項目を生成するってわけですわ。



この辺の仕組みは、addと同じで、addの場合はnew Userで新規にオブジェクトを作成しましたが、editではリンクでクリックされたUserオブジェクトに対して同様のことをやってるだけです。
出力するタグについてもMapperRulesオブジェクトの変更で対応可能。


で、toFormの第二引数には、saveUser関数オブジェクトが設定されているので、このフォームがsubmitされたときにはsaveUser関数にselectedUserオブジェクトが引数で渡されて実行、レコードをsaveで更新する流れになりますねぇ。

最後にDelete

最後は削除です。

f:id:yuroyoro:20081003154541p:image


src/main/webapp/user/delete.html

<lift:surround with="default" at="content">
<lift:snippet type="UserCrud:confirmDelete" form="POST">
Do you really want to delete <xmp:username/>
<a href="/user/index">No</a> <xmp:delete/>
</lift:snippet>
</lift:surround>

削除画面は、消してもOK?メッセージとボタンがあります。
add,editと違ってレコードの内容をformでは出力しませんね。


で、cunfirmDeleteをUserCrudに追加します。


src/main/scala/com/yuroyoro/lift/crudsample/snippet/UserCrud.scala

  def confirmDelete(xhtml: Group): NodeSeq = {
    (for (user <- selectedUser.is) // find the user
     yield {
       def deleteUser(ignore: String) {
         notice("User "+(user.firstName+" "+user.lastName)+" deleted")
         user.delete_!
         redirectTo("/user/index")
       }

       bind("xmp", xhtml, "username" --> (user.firstName.is+" "+user.lastName.is),
      "delete" --> submit("Delete", deleteUser))

     }) openOr {error("User not found"); redirectTo("/user/index")}
   }


deleteConfirm内でやっていることは、yield内のブロックでdeleteUserという実際にレコードを削除する関数を定義し、bindでHTML内にuser名を出力しています。


submitで、yeald内で定義されたdeleteUser関数を渡しているので、ボタンを押してフォームが送信されると、deleteUser関数が実行されてレコードが削除されます。


noticeは、メッセージを出力する処理で、net.liftweb.http.Sで定義されています。
他にもwarningや、oepnOrでselecteduserが空だった場合のエラー処理として呼び出していたerrorなどがあります。
画面には、こんな感じで表示されますよ?

f:id:yuroyoro:20081003154538p:image


これで、liftのCRUDはおしまいです。
おもったより面倒?
なれるといろいろできるよ。
まぁソース読みましょう。