Play2.4-slick3.0s

DBの準備

ツールプロジェクトの準備 slick-codegen-play2.4-slick3.0.zip をダウンロードし、以下のようにplay2-hands-onプロジェクトと同じディレクトリに展開します。 +-/play2-hands-on | | | +-/app | | | +-/conf | | | +-... | +-/slick-codegen | +-/project | +-/src | +-... H2の起動 Windowsの場合 まず、slick-codegenプロジェクトのh2/start.batをダブルクリックしてH2データベースを起動します。データベースには以下のスキーマのテーブルが作成済みの状態になっています。 Macの場合 cd slick-codegen/h2/ sh start.sh ※起動後、そのターミナルは閉じないでください。 モデルの自動生成 SlickではタイプセーフなAPIを使用するために タイプセーフなクエリで使うテーブル定義 エンティティオブジェクト を用意する必要がありますが、これらはSlickが標準で提供しているジェネレータを使用することでDBスキーマから自動生成することができます。 slick-codegenプロジェクトのルートディレクトリで以下のコマンドを実行します。 Windowsの場合 sbt gen-tables Macの場合 ./sbt.sh gen-tables するとplay2-hands-onプロジェクトのapp/modelsパッケージにモデルクラスが生成されます。 DB接続の設定 play2-hands-onプロジェクトのconf/application.confにDB接続のため以下の設定を追加します。 slick.dbs.default.driver="slick.driver.H2Driver$" slick.dbs.default.db.driver=org.h2.Driver slick.dbs.default.db.url="jdbc:h2:tcp://localhost/data" slick.dbs.default.db.user=sa slick.dbs.default.db.password=sa

IDEの準備

プラグインのインストール Java7以降 + Eclipse or IntelliJの環境は構築済みとします。また、ファイルのエンコードはUTF-8にしておいてください。 それぞれのIDEにScalaで開発を行うためのプラグインをインストールします。 Eclipseの場合: メニュー[Help]→[Install New Software…]→[Add…]をクリックして以下を入力 Name: Scala IDE Location: http://download.scala-ide.org/sdk/lithium/e44/scala211/stable/site 全部チェックを入れて(Sourcesは外してもOK)、[Next]をクリック あとはウィザードに従う IntelliJの場合: メニュー[IntelliJ IDEA]→[Preferences]→[Plugins]→[Install JetBrains plugin…]をクリック [Scala]を選択し、右クリック[Download and Install]をクリック ※IntelliJ Ultimate EditionはPlayプラグインを使うことができ、PlayプロジェクトをIntelliJで作成したり、HTMLテンプレートや設定ファイルなどを編集するためのエディタが追加されます。 プロジェクトのインポート Eclipseの場合: play2-hands-onディレクトリで以下のコマンドを実行し、IDE用の設定ファイルを生成します。その後、IDEにインポートします。 activator eclipse build.sbtを編集してライブラリを追加した場合、再度activator eclipseを実行する必要があります IntelliJの場合: IntelliJのScalaプラグインはSBT(Activator)プロジェクトをネイティブサポートしており、「File」メニューから「Open」を選択し、Play2プロジェクトのルートディレクトリを選択するとSBTプロジェクトとしてインポートすることができます。 インポートする際に以下のダイアログが表示されます。初回は「Project SDK」が未選択の状態になっているかもしれません。「New…」をクリックしてJDKがインストールされているディレクトリを選択してから「OK」をクリックしてください。 build.sbtを編集してライブラリを追加した場合、ウィンドウ右上に以下のようなメッセージが表示されます。 「Refresh」を選択するとプロジェクトが再インポートされ、ライブラリが自動的にインターネット経由でダウンロードされクラスパスに追加されます。また、「Enable auto-import」を選択するとbuild.sbtを変更するたびに自動的に再インポートされるようになります(プロジェクトのインポート時に自動インポートを有効にしておくことも可能です)。

JSON APIの準備

