読者です 読者をやめる 読者になる 読者になる

PlayFramework事始め

PlayFramework 事始め

PlayFramework 事始め

playのインストール

公式から最新のactivatorをダウンロードしてbinディレクトリにパスを通しておく。

プロジェクトの作成

activatorからプロジェクト作成

新規プロジェクトの作成は"activator new"から行う

activator new

giter8からplayのプロジェクトを作成

giter8を使うことでsbt newでplayのプロジェクトを作成できる

sbt new playframework/play-scala-seed.g8

上記コマンドで作成したプロジェクトをInteliJに取り込んでみるとbuild.sbtでPlayのプラグインがcannot resolveになることがあります。 その場合は、以下の手順でInteliJのプロジェクトを作成します。

1.既存のtarget, .idea, .idea_modulesを削除
2.project/plugins.sbtに以下を追記
 addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")

3."sbt gen-idea"を実行してプロジェクトファイルを作成。それからはInteliJで取り込んでみるとbuild.sbtで発生していたcannot resolveが消えていると思います。
 それでもcannot resolveになる場合はもう一度.ideaを削除して開きなおしてみると直るかもしれないです。

コンソールの起動

cd プロジェクト名
activator

サーバを起動

[プロジェクト名] $ run

コンパイル

[プロジェクト名] $ compile

テスト

[プロジェクト名] $ test

デバッグ

ポート指定でデバッグ起動できる

activator -jvm-debug 9999

sbtの利用

Play コンソールは普通の sbt コンソールでもあるため、sbtの機能も利用することができます。 ソース更新のたびにコンパイルする

[プロジェクト名] $ ~ compile

サーバ稼働中ソース更新のたびにビルドする    

[プロジェクト名] $ ~ run

ソースが更新されるたびにテストする

[プロジェクト名] $ ~ test

IDE

eclipseで開発する

project/plugins.sbtに以下のプラグインを追加する設定を行う

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

それからeclipseのプロジェクトファイルを生成する

activator eclipse

InteliJ IDEA

ファイルを開くでプロジェクトとして認識してくれる

Playアプリケーションの構造

app                      → アプリケーションソース
 └ assets                → コンパイルされたアセットソース
    └ stylesheets        → 通常は LESS CSS ソース
    └ javascripts        → 通常は CoffeeScript ソース
 └ controllers           → アプリケーションコントローラ
 └ models                → アプリケーションビジネス層
 └ views                 → テンプレート
build.sbt                → アプリケーションビルドスクリプト
conf                     → 設定ファイル、および (クラスパス上の) その他のコンパイルされないリソース
 └ application.conf      → メイン設定ファイル
 └ routes                → ルート定義
dist                     → プロジェクト成果物に含める任意のファイル
public                   → 公開アセット
 └ stylesheets           → CSS ファイル
 └ javascripts           → Javascript ファイル
 └ images                → 画像ファイル
project                  → sbt 設定ファイル群
 └ build.properties      → sbt プロジェクトの目印
 └ plugins.sbt           → Play 自身の定義を含む sbt プラグイン
lib                      → 管理されていない依存ライブラリ
logs                     → ログフォルダ
 └ application.log       → デフォルトログファイル
target                   → 生成物
 └ resolution-cache      → 依存性に関する情報
 └ scala-2.10
    └ api                → 生成された API ドキュメント
    └ classes            → コンパイルされたクラスファイル
    └ routes             → routes から生成されたソース
    └ twirl              → テンプレートから生成されたソース
 └ universal             → アプリケーションパッケーs時
 └ web                   → コンパイルされた web アセット
test                     → 単体、および機能テスト用のソースフォルダ

コントローラーを追加してみる

まず簡単なコントローラを追加してみる

package controllers

import javax.inject.{Inject, Singleton}
import play.api.mvc.{Action, Controller}


@Singleton
class TestController  @Inject() extends Controller{
  def index = Action { request =>
    Ok("Got request [" + request + "]")
  }
}

