Play2.3-slick2.1s

DBの準備

ツールプロジェクトの準備 slick-codegen-play2.3-slick2.1.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接続のための設定を行います。 変更前: # db.default.driver=org.h2.Driver # db.default.url="jdbc:h2:mem:play" # db.default.user=sa # db.default.password="" 変更後: db.default.driver=org.h2.Driver db.default.url="jdbc:h2:tcp://localhost/data" db.default.user=sa db.default.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.db.slick._ import models.Tables._ import profile.simple._ import play.api.libs.json._ object JsonController extends Controller { /** * 一覧表示 */ def list = TODO /** * ユーザ登録 */ def create = TODO /** * ユーザ更新 */ def update = TODO /** * ユーザ削除 */ def remove(id: Long) = TODO } POINT

Play 2.3 + Slick 2.1ハンズオン

目的 Play2 + Slickを使ってWebアプリケーションを作成するハンズオンです。 主な目的は以下の通りです。 Scalaに触れてもらう 数時間でとりあえず動くものを作ってみる そのため、なるべくフレームワークが提供する機能をそのまま使います。 構成 使用するフレームワークおよびバージョンは以下の通りです。 Play 2.3.x Slick 2.1.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/ja/2.3.x/Home で日本語訳されています Slick https://github.com/bizreach-inc/slick-reference でSlickのリファレンスを公開しています 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.sbtを変更してもクラスパスが解決されないことがありますが、その場合は「File」→「Invalidate Caches / Restart…」でIntelliJのキャッシュをクリアしてみてください

プロジェクトの作成

Play本体のインストール http://www.playframework.com/download からtypesafe-activator-1.3.2-minimal.zipをダウンロードし、解凍したディレクトリを環境変数PATHに追加します。 新規プロジェクト作成 コマンドプロンプトで以下のコマンドを実行します。途中でScalaアプリケーションとJavaアプリケーションのどちらを作成するかを聞かれるのでScalaアプリケーションを選択します。 activator new play2-hands-on Play2.3.x用に設定変更 2015/5/26 にPlay 2.4 がリリースされた為、↑のコマンドで作成されるPlayのバージョンも上がっています。このHandsOnでは、まだ 2.3.9 を利用するため、以下の設定を変更してください。また、あわせてORMにSlickを使うため、一緒に設定を変更します。 build.sbt 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, // anorm, // cache, // ws, // specs2 % Test "com.typesafe.play" %% "play-slick" % "0.8.1" // <- この行を追加してください。 ) //routesGenerator := InjectedRoutesGenerator // <- この行は消してください。 project/plugins.

ユーザ一覧APIの実装

USERSテーブルからIDの昇順に全件取得し、ユーザ一覧をJSONで返します。 Writesの定義 Play2のJSONサポートでは、ScalaオブジェクトをJSONに変換するにはWrites、JSONをScalaオブジェクトに変換するためにはReadsを定義する必要があります。 ここではUSERSテーブルを検索して取得したケースクラスのリストをJSONに変換して返却するので、USERSテーブルに対応するUsersRowクラスに対応するWritesを定義しておく必要があります。 object JsonController extends Controller { // UsersRowをJSONに変換するためのWritesを定義 implicit val usersRowWrites = Json.writes[UsersRow] ... POINT Play2のJSONサポートは単純なケースクラスの変換だけでなく、より複雑な変換やバリデーションなどを行うこともできます Json.writesやJson.readsの代わりにJson.formatでFormatを定義することでWritesとReadsを同時に定義することができます。同じケースクラスを読み込みと書き出しの両方に使う場合はJson.formatを使うとよいでしょう コントローラ JsonControllerのlistメソッドを以下のように実装します。 def list = DBAction { implicit rs => // IDの昇順にすべてのユーザ情報を取得 val users = Users.sortBy(t => t.id).list // ユーザの一覧をJSONで返す Ok(Json.obj("users" -> users)) } Json.objメソッドでケースクラスからJSONへの変換が行われますが、このときにケースクラスに対応したWritesが定義されていないとコンパイルエラーになります。 実行 コマンドラインから以下のコマンドを実行してユーザ一覧がJSONで取得できることを確認してみましょう。 curl -XGET http://localhost:9000/json/list 結果として以下のようなJSONが表示されるはずです。 {"users":[{"id":1,"name":"Taro Yamada","companyId":1},{"id":2,"name":"Jiro Sato"}]}

ユーザ一覧の実装

USERSテーブルからIDの昇順に全件取得し、ユーザ一覧画面を表示します。 ビュー テンプレートはviewsパッケージに作成します。appディレクトリ配下にviews.userパッケージを作成し、以下の内容でlist.scala.htmlを作成します。 @* このテンプレートの引数 *@ @(users: List[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) = DBAction.transaction { implicit rs => // ユーザを削除 Users.filter(t => t.id === id.bind).delete Ok(Json.obj("result" -> "success")) } 実行 コマンドラインから以下のコマンドを実行してユーザが削除されることを確認してください。 curl -XPOST http://localhost:9000/json/remove/1

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

ユーザ情報をJSONで受け取り、登録もしくは更新を行います。 Readsの定義 JsonControllerにユーザ情報を受け取るためのケースクラスと、JSONからそのケースクラスに変換するためのReadsを定義します。 object JsonController extends Controller { ... // ユーザ情報を受け取るためのケースクラス case class UserForm(id: Option[Long], name: String, companyId: Option[Int]) // JSONをUserFormに変換するためのReadsを定義 implicit val userFormReads = Json.reads[UserForm] ... コントローラ JsonControllerのcreateメソッドを以下のように実装します。 JSONリクエストを受け取る場合は DBAction(parse.json) { ... } DBAction.transaction(parse.json) { ... } のようにアクションにparse.jsonを指定します。rs.body.validateメソッドでJSONをケースクラスに変換でき、変換に失敗した場合の処理をrecoverTotalメソッドで行うことができます。 def create = DBAction.transaction(parse.json) { implicit rs => rs.body.validate[UserForm].map { form => // OKの場合はユーザを登録 val user = UsersRow(0, form.name, form.companyId) Users.