フロントエンドがAngularJSやスマートフォンアプリの場合、サーバサイドはJSONを返却するAPIを提供する必要があります。ここまでに作成してきたユーザ情報のCRUD処理について、Play2のJSONサポート機能を使ってJSONベースのWeb APIを実装します。 コントローラの雛形を作る controllersパッケージにJsonControllerオブジェクトを以下の内容で作成します。SlickやPlay2のJSONサポートを使用するためのimport文を予め含めています。 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 scala.concurrent.Future import slick.driver.H2Driver.api._ import play.api.libs.json._ import play.api.libs.functional.syntax._ class JsonController @Inject()(val dbConfigProvider: DatabaseConfigProvider) extends Controller with HasDatabaseConfigProvider[JdbcProfile] { /** * 一覧表示 */ def list = TODO /** * ユーザ登録 */ def create = TODO /** * ユーザ更新 */ def update = TODO /** * ユーザ削除 */ def remove(id: Long) = TODO } コンストラクタでDatabaseConfigProviderをDIしていたり、HasDatabaseConfigProviderトレイトをミックスインしているのはUserControllerと同じですが、テンプレートを使用していないので国際化機能のために必要だったMessagesApiのDIやI18nSupportトレイトのミックスインは行っていません。

Play 2.4 + Slick 3.0ハンズオン

目的 Play2 + Slickを使ってWebアプリケーションを作成するハンズオンです。 主な目的は以下の通りです。 Scalaに触れてもらう 数時間でとりあえず動くものを作ってみる そのため、なるべくフレームワークが提供する機能をそのまま使います。 構成 使用するフレームワークおよびバージョンは以下の通りです。 Play 2.4.x Slick 3.0.x 前提条件 このハンズオンを実施するにあたっての前提条件は以下の通りです。 JavaおよびWebアプリケーションの開発に関する基本的な知識を持っていること JDK 1.7以降がインストールされていること EclipseもしくはIntelliJ IDEAの最新版がインストールされていること 内容 ユーザ情報のCRUDを行う簡単なアプリケーションを作成します。 ユーザ一覧を表示する 新規ユーザ登録を行う ユーザ情報を編集する ユーザを削除する また、後半ではこのアプリケーションと同じCRUD処理を行うJSONベースのWeb APIも作成します。

Tips

Play2 playコマンド実行時のVMオプションの設定 ヒープ設定やプロキシ設定などJVMの起動オプションを設定するには環境変数JAVA_OPTSを使用します Playが使用するローカルリポジトリ 以前のバージョンのPlayはPLAY_HOME/repositoryディレクトリ配下に独自のローカルリポジトリとキャッシュを持っていましたが、Play 2.3ではSBT標準のHOME/.ivy2ディレクトリ配下を使うようになりました Play公式サイトのドキュメントは https://www.playframework.com/documentation/2.5.x/Home にあります(2.5.x向けのドキュメントは日本語には未翻訳です) Play 2.5以前から2.5へのマイグレーションについては以下のドキュメントを参照してください Play 2.5 Migration Guide Slick https://github.com/bizreach-inc/slick-reference でSlickのリファレンスを公開しています Slick 2.xから3.0へのマイグレーションについては http://slick.typesafe.com/doc/3.0.0/upgrade.html#upgrade-from-2-1-to-3-0 を参照してください ScalaIDE Scala IDEはEclipseのバージョン、Scalaのバージョンにあわせて更新サイトが用意されています 最新情報は http://scala-ide.org/download/current.html を参照してください IntelliJ IDEA キーボードショートカット (Windows) http://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard.pdf (Mac) http://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard_Mac.pdf IDEAの外観を変更 スキンの変更(背景を黒に) [Appearance]→[Theme]の箇所を"Default"から"Darcula"に変更 IntelliJ上でクラスパスが解決できない場合 StringなどJavaの基本的な型が解決できずエラーになってしまう場合は実行した直後はJDKが選択されていない可能性があります [File]→[Project Structure…]からインストール済みのJDKを選択してください build.

プロジェクトの作成

Play本体のインストール http://www.playframework.com/download からtypesafe-activator-1.3.5-minimal.zipをダウンロードし、解凍したディレクトリを環境変数PATHに追加します。 新規プロジェクト作成 コマンドプロンプトで以下のコマンドを実行します。途中でScalaアプリケーションとJavaアプリケーションのどちらを作成するかを聞かれるのでScalaアプリケーションを選択します。 activator new play2-hands-on play2-hands-onディレクトリのbuild.sbtにORMとしてSlickを使用するための設定を行います。 name := "play2-hands-on" version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.6" libraryDependencies ++= Seq( // jdbcからspec2〜まで消してください // jdbc, // cache, // ws, // specs2 % Test "com.h2database" % "h2" % "1.4.177", // <- この行を追加してください "com.typesafe.play" %% "play-slick" % "1.0.0" // <- この行を追加してください ) resolvers += "scalaz-bintray" at "http://dl.