play 2.4からDIが使われるようになっている、今回はDI対象のコンポーネントがないが、DI対象のコンポーネントがある場合は@Injectアノテーションの後に引数としてDI対象コンポーネントを渡すことができる。またクラス自体に@Singletonアノテーションが付与されておりシングルトンとして扱う事を明示しているが、PlayフレームワークがDIに対応する前は普通にobject型として定義するなどしていた。
また受け取るリクエストはrequestで指定することができるが、これはplayフレームワークのplay.api.mvc.Actionクラスがリクエストを受け取る際にapplyメソッドでリクエストをパースしているようである。

それからリクエストを受け取れるようにするためのルート定義をconf/routesに追加する

GET      /test                       controllers.TestController.index

これで"http://localhost:9000/test"にアクセスしたら自作のコントローラが呼び出されるようになる。

リクエストを暗黙のパラメータとして扱う

受け取るリクエストを暗黙のパラメータにすることもできる。受け取ったリクエストのjsonをパースするときとかは暗黙のパラメータとして受け取って

package controllers

import javax.inject.{Inject, Singleton}

import play.api.mvc.{Action, Controller, RequestHeader}


@Singleton
class TestController  @Inject() extends Controller{
  def index = Action { implicit request =>
    Ok(greeting("Hello"))
  }

  private def greeting(say: String)(implicit req: RequestHeader) = say + "," + req.remoteAddress
}

jsonをリクエストとして受け取れるようにする

まずはplay標準のパーサでjsonのリクエストを受け取ってみるため以下のメソッドを追加する。

def jsonReq = Action(parse.json) { request =>
  (request.body \ "name").asOpt[String].map { name =>
    Ok("Hello " + name)
  }.getOrElse {
    BadRequest("Missing parameter [name]")
  }
}

それからroutesを追加

POST     /json                       controllers.TestController.jsonReq

次に以下のコマンドでリクエストを飛ばしjsonを受け取れている事を確認する

curlhttp://localhost:9000/json’ -H ‘Content-Type: text/json’ –data-binary ‘{“name”: “jon doh”}’

jsonをcase classにセットしてみる

次にjsonのリクエストをcase classにセットする方法を試してみたいと思う。次のようにcaseクラスとmapping、Actionメソッドを定義しておく。

case class AddTodo(categoryId:Long, title:String, text:String)
implicit val addTodoForm:Form[AddTodo] = Form (
  mapping(
    "categoryId" -> longNumber,
    "title"-> text,
    "text"-> text
  )(AddTodo.apply)(AddTodo.unapply)
)

def jsonReq = Action { request =>
  try{
    addTodoForm.bindFromRequest.fold(
      errors => {BadRequest("bad request")},
      validForm => {
        Ok("categoryId: " + validForm.categoryId + " title:" + validForm.title + " text:" + validForm.text)
      }
    )
  }
}

それから以下のリクエストを投げることでjsonをcase classにセットできることが確認できる

curlhttp://localhost:9000/json’ -H ‘Content-Type: text/json’ –data-binary ‘{“categoryId”: 1, “title”: “play test”, “text”: “text”}’

Formにセットする際のバリデーション処理は以下の公式を確認する
https://www.playframework.com/documentation/ja/2.4.x/ScalaForms

jsonを返してみる

jsonをレスポンスで返すのも簡単に行える。先ほどのjsonで受け取ったリクエストをそのまま返すようにして動作確認してみたいと思う。
まず先ほど利用したコントローラに以下のメソッドを追加しておく

implicit val todoWrite = new Writes[AddTodo] {
  def writes(todo: AddTodo) =
    Json.obj(
      "categoryId" -> todo.categoryId,
      "title" -> todo.title,
      "text" -> todo.text
    )
}

それから先ほどのコントローラのメソッドのレスポンスを以下のように変更する

def jsonReq = Action { request =>
  try{
    addTodoForm.bindFromRequest.fold(
      errors => {BadRequest("bad request")},
      validForm => {
        Ok(Json.toJson(validForm))
      }
    )
  }
}

