コメント機能

前回は投稿とユーザーの紐付けを行ったので、今回は投稿に対して一般のユーザーがコメントをできる機能を作っていきます。

流れとしてはこんな感じです。

・commentテーブルの作成&外部キーの追加
・entityとschemaの作成
・全ての投稿一覧が見れるページを作成
・各投稿が閲覧でき、かつコメントするページ
・routesファイルの設定
・controllersファイルの設定

では早速作っていきましょう。

Post Entity

case class Post(
  @Column("post_id")
  id: Long,
  @Column("title")
  title: String,
  @Column("content")
  content: String,
  @Column("user_id")
  userId: Int ) extends KeyedEntity[Long] {
  }

User Entity

case class User(
  @Column("user_id")
  id: Long,
  @Column("user_name")
  userName: Option[String],
  @Column("mail_address")
  mailAddress: Option[String] ) extends KeyedEntity[Long] {
  }

・commentテーブルの作成&外部キーの追加

ここではuser_idはuserテーブルのuser_idに、post_idはpostテーブルのpost_idにリレーションさせています。

create table comment (
  comment_id integer primary key,
  comment text,
  post_id integer REFERENCES post(post_id)
);

・entityとschemaの作成

//entityの作成
case class Comment(
  @Column("comment_id")
  id: Int,
  @Column("comment")
  comment: Option[String],
  @Column("post_id")
  postId: Int ) extends KeyedEntity[Int] {
    }


object AppDB extends Schema {
  ...省略...
  //schemaの作成
  val commentTable = table[Comment]("comment")
}

・全ての投稿一覧が見れるページを作成

まずはviewから

//allposts.scala.html
@(posts: List[(db.Post, db.User)])

<ul>
    @for( (post, user) <- posts ){
    <li>title: @post.title
        <br/>content: @post.content
        <br/>author: @user.userName
        <p>
          <a href="@routes.Application.entry(post.id)">view comments</a>
        </p>
    </li>
    }
</ul>

・各投稿が閲覧でき、かつコメントするページ

今回コメントは各投稿閲覧時のページからできるようにします。

@(post: db.Post, comments: List[db.Comment], form: play.api.data.Form[db.Comment])


<h2>@post.title</h2>
<p>@post.content</p>

<ul>
    <li>
    @for( comment <- comments){
    <p>Viewer's Comment: @comment.comment</p>
    </li>
    }
</ul>

@helper.form(action = routes.Application.addComment) {

<input type="hidden" name="post_id" value="@post.id"/>
@helper.inputText(form("comment"))
<input type="submit"/>

}

・routesファイルの設定

今度はroutesの設定を行っていきます。
ここでも今までと比べてあまり変わったことはしていないので、細かい説明は省きます。

//全ての投稿を表示
GET   /                          @controllers.Application.index

//各投稿を表示
GET   /entry/:id                        @controllers.Application.entry(id: Long)

//commentフォームのデータの送信先として
POST  /addComment                       @controllers.Application.addComment

・controllersファイルの設定

前回は投稿とユーザーの紐付けを行ったので、今回は投稿に対して一般のユーザーがコメントをできる機能を作っていきます。

流れとしてはこんな感じです。

・commentテーブルの作成&外部キーの追加
・entityとschemaの作成
・全ての投稿一覧が見れるページを作成
・各投稿が閲覧でき、かつコメントするページ
・routesファイルの設定
・controllersファイルの設定

では早速作っていきましょう。

Post Entity
>|scala|
case class Post(
  @Column("post_id")
  id: Long,
  @Column("title")
  title: String,
  @Column("content")
  content: String,
  @Column("user_id")
  userId: Int ) extends KeyedEntity[Long] {
  }

User Entity

case class User(
  @Column("user_id")
  id: Long,
  @Column("user_name")
  userName: Option[String],
  @Column("mail_address")
  mailAddress: Option[String] ) extends KeyedEntity[Long] {
  }

・commentテーブルの作成&外部キーの追加

ここではuser_idはuserテーブルのuser_idに、post_idはpostテーブルのpost_idにリレーションさせています。

create table comment (
  comment_id integer primary key,
  comment text,
  post_id integer REFERENCES post(post_id)
);

