( ꒪⌓꒪) ゆるよろ日記

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

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

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


ずいぶんと間が開いてしまったけど、liftネタの続きをやるよ。
前回はModelを作ったので、そいつをCRUDする画面を作ってみるんだ。

手順

liftには、Railsのscaffoldみたいにコマンド一発でドゥーン!とCRUDができるなんてことはありません。
自分でコードを書きます。コーディングばんざい!!

プロジェクト作成

まずはCRUD用サンプル用のプロジェクトを作成する。
mavenのコマンドでゴー。

mvn archetype:create -U  -DarchetypeGroupId=net.liftweb  -DarchetypeArtifactId=lift-archetype-basic -DarchetypeVersion=0.9  -DremoteRepositories=http://scala-tools.org/repo-releases -DgroupId=com.yuroyoro.lift.crudsample -DartifactId=crudsample


cd crudsample
mvn eclipse:eclipse

groupIdだけ変えてます。ついでにプロジェクトをEclipse用にしておく。
Eclipseにプロジェクトをインポートして準備完了だ。

Model

Modelはプロジェクトを生成したときにできるUser Modelををそのまま流用します。

まずはList

手始めに、一覧画面を作りますよ。
html作るよ。これは今まで特に変わったところはない。


src/main/webapp/index.html

<lift:surround with="default" at="content">
<a href='/add'>Add a User</a>
<table width="90%">
<lift:snippet type="UserCrud:users"/>
</table>
</lift:surround>


4行目でUserCrud:usersっていうsnippetを呼び出している。
このsnippetの中で、Userの一覧をDBから検索するように実装しますよ。


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

package com.yuroyoro.lift.crudsample.snippet

import com.yuroyoro.lift.crudsample.model._
import scala.xml.{NodeSeq, Text, Group}
import net.liftweb.http._
import net.liftweb.http.S
import net.liftweb.mapper._
import net.liftweb.http.S._
import net.liftweb.http.SHtml._
import net.liftweb.util.Helpers._
import net.liftweb.util._
import java.util.Locale

class UserCrud {

  private object selectedUser extends RequestVar[Can[User]](Empty)
  
   /**
   * Get the XHTML containing a list of users
   */
  def users: NodeSeq = {
    User.find() match {
      case Empty => User.create.firstName("Archer").lastName("Dog").email("archer@dogfood.com").password("mypassword").save
      case _ =>
    }
    // the header
    <tr>{User.htmlHeaders}<th>Edit</th><th>Delete</th></tr> ::
    // get and display each of the users
    User.findAll(OrderBy(User.id, true)).flatMap(u => <tr>{u.htmlLine}
           <td>{link("/simple/edit", () => selectedUser(Full(u)), Text("Edit"))}</td>
           <td>{link("/simple/delete", () => selectedUser(Full(u)), Text("Delete"))}</td>
           </tr>)
  }
}

ここまで作って、Boot.scalaにSiteMapを追加して動かすと、こんな感じになりまっせ。
f:id:yuroyoro:20080919203703p:image



じゃあ中身の解説ですよ。


22行目:

    User.find() match {
      case Empty => User.create.firstName("Archer").lastName("Dog").email("archer@dogfood.com").password("mypassword").save
      case _ =>
    }


User Modelのfind()メソッドでusersテーブルから1件検索しています。
case Empytは検索して0件だった場合で、User.create....で1件レコードを作成。

この際に、設定するカラムをメソッドチェーン的につなげて最後にsaveを呼び出すことで、データの設定と保存を同時にやってるて寸法よ。


26行目:

    // the header
    <tr>{User.htmlHeaders}<th>Edit</th><th>Delete</th></tr> ::
    // get and display each of the users
    User.findAll(OrderBy(User.id, true)).flatMap(u => <tr>{u.htmlLine}
           <td>{link("/simple/edit", () => selectedUser(Full(u)), Text("Edit"))}</td>
           <td>{link("/simple/delete", () => selectedUser(Full(u)), Text("Delete"))}</td>
           </tr>)
  }


ここからがsnippetの返すXmlNodeなんだけど、tableタグのヘッダー部分をUser.htmlHeadersで出力している。
このhtmlHeadersなんだけど、MetaMapperで定義されていて、デフォルトではthタグでフィールド名を出力するようになっている。


そのまんまだとth以外出せないし、日本語の名前とか出したい場合に困るので、使いづらい。
これをカスタマイズするにはMapperRulesオブジェクトをoverrideすればよい。

Google groupでも議論されていて、作者のDavid Pollakがサクッと対応してしまったのでまだ公式ドキュメントには載ってない。
Automatically generated forms


詳しいやり方は今度別なエントリで書く予定。


で、レコード本体の部分は、User.findAllで全件検索したデータ(OrderByでID順にソート)がList[Full]で返ってくるので、flatMapで1レコードづつ展開したうえでtrタグを出力している。


trタグの中身は、u.htmlLineでUserモデルの中身を出力している。
このhtmlLineも、ヘッダーと同じくMetaMapperで定義されていて、デフォルトではtdタグとカラムのデータを出力する。


カスタマイズについても、同様にMapperRulesオブジェクトをいじればオケ。


ちょっと長くなったので、今回はここまでにしておく。
次回は、Create,Update,Deleteを書きますよ。