これで受け取ったjsonをFormにセットしてそのまま返す動作が確認できたはずである。jsonのルート要素を変更したい場合は以下のようにすれば良い

Ok(Json.obj("todo" -> validForm))

セッションを使ってみる

以下のメソッドによりセッションごとでのカウントアップを行うことができる。

def count = Action { request =>
  val nextCount = request.session.get("count") match {
    case Some(x) => x.toInt + 1
    case None => 1
  }
  Ok("counta: " + nextCount).withSession(
    request.session + ("count" -> nextCount.toString)
  )
}

ただしplayフレームワークはステートレスのフレームワークとなっておりplay側ではセッションを保持せずクライアント側でクッキーとして保持させる形になっている。クライアント側で自由に帰られては困るような情報はredisなど別で持たせるような仕組みにしておく必要がある。

htmlテンプレートを使ってみる

htmlテンプレートのtwirlを使って画面を描画してみる まず以下のコントローラを作成する

package controllers

import javax.inject.{Inject, Singleton}
import play.api.mvc.{Action, Controller}

case class User(firstName: String, lastName: String)

@Singleton
class SampleController  @Inject() extends Controller{
  def index = Action {
    val user = User("john", "does")
    Ok(views.html.sample(user))
  }
}

それからapp/viewsにsample.scala.htmlのファイルを作成し、以下を書き込む

@(user: User)

<span>this is twirl page</span>

@defining(user.firstName + " " + user.lastName) { fullName =>
<div>Hello @fullName</div>
}

これでtwirlのページが表示されるはずである。置換周りについて詳しくか以下を参照する。
https://www.playframework.com/documentation/ja/2.4.x/ScalaTemplates

DBにアクセスする

anromを使う

play2.5では既にplayの標準ではなくなっているけどanormを使ってDBにアクセスしてみる。

依存ライブラリの追加

まずbuild.sbtのdependenciesに以下を追加する
今回は動作確認なのでDBにはh2を使う

libraryDependencies ++= Seq(
  jdbc,
  "com.typesafe.play" %% "anorm" % "2.4.0",
  "com.h2database"  %  "h2"      % "1.4.193"
)

DB接続先設定

それからapplication.confにDBの接続先設定として以下を記入する。defaultはplayフレームワークで扱うDB名になる

db {
  # You can declare as many datasources as you want.
  # By convention, the default datasource is named `default`

  # https://www.playframework.com/documentation/latest/Developing-with-the-H2-Database
  default.driver = org.h2.Driver
  default.url = "jdbc:h2:mem:play"
  default.username = sa
  default.password = ""

  # You can turn on SQL logging for any datasource
  # https://www.playframework.com/documentation/latest/Highlights25#Logging-SQL-statements
  default.logSql=true
}

初期データの投入

h2のコンソールにアクセスする場合はactivatorのコンソールで"h2-browser"を入力する

h2-browser

h2のコンソールで接続先のDBにはapplication.confで設定した"jdbc:h2:mem:play"を指定する。
それから今回使用するDB作成のSQLを実行する

create table request_text (
  id bigint identity primary key,
  input_text char(256)
);
insert into request_text(id, input_text) values ( null, 'one text');
insert into request_text(id, input_text) values ( null, 'two text');
insert into request_text(id, input_text) values ( null, 'three text');
insert into request_text(id, input_text) values ( null, 'four text');

今回はDBのデータを永続化させていなため次回play起動時にはデータがリセットされるので注意が必要

DBオブジェクト

defaultのDBを使えるようにするために以下のクラスを作成する。使用するときはコントローラ側でDIするようにする。

package db

import javax.inject.Inject
import play.api.db.DBApi

@javax.inject.Singleton
class DefaultDB @Inject()(dbapi: DBApi){
  val db = dbapi.database("default")
}

DAOの作成

次にDBアクセス用のDAOを作成する

package services.repository

import javax.inject.Inject

import dto.RequestText
import play.api.db.Database
import anorm.SqlParser.get
import anorm.{SQL, ~}

case class RequestText(id: Long, input_text: String)

