Play Frameworkハンズオン

  • 24
    いいね
  • 0
    コメント

1.ScalaとPlay Frameworkの簡単な説明

Scalaについて

  • オブジェクト指向言語と関数型言語の特徴を統合した言語
  • Javaプラットフォーム(Java仮想マシン)上で動作する
  • 既存のJavaのプログラムと容易に連携できるため、Javaの豊富なライブラリが使える

Scalaの採用事例

  • TwitterがバックエンドをRubyからScalaに2009年に移行した
  • 株式会社はてな(mackerel)
  • SmartNews
  • LINE株式会社
  • ヌーラボ(Typetalk)

Play Frameworkについて

  • ScalaとJava言語で書かれたオープンソースのWebアプリケーションフレームワーク
  • Ruby on RailsやDjangoと似た同種のフレームワーク
  • ホットリローディング
  • Java版ではサーブレットやJSPを使わない

2.ディレクトリ構成についての説明

.
├── app                   アプリケーションのソースコード
│   ├── controllers       アプリケーションのコントローラ
│   ├── filters      アプリケーションの事前/事後処理
│   ├── services          アプリケーションのサービス層
│   └── views             テンプレート
├── build.sbt             アプリケーションビルドスクリプト
├── conf
│   ├── application.conf  メイン設定ファイル
│   ├── logback.xml       ログ出力設定ファイル
│   └── routes            ルート定義
├── project               sbt 設定ファイル群
│   ├── build.properties  sbt プロジェクトの目印
│   └── plugins.sbt       Play 自身の定義を含む sbt プラグイン
├── public                公開アセット
└── test                  単体、および機能テスト用のソースフォルダ

3.Playコンソール

1)activatorコマンド

activator

2)ヘルプコマンド

activator help

3)実行コマンド

activator run

4)コンパイルコマンド

activator compile

5)テストコマンド

activator test

6)テンプレート一覧

activator list-templates

7)ファイルを監視して変更がある度にコンパイル

activator "~compile"

4.開発環境チェック

1)Java

次のコマンドを実行します。

java -version

次のように表示されたら成功です。バージョンは1.8以上であれば大丈夫です。
(2016年5月10日時点の最新バージョン:1.8.0_92)

java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)

2)Activator

プロジェクト直下に移動し、次のコマンドを実行します。

activator -v

次のように表示されれば大丈夫です。(バージョンは環境によってちがいます。)

# Executing command line:
/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin/java
-Dactivator.home=/usr/local/activator-1.3.5-minimal
-Xms1024m
-Xmx1024m
-XX:MetaspaceSize=64m
-XX:MaxMetaspaceSize=256m
-jar
/usr/local/activator-1.3.5-minimal/activator-launch-1.3.5.jar
...

3)プロジェクト

IntelliJを起動し次のように画面が表示されれば大丈夫です。
check00.png

5.作成前の準備

1)サンプルファイルの削除

この時点でサンプルのファイルが入っているため削除します。
プロジェクトのルートディレクトリ配下のappディレクトリの下にあるファイルを全て削除します。

次のように選択し
prepare_01.png

右クリックし[Delete]を選択します。(safe deleteのチェックは外します。)
prepare_02.png

2)routesの不要ルートの削除