ユーザ一覧APIの実装

USERSテーブルからIDの昇順に全件取得し、ユーザ一覧をJSONで返します。 Writesの定義 Play2のJSONサポートでは、ScalaオブジェクトをJSONに変換するにはWrites、JSONをScalaオブジェクトに変換するためにはReadsを定義する必要があります。 ここではUSERSテーブルを検索して取得したケースクラスのリストをJSONに変換して返却するので、USERSテーブルに対応するUsersRowクラスに対応するWritesを定義しておく必要があります。画面から値を受け取るFormと同様、該当のコントローラ(ここではJsonController)のコンパニオンオブジェクトとして以下の内容を追加します。 object JsonController { // UsersRowをJSONに変換するためのWritesを定義 implicit val usersRowWritesWrites = ( (__ \ "id" ).write[Long] and (__ \ "name" ).write[String] and (__ \ "companyId").writeNullable[Int] )(unlift(UsersRow.unapply)) } Play2のJSONサポートが提供するDSLを使用してマッピングを定義していますが、DSLを使わずに以下のように記述することもできます。 implicit val usersRowWritesFormat = new Writes[UsersRow]{ def writes(user: UsersRow): JsValue = { Json.obj( "id" -> user.id, "name" -> user.name, "companyId" -> user.companyId ) } } POINT

ユーザ一覧の実装

USERSテーブルからIDの昇順に全件取得し、ユーザ一覧画面を表示します。 ビュー テンプレートはviewsパッケージに作成します。appディレクトリ配下にviews.userパッケージを作成し、以下の内容でlist.scala.htmlを作成します。 @* このテンプレートの引数 *@ @(users: Seq[models.Tables.UsersRow]) @* main.scala.htmlを呼び出す *@ @main("ユーザ一覧") { <div> <a href="@routes.UserController.edit()" class="btn btn-success" role="button">新規作成</a> </div> <div class="col-xs-6"> <table class="table table-hover"> <thead> <tr> <th>ID</th> <th>名前</th> <th>&nbsp;</th> </tr> </thead> <tbody> @* ユーザの一覧をループで出力 *@ @users.map { user => <tr> <td>@user.id</td> <td><a href="@routes.UserController.edit(Some(user.id))">@user.name</a></td> <td>@helper.form(routes.UserController.remove(user.id)){ <input type="submit" value="削除" class="btn btn-danger btn-xs"/> } </td> </tr> } </tbody> </table> </div> } POINT

ユーザ削除APIの実装

指定したIDのユーザをUSERSテーブルから削除します。 コントローラ JsonControllerのremoveメソッドを以下のように実装します。 def remove(id: Long) = Action.async { implicit rs => // ユーザを削除 db.run(Users.filter(t => t.id === id.bind).delete).map { _ => Ok(Json.obj("result" -> "success")) } } 実行 コマンドラインから以下のコマンドを実行してユーザが削除されることを確認してください。 curl -XPOST http://localhost:9000/json/remove/1

ユーザ登録・更新APIの実装

ユーザ情報をJSONで受け取り、登録もしくは更新を行います。 Readsの定義 JsonControllerのコンパニオンオブジェクトにユーザ情報を受け取るためのケースクラスと、JSONからそのケースクラスに変換するためのReadsを定義します。 object JsonController { ... // ユーザ情報を受け取るためのケースクラス case class UserForm(id: Option[Long], name: String, companyId: Option[Int]) // JSONをUserFormに変換するためのReadsを定義 implicit val userFormFormat = ( (__ \ "id" ).readNullable[Long] and (__ \ "name" ).read[String] and (__ \ "companyId").readNullable[Int] )(UserForm) } 前述のWritesと同様、DSLを使わずに以下のように記述することもできます。 implicit val userFormFormat = new Reads[UserForm]{ def reads(js: JsValue): UserForm = { UserForm( id = (js \ "id" ).