@javax.inject.Singleton
class TextService  @Inject()() {
  val textParser = {
    get[Long]("id") ~
      get[String]("input_text") map {
      case id ~ input_text => RequestText(id, input_text)
    }
  }

  def addText(requestText: String)(implicit db: Database): Option[Long] = {
    insertText(requestText)
  }

  def insertText(requestText: String)(implicit db: Database) = {
    db.withConnection { implicit connection =>
      SQL(
        """
          insert into request_text (id, input_text) values
          ( (select nextval('text_id_seq')),
            {request_text}
           )
        """
      ).on(
        'request_text -> requestText
      ).executeInsert()
    }
  }

  def getText()(implicit db: Database): Seq[RequestText] = {
    db.withConnection { implicit connection =>
      SQL("select * from request_text").as(textParser.*)
    }
  }
}

コントローラ側からDAOを利用する

それから、DBとDAOをDIして利用するコントローラも作成する

package controllers

import javax.inject.{Inject, Singleton}

import db.DefaultDB
import dto.RequestText
import play.api.data.Form
import play.api.data.Forms._
import play.api.data.Forms.mapping
import play.api.i18n.MessagesApi
import play.api.mvc.{Action, Controller}
import services.repository.TextService

@Singleton
class SampleController  @Inject()(val messagesApi: MessagesApi,defaultDB: DefaultDB,textService: TextService) extends Controller{
  val db = defaultDB.db

  def anormSample = Action { implicit request =>
    db.withTransaction { tr =>
      val texts:Seq[RequestText] = textService.getText()(db)
      Ok(views.html.sample(texts))
    }
  }


  case class AddText(input_text:String)
  implicit val addTextForm:Form[AddText] = Form (
    mapping(
      "input_text"-> text
    )(AddText.apply)(AddText.unapply)
  )
  def anormAdd = Action { implicit request =>
    addTextForm.bindFromRequest.fold(
      errors => {BadRequest("bad request")},
      validForm => {
        db.withTransaction{ tr =>
          textService.insertText(validForm.input_text)(db)
        }
      }
    )
    Redirect("/anorm")
  }

}

DBオブジェクトとDAOはDIさせて利用している

最後に動作確認ように以下のhtmlを準備したらanormの確認が行えるはず

@import dto.RequestText
@(requestTexts: Seq[RequestText])

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>anorm sample page</title>
</head>
<body>
<span>this is anorm sample</span>
<form method="post" action="anorm">
    <input type="text" name="input_text" ></input><input type="submit" name="butto" value="送信" />
</form>

<ul>
    @for(requestText <- requestTexts) {
    <li>@requestText.id:  @requestText.input_text</li>
    }
</ul>
</body>
</html>

今回はコントローラ側から暗黙のパラメータとしてDBを渡すようにしており、利用するDBを切り替える場合にも簡単に対応できる。トランザクション処理は"db.withTransaction"で行うことができる

ScalikeJDBCを使う

次にScalikeJDBCを試してみたいと思います。 まずbuild.sbtのlibDependenciesが以下になるようにします。

libraryDependencies ++= Seq(
  filters,
  "com.h2database"  %  "h2"                % "1.4.193",
  "org.scalikejdbc" %% "scalikejdbc"                  % "2.5.1",
  "org.scalikejdbc" %% "scalikejdbc-config"           % "2.5.1",
  "org.scalikejdbc" %% "scalikejdbc-play-initializer" % "2.5.1",
  "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0" % Test
)

それからconf/application.confに以下を追加します。

scalikejdbc.global.loggingSQLAndTime.enabled=true
scalikejdbc.global.loggingSQLAndTime.singleLineMode=false
scalikejdbc.global.loggingSQLAndTime.logLevel=debug
scalikejdbc.global.loggingSQLAndTime.warningEnabled=true
scalikejdbc.global.loggingSQLAndTime.warningThresholdMillis=5
scalikejdbc.global.loggingSQLAndTime.warningLogLevel=warn

play.modules.enabled += "scalikejdbc.PlayModule"
# scalikejdbc.PlayModule doesn't depend on Play's DBModule
play.modules.disabled += "play.api.db.DBModule"