routes.png
play-hands-on/conf/routesファイルで[GET /assets/*file ...]の行を残して他の行を削除します。

次のようになります。

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.versioned(path="/public", file: Asset)

3)今日作成するアプリ

画面表示
handson8_01.png

登録後
handson8_02.png

6.Hello World

目的:コントローラーの書き方、routesの書き方
handson1.png

次のような画面を作成します。
handson1_03.png

1)routesにhelloworld用のルートを追加

/play-hands-on/conf/routes

GET     /todo/helloworld                       controllers.TodoController.helloworld()

2)TodoControllerの作成

controllersパッケージにTodoControllerクラスを作成します。

IntelliJ上でcontrollersディレクトリを右クリックし、[New]の[Scala Class]をクリックしします。
handson1_01.png

表示されたダイアログに[TodoController]と入力しOKをクリックします。
handson1_02.png

TodoController.scala


package controllers

import javax.inject.Inject

import play.api.data.Forms._
import play.api.data._
import play.api.i18n.{I18nSupport, MessagesApi}
import play.api.mvc._
import views._

class TodoController @Inject() (val messagesApi: MessagesApi) extends Controller with I18nSupport {

  def helloworld() = Action { implicit request =>
    Ok("Hello World")
  }

}
メソッド ステータスコード
Ok 200
BadRequest 400
Forbidden 403
NotFound 404
Redirect リダイレクト処理

3)サーバー起動

プロジェクト直下で次のコマンドを実行します。

activator run

次のように表示されます。

--- (Running the application, auto-reloading is enabled) ---

[info] p.c.s.NettyServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Ctrl+D to stop and go back to the console...)

[success] Compiled in 887ms

4)画面をブラウザで見てみましょう

Hello World画面

7.リスト画面の作成(テンプレート)

目的:テンプレートの使い方、Scalaの変数の書き方
handson2.png

次のような画面を作成します。
handson2_03.png

1)routesにlist用のルートを追加

/play-hands-on/conf/routes

GET     /todo/list                       controllers.TodoController.list()

2)TodoControllerにlistメソッドを追加

次のlistメソッドを追加します。

TodoController.scala

def list() = Action { implicit request =>
  val message: String = "ここにリストを表示"
  Ok(html.list(message))
}

3)listテンプレートの作成

/play-hands-on/app/viewsの下に[list]テンプレートを作成します。

IntelliJ上でviewsディレクトリを右クリックし、[New]の[File]をクリックします。
01.png

表示されたダイアログに[list.scala.html]と入力しOKをクリックします。
02.png

こちらのHTMLをコピーして書いてください。

ここを実装してください
<html>
    <head>
        <title>Todo</title>
    </head>
    <body>

        <section>
        ここを実装してください
        </section>

    </body>
</html>

list.scala.html

@(message: String)

<html>
    <head>
        <title>Todo</title>
    </head>
    <body>

        <section>
        @message
        </section>

    </body>
</html>

4)画面をブラウザで見てみましょう

リスト画面

8.リスト画面の作成(case class)

目的:case class、Seq()、viewでのループ

handson3_00.png

1)サービスの作成

servicesパッケージの下にTodoクラスを作成します。

IntelliJ上でservicesディレクトリを右クリックし、[New]の[Scala Class]をクリックします。
03.png

表示されたダイアログに[Todo]と入力しOKをクリックします。
create-todo02.png

Todo.scala

package services

case class Todo(name: String)

case classのいいところ
- プロパティが公開される(todo.nameなど)
- インスタンスを作る時にnewが必要なくなる(val todo = Todo())
- equals,hashCode,toStringなどが実装される

2)TodoControllerの編集

TodoController.scala

servicesのパッケージを追加します。

import services._

listメソッドを編集します

def list() = Action { implicit request =>
  val items: Seq[Todo] = Seq(Todo("Todo1"), Todo("Todo2"))
  Ok(html.list(items))
}

3)listテンプレートの編集

こちらのHTMLを使用して書いてください。

ここを実装してください
<html>
    <head>
        <title>Todo</title>
    </head>
    <body>

        <section>
            <table>
                <thead>
                    <tr>
                        <th>名前</th>
                    </tr>
                </thead>
                <tbody>
                ここを実装してください
                </tbody>
            </table>
        </section>

    </body>
</html>

list.scala.html

@import services._
@(items: Seq[Todo])

<html>
    <head>
        <title>Todo</title>
    </head>
    <body>

        <section>
            <table>
                <thead>
                    <tr>
                        <th>名前</th>
                    </tr>
                </thead>
                <tbody>
                @items.map { todo =>
                    <tr>
                        <td>@todo.name</td>
                    </tr>
                }
                </tbody>
            </table>
        </section>

    </body>
</html>

4)画面をブラウザで見てみましょう

リスト画面

9.データベースの設定

目的:evolutions、anormの使い方

1)evolutionsについて

今回データベースを使用するにあたりDBマイグレーションツールを使用します。DBマイグレーションツールとは、スキーマのバージョン管理のようなもので、スキーマの変更に対してデータベースを追随させることができます。evolutionsはPlayframeworkで使われているDBマイグレーションツールです。

2)Anormについて

データベースアクセスにはAnormというライブラリを使用します。SQLを直接記述できSQL実行/結果解析をサポートします。

3)build.sbtの設定

プロジェクトルートディレクトリ配下のbuild.sbtを編集します。
build.sbt.png
次の2行をlibraryDependenciesに追加してください。

"com.typesafe.play" %% "anorm" % "2.5.0",
evolutions,
libraryDependencies ++= Seq(
  jdbc,
  cache,
  ws,
  "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test
)

次のようになります。

libraryDependencies ++= Seq(
  "com.typesafe.play" %% "anorm" % "2.5.0",
  evolutions,
  jdbc,
  cache,
  ws,
  "org.scalatestplus.play" %% "scalatestplus-play" % "1.5.1" % Test
)

4)evolutionsの設定ファイルの作成

confディレクトリの下に[evolutions/default]ディレクトリを作成します。
conf.png

IntelliJ上で[conf]ディレクトリを右クリックし、[New]の[Directory]をクリックします。
handson5_01.png

表示されたダイアログに[evolutions/default]と入力しOKをクリックします。
evolutions05.png

5)マイグレーションファイルの作成

作成したディレクトリにマイグレーションファイルを作成します。

IntelliJ上で[evolutions.default]ディレクトリを右クリックし、[New]の[File]をクリックします。
handson5_03.png

表示されたダイアログに[1.sql]と入力しOKをクリックします。
handson5_04.png

1.sql

# --- First database schema

# --- !Ups
create table todo (
  id                        bigint not null auto_increment,
  name                      varchar(255) not null,
  constraint pk_todo primary key (id))
;
create sequence todo_seq start with 1000;

insert into todo (id,name) values (1,'書類の整理');
insert into todo (id,name) values (2,'本の返却');

# --- !Downs
drop table if exists todo;

drop sequence if exists todo_seq;

6)データベースの設定

confディレクトリの下のapplication.confの次の部分のコメントを外します。
appconf.png

H2 Databaseと言うJavaプラットフォーム上で動く、インメモリデータベースを使用するように設定されます。

default.driver = org.h2.Driver
default.url = "jdbc:h2:mem:play"

次のようにします。

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
}

7)データベースを作成しましょう

一旦、Ctrl + Dでサーバーを停止したのち再度サーバーを起動します。

activator run

リスト表示画面にアクセスすると、データベース作成画面が表示されます。
[Apply this script now!]ボタンをクリックしデータベースを作成します。
リスト表示画面

evolutions01.png

10.リスト画面の作成(データベース)

目的:Scalaでのデータベースの使い方
handson6.png

handson4_01.png

1)モデルの編集

Todo.scala

package services

import javax.inject.Inject

import anorm.SqlParser._
import anorm._
import play.api.db.DBApi

import scala.language.postfixOps

case class Todo(name: String)

@javax.inject.Singleton
class TodoService @Inject() (dbapi: DBApi) {

  private val db = dbapi.database("default")

  val simple = {
    get[String]("todo.name") map {
      case name => Todo(name)
    }
  }

  def list(): Seq[Todo] = {

    db.withConnection { implicit connection =>

      SQL(
        """
          select * from todo
        """
      ).as(simple *)

    }

  }

}

2)TodoControllerでTodoServiceを使えるように設定する

class TodoController @Inject()(todoService: TodoService, val messagesApi: MessagesApi) extends Controller with I18nSupport {

3)TodoControllerのlistメソッドの編集

TodoController.scala

def list() = Action { implicit request =>
  val items: Seq[Todo] = todoService.list()
  Ok(html.list(items))
}

4)画面をブラウザで見てみましょう

リスト画面

11.登録画面の作成(テンプレート)

目的:Playのフォームテンプレートヘルパーの使い方
handson7.png

画面表示
handson7_02.png

登録後
handson7_03.png

1)routesにcreateとsave用のルートを追加

/play-hands-on/conf/routes

GET     /todo/new                   controllers.TodoController.create()

POST    /todo                       controllers.TodoController.save()

2)TodoControllerにメソッドを追加

TodoController.scala

  val todoForm: Form[String] = Form("name" -> nonEmptyText)

  def create = Action {
    Ok(html.createForm(todoForm))
  }

  def save() = Action { implicit request =>
    val name: String = todoForm.bindFromRequest().get
    println(name)
    Ok("Save")
  }

3)createFormテンプレートの作成

/play-hands-on/app/viewsの下にcreateFormテンプレートを作成します。

IntelliJ上でviewsディレクトリを右クリックし、[New]の[File]をクリックします。
a1.png

表示されたダイアログに[createForm.scala.html]と入力しOKをクリックします。
a2.png

こちらのHTMLを使用して書いてください。

ここを実装してください
<html>
    <head>
        <title>Todo</title>
    </head>
    <body>

        <h1>Todo登録</h1>

        ここを実装してください

    </body>
</html>

createForm.scala.html

@import helper._

@(todoForm: Form[String])(implicit messages: Messages)

<html>
<head>
    <title>Todo</title>
</head>
<body>

<h1>Todo登録</h1>

@form(action = routes.TodoController.save()) {

<fieldset>

    @inputText(todoForm("name"), '_label -> "名前")

</fieldset>

<input type="submit" value="登録">

}

</body>
</html>

4)画面をブラウザで見てみましょう

登録画面

12.登録画面の作成(データベース)

目的:データベースの利用(登録編)
handson8.png

画面表示
handson8_01.png

登録後
handson8_02.png

1)TodoServiceにメソッドを追加します。

  def insert(todo: Todo) = {
    db.withConnection { implicit connection =>
      SQL(
        """
        insert into todo values ((select next value for todo_seq), {name})
        """
      ).on(
        'name -> todo.name
      ).executeUpdate()
    }
  }

2)TodoControllerのsaveメソッドを次のように編集します。

TodoController.scala

def save() = Action { implicit request =>
  val name: String = todoForm.bindFromRequest().get
  todoService.insert(Todo(name))
  Redirect(routes.TodoController.list())
}

3)画面をブラウザで見てみましょう

登録画面