Play2.3でCRUDを使った一覧ページの作成してみる

今回も前回に引き続き、簡単なブログ機能を作成したときに行ったことをまとめていきます。
前回はフォームの内容をDBに反映させる機能をつくりました。今回は一覧表示ページで必要になるDBに入っている内容をとってくる処理をつくっていきます。
playとSquerylでフォームの内容をDBに反映させるまで - hikonori07’s blog

今回使用するpostテーブルやschema定義は↓の記事に書いています。

play frameworkでSqueryl Schema及びentity定義するまで - hikonori07’s blog


今回は主にこんな感じで機能つくっていきます。

・postテーブルから必要な情報を取ってきて一覧表示する
・投稿1つひとつを削除できるようにする
・投稿内容を変更できるようにする


・postテーブルから必要な情報を取ってきて一覧表示する

ここではselectで取ってきたpostテーブルのデータをcontrollerでviewに渡して、viewでそのデータ加工して一覧表示させるということを行います。

//app/controllers/Application.scala
  def index = Action {
    transaction {
      val posts = AppDB.postTable.toList
      Ok(views.html.index(posts))
    }
  }

↑selectでpostテーブルから必要な情報をとってきて、index.scala.htmlに取ってきた情報を渡しています。

//app/views/index.scala.html
@(posts: List[db.Post])

<ul>
@for( post <- posts ){
<li>@post.title, @post.content</li>
}
</ul>

viewでやっていることはほとんど↓のページにまとめられています。
https://www.playframework.com/documentation/2.3.x/ScalaTemplates

一覧の表示はこんな感じでさらっとできました。

・投稿1つひとつを削除できるようにする

ここではview部分で削除できるようにするために必要なroutesの設定、controllerでdeleteアクションの作成などを行います。

//app/views/index.scala.html
<ul>
@for( post <- posts ){
<li>@post.title, @post.content</li>
  ■<a href="@routes.Application.deletePost(post.id)">delete</a>
}
</ul>

idはさっきの一覧を取ってくるselect文では表示されていませんでしたが、↑のように指定することでidを取得することが可能です。

//conf/routes
GET    /deletePost/:id             @controllers.Application.deletePost(id: Int)

↑getメソッドでdeletePost/:idに遷移した際にApplicationのdeletePostメソッドを呼び出すように設定

//app/controllers/Application.scala
  def deletePost(id: Int) = Action {
      inTransaction(AppDB.postTable.deleteWhere(p => p.id === id))
      Redirect(routes.Application.index())
  }

↑ではviewから受け取ったidをdeleteして、indexページにリダイレクトするという一連の処理をしています。

・投稿内容を変更できるようにする

ここはつまずいたので、詳しく書いていきます。
やることとしては、indexページからupdateページに移動できるようにする。
updateページを開くと現在の該当カラムの内容がフォームに表示されている状態にする。
updateページで入力されたフォームの内容を該当idのカラムにupdateできるようにする。
では下に今回のupdateのための処理をずらっと書いたので、1つずつ見て行きます。

//app/views/update.scala.html
@(form: play.api.data.Form[db.Post])

//フォームを宣言して、actionを指定
@helper.form(action = routes.Application.updatePost) {
//hiddenに対応したhelperはないため↓のようにHTMLで書く
<input type="hidden" name="id" value="@form.data.get("id")" />
@helper.inputText(form("title"))
@helper.inputText(form("content"))
<input type="submit"/>

}

↑updateページの内容です。
その他のhelperの知りたいときは↓
https://www.playframework.com/documentation/2.3.x/ScalaForms
https://github.com/playframework/playframework/tree/master/framework/src/play/src/main/scala/views/helper

//app/views/index.scala.html
<ul>
@for( post <- posts ){
<li>@post.title, @post.content
<p>■<a href="@routes.Application.deletePost(post.id)">delete</a>
 ■<a href="@routes.Application.update(post.id)">update</a></p>
</li>
}
</ul>

indexページには変更したいpostの下に、編集ページヘのリンクが表示されるようにしました。

//app/conf/routes
# update post form
//indexページからの遷移先を作成
GET     /update/:id                @controllers.Application.update(id: Int)
//上のupdateページのフォーム送信された際に使うupdatePostメソッドへのroutes設定
POST    /updatePost                @controllers.Application.updatePost
//変更前
    case class Post(
      title: String,
      content: String) extends KeyedEntity[Long] {
        val id: Long = 0

    }

//変更後
    case class Post(
      id: Long,
      title: String,
      content: String) extends KeyedEntity[Long] {
    }

上が一番つまずいた点だったのですが、変更前の方法だとidを0しか渡してくれません。
insertの場合はidは0で良かったのですが、updateの際にはidは0ではなく投稿と紐付いているidをしっかり渡してくれないと困ります。
なので上のように記述を変更します。

//app/controllers/Application.scala
  val postForm = Form(
    mapping(
      "id" -> ignored(0L),
      "title" -> text,
      "content" -> text
    )(Post.apply)(Post.unapply)
  )

  val updateForm = Form(
    mapping(
      "id" -> longNumber,
      "title" -> text,
      "content" -> text
    )(Post.apply)(Post.unapply)
  )

  def update(id: Int) = Action {
    inTransaction {
   //該当idに入っている内容を表示させるための処理
      from(AppDB.postTable)(p => where(p.id === id) select (p)).headOption.map { post =>
        val filledForm = updateForm.fill(post)
        Ok(views.html.update(filledForm))
      }.getOrElse(
          BadRequest
        )
    }
  }
  //フォームの内容をupdateするための処理
  def updatePost = Action { implicit request =>
    updateForm.bindFromRequest.fold(
      errors => { BadRequest },
      post => {
        inTransaction(AppDB.postTable.update(post))
        Redirect(routes.Application.index())
      }
    )
  }

postFormがinsert用のフォームです。
updateFormがupdate用のフォームです。
insertの際にはid = 0なので0に固定するためにignored(0L)のような記述をしています。
↓ページのIgnored values参照
https://www.playframework.com/documentation/2.3.x/ScalaForms

updateFormのidはLongなのでlongNumberにします。
How to handle Long and different formats in a Play! Scala form? - Stack Overflow

これでFormはひと通り作成できました。
あとは通常どおりupdateの処理を作成していきます。

update処理で行っているようなことは前のブログ記事でも紹介しましたので省略します。
詳しく知りたい方は、この記事を参考にしてみてください。
playとSquerylでフォームの内容をDBに反映させるまで - hikonori07’s blog

ということで長くなりましたが、これで投稿の削除、更新、一覧表示のされている一覧ページが見れるようになりました。