Query DSLSQLを実行してみる

それではまずは一覧表示できるようにしてみたいと思います。 以下のmodelを追加します。

import scalikejdbc._

case class RequestTexts(id: Long,
                  input_text: String) {

  def save()(implicit session: DBSession = RequestTexts.autoSession): RequestTexts = RequestTexts.save(this)(session)
  def destroy()(implicit session: DBSession = RequestTexts.autoSession): Int = RequestTexts.destroy(this)(session)

}


object RequestTexts extends SQLSyntaxSupport[RequestTexts] {

  override val autoSession = AutoSession
  override val schemaName = Some("PUBLIC")
  override val tableName = "request_text"
  override val columns = Seq("id", "input_text")
  val r = RequestTexts.syntax("r")

  def apply(r: SyntaxProvider[RequestTexts])(rs: WrappedResultSet): RequestTexts = apply(r.resultName)(rs)
  def apply(r: ResultName[RequestTexts])(rs: WrappedResultSet): RequestTexts = new RequestTexts(
    id = rs.get(r.id),
    input_text = rs.get(r.input_text)
  )

  def save(entity: RequestTexts)(implicit session: DBSession = autoSession): RequestTexts = {
    withSQL {
      update(RequestTexts).set(
        column.id -> entity.id,
        column.input_text -> entity.input_text
      ).where.eq(column.id, entity.id)
    }.update.apply()
    entity
  }

  def destroy(entity: RequestTexts)(implicit session: DBSession = autoSession): Int = {
    withSQL { delete.from(RequestTexts).where.eq(column.id, entity.id) }.update.apply()
  }

  def findAll()(implicit session: DBSession = autoSession): List[RequestTexts] = {
    withSQL(select.from(RequestTexts as r)).map(RequestTexts(r.resultName)).list.apply()
  }
}

それからmodelを利用するコントローラを追加します。

package controllers

import javax.inject._

import models.RequestTexts
import play.api.data.Form
import play.api.data.Forms.mapping
import play.api.data.Forms._
import play.api.libs.json.{JsError, Json, Writes}
import play.api.mvc._
import scalikejdbc.DB

@Singleton
class ScalikeJdbcController @Inject() extends Controller {


  // jsonの書き込み
  implicit val requestWrite = new Writes[RequestTexts] {
    def writes(request: RequestTexts) =
      Json.obj(
        "id" -> request.id,
        "input_text" -> request.input_text
      )
  }

  def list = Action { implicit request =>
    DB.readOnly { implicit session =>
      Ok(Json.obj("root" -> RequestTexts.findAll()))
    }
  }
}

あとはapiを利用できるようにするためconf/routesに以下を追加します。

GET     /scalike                    controllers.ScalikeJdbcController.list

これで以下のコマンドを実行するとjsonのレスポンスが確認できると思います。 curlhttp://localhost:9000/scalike’

id指定でselectする場合は以下のようにwhereのパラメータを渡すことができる

def findById(id: Long)(implicit session: DBSession = autoSession): Option[RequestTexts] = {
  withSQL {select.from(RequestTexts as r).where.eq(r.id, id)
  }.map(RequestTexts(r.resultName)).single.apply()
}

SQLInterpolationでSQLを実行してみる

実行するSQLを自分で書きたい場合は以下のようになります。

def findByIdWithQuery(id: Long)(implicit session: DBSession = autoSession): Option[RequestTexts] = {
  sql"select   id, input_text from   request_text where id = ${id}"
     .map{rs => RequestTexts(rs.long("id"), rs.string("input_text"))}.single.apply
}

insertを実行する

insert文は以下のようになります。

def create(entity: RequestTexts)(implicit session: DBSession = autoSession): RequestTexts = {
  val generatedKey = withSQL {
    insert.into(RequestTexts).namedValues(
      column.input_text -> entity.input_text
    )
  }.updateAndReturnGeneratedKey.apply()

  RequestTexts(
    id = generatedKey,
    input_text = entity.input_text)
}