・entityとschemaの作成

//entityの作成
case class Comment(
  @Column("comment_id")
  id: Int,
  @Column("comment")
  comment: Option[String],
  @Column("post_id")
  postId: Int ) extends KeyedEntity[Int] {
    }


object AppDB extends Schema {
  ...省略...
  //schemaの作成
  val commentTable = table[Comment]("comment")
}

・全ての投稿一覧が見れるページを作成

まずはviewから

//allposts.scala.html
@(posts: List[(db.Post, db.User)])

<ul>
    @for( (post, user) <- posts ){
    <li>title: @post.title
        <br/>content: @post.content
        <br/>author: @user.userName
        <p>
          <a href="@routes.Application.entry(post.id)">view comments</a>
        </p>
    </li>
    }
</ul>

・各投稿が閲覧でき、かつコメントするページ

今回コメントは各投稿閲覧時のページからできるようにします。

@(post: db.Post, comments: List[db.Comment], form: play.api.data.Form[db.Comment])


<h2>@post.title</h2>
<p>@post.content</p>

<ul>
    <li>
    @for( comment <- comments){
    <p>Viewer's Comment: @comment.comment</p>
    </li>
    }
</ul>

@helper.form(action = routes.Application.addComment) {

<input type="hidden" name="post_id" value="@post.id"/>
@helper.inputText(form("comment"))
<input type="submit"/>

}

・routesファイルの設定

今度はroutesの設定を行っていきます。
ここでも今までと比べてあまり変わったことはしていないので、細かい説明は省きます。

//全ての投稿を表示
GET   /                          @controllers.Application.index

//各投稿を表示
GET   /entry/:id                        @controllers.Application.entry(id: Long)

//commentフォームのデータの送信先として
POST  /addComment                       @controllers.Application.addComment

・controllersファイルの設定

package controllers

import models.UserData
import play.api.mvc._
import play.api.data._
import org.squeryl.PrimitiveTypeMode._
import db._
import play.api.data.Forms._
import securesocial.core._


class Application(override implicit val env: RuntimeEnvironment[UserData]) extends Controller with securesocial.core.SecureSocial[UserData] {

  //コメントのフォームの定義
  val commentForm = Form(
    mapping(
      "comment_id" -> ignored(0),
      "comment" -> optional(text),
      "post_id" -> number
    )(Comment.apply)(Comment.unapply)
  )

  def index = Action { implicit request =>
    transaction {
      val posts = from(AppDB.postTable, AppDB.userTable)((p, u) =>
        select(p, u)).toList
      Ok(views.html.allpost(posts))
    }
  }

  def entry(id: Long) = Action { implicit request =>
    transaction {
      val comments = from(AppDB.commentTable)((c) =>
        where(c.postId === id)
        select(c)).toList

      from(AppDB.postTable)((p) =>
      where(p.id === id)
      select(p)).headOption.map { postContent =>
        Ok(views.html.entry(postContent, comments, commentForm))
      }.getOrElse {
        NotFound("404 Page not found")
      }
    }
  }

  def addComment = Action { implicit  request =>
    transaction {
      commentForm.bindFromRequest.fold(
        errors => {
          BadRequest
        },
        comment => {
          inTransaction(AppDB.commentTable.insert(comment))
          Redirect(routes.Application.entry(comment.postId))
        }
      )
    }
  }

}

簡単に各Actionでどのようなことをしているかを説明していきます。

indexでは、ユーザー関わらず全ての投稿を表示させています。

entryでは該当のpost_idがない場合、404ページを表示するしています。
今回は簡易的に「404 Page not found」の文字のみ表示させています。

addCommentではフォームの内容が正しく受け取れた場合、DBにその内容を反映させた後該当の投稿ページにリダイレクトさせています。

これでcompileしてerrorが表示されなければ、runコマンドのあとページが正しく表示されることを確認しましょう。
viewで正しい表示が確認できたら、次はコメントフォームからコメントしてcommentテーブルに入力したデータが正しく反映されていることを確認しましょう。

これでコメント機能の完成です。
次回は全投稿閲覧ページにページング機能を作成していきたいと思います。