slick3を使ってみる

slick3も使ってみたいと思います。
build.sbtが以下になるようにします。

libraryDependencies ++= Seq(
  filters,
  "com.h2database" % "h2" % "1.4.193",
  "com.typesafe.play" %% "play-slick" % "2.0.2",
  "com.typesafe.slick" %% "slick-codegen" % "3.1.1",
  "org.scalatestplus.play" %% "scalatestplus-play" % "2.0.0" % Test
)

それからapplication.confでDBの接続先を設定します。Anorm、scalikeとは設定が違うので注意です。

slick.dbs.default.driver="slick.driver.H2Driver$"
slick.dbs.default.db.driver=org.h2.Driver
slick.dbs.default.db.url="jdbc:h2:mem:play"
slick.dbs.default.db.user=sa
slick.dbs.default.db.password=""

SourceCodeGeneratorでモデルを自動生成する

SourceCodeGeneratorを使ってモデルのクラスを自動で生成したいと思います。次のobjectを作成しておいてください。

package tool.codegen

import slick.codegen.SourceCodeGenerator

object SlickCodgeGen {

  def main(args: Array[String]): Unit = {
    codeGen
  }

  def codeGen: Unit ={
    val slickDriver = "slick.driver.H2Driver"
    val jdbcDriver = "org.h2.Driver"
    val url ="jdbc:h2:mem:play"
    val user = "sa"
    val password = ""
    val outputFolder = "app"
    val pkg = "models"

    SourceCodeGenerator.main(
      Array(
        slickDriver,
        jdbcDriver,
        url,
        outputFolder,
        pkg,
        user,
        password
      )
    )
  }
}

あとはcodeGenを呼び出せば良いのですが、プロジェクト作成時に作られるHomeControllerの最初の部分で呼び出すようにしてみます。

package controllers

import javax.inject._
import play.api._
import play.api.mvc._

@Singleton
class HomeController @Inject() extends Controller {
  import tool.codegen.SlickCodgeGen
  SlickCodgeGen.codeGen

  def index = Action { implicit request =>
    Ok(views.html.index())
  }
}

これでhttp://localhost:9000/にアクセスするとモデルクラスが自動で生成されると思います。
テーブルのカラムのデフォルト値をnextval(‘自作seq’)などとしているとモデルクラスを作成するときにエラーになりました。その場合はカラムにauto incrementを付与するなどしてDB自体の機能で自動採番させれば大丈夫なようです。PostgreSQLならserial, bigserial型を使えば良さそうだと思います。

一覧表示してみる

それからmodelを利用するコントローラを追加します。

package controllers

import play.api.mvc._

import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.db.slick._
import slick.driver.JdbcProfile
import models.Tables._
import javax.inject.Inject
import javax.inject.Singleton
import slick.driver.H2Driver.api._

import play.api.libs.json._

@Singleton
class SlickContorller @Inject()(val dbConfigProvider: DatabaseConfigProvider) extends Controller
  with HasDatabaseConfigProvider[JdbcProfile] {

  // jsonの書き込み
  implicit val requestWrite = new Writes[RequestTextRow] {
    def writes(request: RequestTextRow) =
      Json.obj(
        "id" -> request.id,
        "input_text" -> request.inputText
      )
  }

  def list = Action.async { implicit rs =>
    db.run(RequestText.sortBy(t => t.id).result).map{ texts =>
      Ok(Json.obj("root" -> texts))
    }
  }
}

あとはapiを利用できるようにするためconf/routesに以下を追加します。

GET     /slick                     controllers.SlickContorller.list

これで以下のコマンドを実行するとjsonのレスポンスが確認できると思います。 curlhttp://localhost:9000/slick’

DatabaseForconfigで使用するDBを選択する

Slick3では使用するDBを変更することも簡単に行えます。まずapplication.confを以下のようにして新しいDB接続設定を追記してください。

mydb = {
  driver=org.h2.Driver
  url="jdbc:h2:mem:play"
  user=sa
  password=""
}

それから一覧表示するコントローラを以下のように編集してください。

package controllers

import play.api.mvc._

import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.db.slick._
import slick.driver.JdbcProfile
import models.Tables._
import javax.inject.Inject
import javax.inject.Singleton
import slick.driver.H2Driver.api._

import play.api.libs.json._

@Singleton
class SlickContorller @Inject() extends Controller {
  val mydb = Database.forConfig("mydb")

  // jsonの書き込み
  implicit val requestWrite = new Writes[RequestTextRow] {
    def writes(request: RequestTextRow) =
      Json.obj(
        "id" -> request.id,
        "input_text" -> request.inputText
      )
  }

  def list = Action.async { implicit rs =>
    mydb.run(RequestText.sortBy(t => t.id).result).map{ texts =>
      Ok(Json.obj("root" -> texts))
    }
  }
}

これで'http://localhost:9000/slick'にアクセスすると新しく用意した接続先が使えていることが確認できます。

ID指定で取得する

先ほどは一覧表示したので今回はidを指定してみたいと思います。コントローラに以下のidでフィルターをかけるメソッドを追加します。

def findById(id: Long) = Action.async {implicit rs =>
  mydb.run(RequestText.filter(t => t.id === id).result).map{ text =>
    Ok(Json.obj("root" -> text))
  }
}

それからroutesに以下を追加してメソッドを呼び出せるようにします。

GET     /find/:id                  controllers.SlickContorller.findById(id :Long)

これで'curlhttp://localhost:9000/find/2'のようにid指定でSQLを実行できるのを確認できます。

createを実行する

次にinsertを行ってみます。先ほどのコントローラーに以下を追加します。

 case class Reuqest(input_text:String)
  // jsonの読み込み
  implicit val addRequestForm:Form[Reuqest] = Form (
    mapping(
      "input_text"-> text
    )(Reuqest.apply)(Reuqest.unapply)
  )


  def create = Action.async { implicit rs =>
    addRequestForm.bindFromRequest.fold(
      error => {
        Future {
          BadRequest(Json.obj("result" ->"failure"))
        }
      },
      form => {
        val requestTextRow = RequestTextRow(0, Option(form.input_text))
        mydb.run(RequestText += requestTextRow).map { _ =>
          Ok(Json.obj("result" -> "success"))
        }
      }
    )
  }

それからroutesに以下を追加します。

POST    /create                    controllers.SlickContorller.create

これで以下のコマンドを実行するとinsertが行えているのが確認できるはずです。

curl -H “Content-type: application/json” -XPOST -d ‘{“input_text”:“new text”}’ http://localhost:9000/create

updateを実行する

updateを実行する場合は以下のようになります。

def update (id: Long)= Action.async { implicit rs =>
  addRequestForm.bindFromRequest.fold(
    error => {
      Future {
        BadRequest(Json.obj("result" ->"failure"))
      }
    },
    form => {
      val requestTextRow = RequestTextRow(id, Option(form.input_text))
      mydb.run(RequestText.filter(t => t.id === requestTextRow.id.bind).update(requestTextRow)).map { _ =>
        Ok(Json.obj("result" -> "success"))
      }
    }
  )
}

それからroutesに以下を追加します。

POST    /update/:id                controllers.SlickContorller.update(id :Long)

これで以下のコマンドを実行するとupdateが行えているのが確認できるはずです。

curl -H “Content-type: application/json” -XPOST -d ‘{“input_text”:“update text”}’ http://localhost:9000/update/2

deleteを実行する

updateを実行する場合は以下のようになります。

def delete(id: Long) = Action.async {implicit rs =>
  mydb.run(RequestText.filter(t => t.id === id).delete).map { _=>
    Ok(Json.obj("result" -> "success"))
  }
}

それからroutesに以下を追加します。

POST    /delete/:id                controllers.SlickContorller.delete(id :Long)

これで以下のコマンドを実行するとdeleteが行えているのが確認できるはずです。

curl -H “Content-type: application/json” -XPOST -d ‘{}’ http://localhost:9000/delete/3