playはじめました

[参考](http://www.playframework-ja.org/documentation/2.1.5/JavaTodoList)


最近色々としがらみから開放されて時間に余裕ができたので、新しく何かに手を出そうと考えてました。まあ、タイトルでネタバレなんですが……。

やっぱ今まで避け続けてたwebアプリケーション辺りをさわろうかと。まあrailsでもやろうかとも思ったんですけど、大分乗り遅れてる感あるので、playでもいっかなーと(テキトー)。まあJava好きですし、Scalaもできるっぽいしで[play](http://www.playframework-ja.org/)に至りました。僕が全然webフレームワーク使ってないんで不安はあるんですが、心が折れるまで頑張ってみます。

playの開発はrailsとかと同じようにターミナルでパチパチやるか、Typesafe Activatorなるものを使って、ブラウザ上で開発するか。

環境が悪いのか、くっそ遅かったのでterminalでやります。なーんか見た感じ途中からでも変更できそうだし。

##インストール


[play install site](http://www.playframework-ja.org/download)
これの右側の「これまでの配布形態」の方です。直接リンク貼ろうと思ったけど、リンクが明らかにバージョンアップしたらダメになりそうだったので。

展開したら適当なディレクトリにおいてパスを通す。

```
export PATH=$PATH:/relativePath/to/play
```



ごめんなさい、普通にbrew installできました。
play helpコマンド打ってヘルプが表示されればおｋ。

[playのDocumentation](http://www.playframework-ja.org/documentation/2.1.5/JavaTodoList)を見ながらチュートリアルします。

terminal使おうかとも思ったけどなんかEclipseでもいいらしい。

##プロジェクト作成
新しくアプリケーション作成を行います。

```
$ play new todolist
```

これ実行したディレクトリにプロジェクトが作られるので実行する場所注意。なんかパス通したりしてても怖いか後から移動とかもやめたほうがいいかもです。
application nameはプロジェクト名にとりあえず同じにしといて、テンプレートはJavaを指定。
<img src="https://qiita-image-store.s3.amazonaws.com/0/28491/55c060bb-eaf6-c6fd-a962-4125792f4376.png">


play new  するとアプリケーション名(もしくはプロジェクト名)と同じ名前のディレクトリができる。

| ディレクトリ名 | 説明 |
|:-------------|:----|
| app/         | ここにmodels, views, controllersが入ってる。.javaファイルが入ってる|
| conf/        | 全てのアプリケーションの設定ファイルが入ってる。application.confがメイン。routesもある。|
| project/     | ビルドスクリプトが入ってる。ビルドスクリプトはsbt(?)に基づいてる。新しいPlayアプリケーションはアプリケーションを正常動作させるデフォルトのビルドスクリプトが同梱されている。|
| public/      | 全てのパブリックに利用可能なリソース(JavaScript, css, 画像イメージ)が入ってる|
| test/ |テストが入ってる。JUnitで書かれてる|


projectのsbtってなんだ……。
Simple Build Toolの略。Scalaのコンパイル、テストの実行に必要なツール。

- Scalaのコンパイル
- ライブラリの自動ダウンロード
- コードライブラリの作成(Scala, Javaで動くものはすべて使える)
- 作成したライブラリを公開サーバーにアップロードする
- テストの実行

ができる……らしい。よくわかんないね。飛ばして進もう。わかったら後から追記しようかな。

## playコンソール

playのコンソールを開いてrunする。

```
$ play
[todolist] $ run
```
developmentモードでアプリケーションが実行される。ブラウザを開き、
http://localhost:9000/
にアクセス。
Welcome to Playのページが表示されればおｋ。
英語で色々書かれてます。どうやらこのページがどう表示されているかを書いてくれているらしい。さすがチュートリアル。
このアプリのエントリーポイント(プログラムが最初に見るファイルらしい！初めて知った、そんな言葉！)はconf/routesファイル。ここでアプリケーションがアクセス可能なURL全てを定義している。生成されたroutesファイルを開けば初期のrouteを確認できる。

```routes
# Home page
GET     /                           controllers.Application.index()
```

webサーバーが/パスへGETリクエストを受信したらcontrollers.Application.index()メソッドを返す、という記述。早速ソースを見ていこう。controllersって書いてあるので当然app/controllersの下にあります。

```Java:Application.java
package controllers;

import play.*;
import play.mvc.*;

import views.html.*;

public class Application extends Controller {

    public static Result index() {
        return ok(index.render("Your new application is ready."));
    }

}
```
index()メソッドがResult型を返してる。アクションメソッドは全部この型を返さないといけない。

## URLとメソッドの設定

このTODOアプリのアクションとURLの対応を書いてやる必要がある。もちろんそれはroutesに書く。

とりあえず必要な操作は「新規作成」「閲覧」「削除」の3つ。これらを定義していく。まあ、こんな感じ。

```
# Home page
GET     /                       controllers.Application.index()
                                
# Tasks          
GET     /tasks                  controllers.Application.showTask()
POST    /task                  controllers.Application.createTask()
POST    /tasks/:id/delete       controllers.Application.deleteTask(id: Long)
```

これ、本来のチュートリアルではTaskを生成するメソッドがnewTaskなんだけど、気に食わないのでcreateTaskに変更。メソッド名は動詞じゃないと気持ち悪いんだけど、これ一般的なんかな。一応公式のチュートリアルだし……。
参考として[routesファイルの文法](http://www.playframework-ja.org/documentation/2.1.5/JavaRouting)

で、これ保存してブラウザリロード。サーバ立て直す必要はありません。
<img src="https://qiita-image-store.s3.amazonaws.com/0/28491/ee5b15f5-77ff-6193-8d1b-a3fc492f27a2.png" width="600">


「いや、そんなメソッド無いんだけど……」って感じのエラー。とりあえずエラー消すためにメソッド書きましょう。実装とかナシで。index()のしたに3つのメソッドを追加。

```Java:Application.java
    public static Result showTasks() {
        return TODO;
    }

    public static Result createTask() {
        return TODO;
    }

    public static Result deleteTask(Long id) {
        return TODO;
    }
```

確認してみましょう。http://localhost:9000/tasks

<img src="https://qiita-image-store.s3.amazonaws.com/0/28491/0f5bcdf3-1345-fff8-277a-e07f95e66e96.png" width="400">

まあ、当然な感じ。とりあえずこれで良しとする。まず最初にいじるべきはtopページ。topページは今あるtaskの一覧であるべき。topページをいじるならindexアクションをいじる。indexアクションでタスク一覧にリダイレクトするように変更する。

```Java:Application.java
    public static Result index() {
		// 変更前
		// return ok(index.render("Your new application is ready!"));

		// 変更後
		return redirect(routes.Apprication.showTasks());
    }
```

## Taskモデル
TodoListの扱うTaskを定義する。Taskは扱うデータ、つまりモデル。app/model下にクラスを生成する。

```Java:Task.java
package models;

import java.util.ArrayList;

public class Task {
	public Long id;
	public String label;
	
	public static ArrayList<Task> all() {
		return new ArrayList<Task>();
	}
	
	public static void create(Task task) {
	} 
	
	public static void delete(Long id) {	
	}
}
```
とりあえずダミー実装。後からデータベースに格納する処理を書きそう。

## アプリケーションテンプレート
なんのことかよーわからんかったけど、viewsの下に書くものだからviwerでしょう。
Scalaライクなhtml生成用のテンプレートっぽい。
views/Application/index.scala.htmlっていうテンプレートを作ったら、views.html.Application.indexというrender()メソッドを持ったクラスが生成される。このメソッドはstaticなので、コードのどこからでも呼び出せる。Application.javaに書いてあったrender()メソッドのこと。

### 文法"@"
Scalaテンプレートでは@という文字が唯一の特殊文字。この文字がテンプレート中に出てくると動的コードとして認識される。エスケープしたいなら@@と書けばよい。

### テンプレート引数
引数を取れる。引数はテンプレートの先頭行で宣言する。
こんな感じ

```
@ (customer: models.Customer, orders:List[models.Order])
```

変数名:型名　ですね。

それらを踏まえて、アプリケーションテンプレートを書く。

```html:index.scala.html
@(tasks: List[Task], taskForm: Form[Task])
@* 引数の受け取り *@
@import helper._

@main("Todo list") {
    
    <h1>@tasks.size() task(s)</h1>
    @* Taskが何個あるか出力 *@
    <ul>
        @for(task <- tasks) {
            <li>
                @task.label
                @* Taskのラベルを出力し、 *@

				@form(routes.Application.deleteTask(task.id)) {
					@* Taskのラベルの横には削除用のフォームを設置 *@
                    <input type="submit" value="Delete">
					@* 削除ボタン *@
                }
            </li>
        }
    </ul>
    
    <h2>Add a new task</h2>
    
    @form(routes.Application.newTask()) {
        
        @inputText(taskForm("label")) 
		@* 作成するTaskの入力フォーム *@
        
        <input type="submit" value="Create">
		@* 作成ボタン *@
        
    }
}   
```

### TaskForm
Applicationコントローラ(controllers/Application.java)にTaskを生成する型、Form<Task>をもたせる。以下のコードを追加すればよい。

```Java:Application.java
static Form<Task> taskForm = Form.form(Task.class);
```
あとlabelを必須にするため、TaskクラスのString labelに@Requierdアノテーションをつけてみる。

```Java:Task.java
@requierd
public String label;
```

## 最初のページをレンダリングする。
準備がだいたいできたので、tasksアクション(TaskクラスのshowTasks)を実装する。

``` Java:Application.java
public static Result tasks() {
	return ok(
		views.html.index.render(Task.all(), taskForm)
	);
}
```
showTasksによりTaskListとTaskFormを呼び出し、index.scala.htmlテンプレートでレンダリングしたhtmlを返す。

確認してみましょう。http://localhost:9000/tasks

<img src="https://qiita-image-store.s3.amazonaws.com/0/28491/51dead6d-fb74-840b-47e7-12350235de22.png" width="200">

やったー、糞UIのTaskListができた！まあ、create押して例のunimplementsなんちゃらが出るだけだよね。実装してないし。実装しましょう。createTask()を実装します。

```Java:Application.java
	public static Result createTask() {
		Form<Task> filledForm = taskForm.bindFromRequest();
		// Form内にエラーがあるかどうかチェック
		
		if (filledForm.hasErrors()) { // errorがあるならbadRequest
			return badRequest( views.html.index.render(Task.all(), filledForm));
		}
		
		// Form にエラーがないならTaskを作って再表示
		Task.create(filledForm.get());
		return redirect(routes.Application.showTasks());
	}
```

なんかチュートリアルのコードは無駄なelse句があって見づらかったのでリファクタ。うーん、この時点でcreateしたTaskの表示くらいはできると思ったんだけど、なんかできない。続けたらできるのかね。とりあえずスルーして続けてみよう。

## データベース内のTaskの永続化

application.confでコメントアウトされている内容をコメントインすることでデータベースの使用を可能にする。私の環境だとL36, L37だったけど、他だと違ったりするかも。

```application.conf
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
```

H2というシンプルなインメモリデータベースを使用する。サーバの再起動は必要なく、ブラウザの更新だけでおｋ。インメモリってことは、PCシャットダウンした時とかにデータが残らない。残したいならファイルに保存すべき。
db.default.url="jdbc:h2:mem:play"を
db.default.url="jdbc:h2:file:play"にすればおｋ。

次はApplication.confでEbeanを有効にする。さっきのようにコメントインする。L54だった。

Ebean

```Application.conf
ebean.default="models.*"
```

modelsパッケージ以下にある全てのエンティティ(オブジェクトのこと？)を管理する。

EbeanってのはO/Rマッピングフレームワーク。オブジェクトとリレーショナルデータベースを対応付けること。アプリケーションとデータベースのインターフェイス、かな？SQL文とかを直接埋め込むのは手間いし、なにより使用するデータベースが変更になった時に修正しないといけないとこが膨大になったりする。O/Rマッピングを使用したらJavaのコードを変更する必要がなくなる。なのでデータベースは直でさわるものではなく、O/Rマッピングを介して使用する。
PlayはEbeanに対応している。

modelをEbeanで管理するにはもう少しやることがある。app/models/Task.javaをちょっと変更する。

```Java:Task.java
@Entity // 変更点1アノテーション追加
public class Task extends Model {
	// 変更点2　Modelクラスを敬称
	
	@Id // 変更点1アノテーション追加
	public Long id;
	
	@Required
	public String label;

	// 変更点3findヘルパーを作成
	public static Finder<Long, Task> finder = new Finder<Long, Task>(Long.class, Task.class);
	....
}
```

Modelクラスをextendsはなんとなくわかる。あとはアノテーションか。

|アノテーション|説明|
|:--|:--|
|@Entity|このクラスがDBテーブルを表すオブジェクト|
|@Id|DBテーブルのprimary key|
|@NotNull|NotNull制約|
|@CreatedTimestamp|作成時の時間|
|@Version|更新時の時間|

こんな感じ。finderはデータベースとJavaをつなぐもの。これを介してデータベースを操作する。
CRUD操作のメソッドを実装する。

```Java:Task.java
	public static List<Task> all() {
		return finder.all();
	}
	
	public static void create(Task task) {
		task.save();
	} 
	
	public static void delete(Long id) {
		finder.ref(id).delete();
	}
```
これで、Task生成はできるようになった。createしてみよう。
<img src="https://qiita-image-store.s3.amazonaws.com/0/28491/a9463dd2-5160-8980-5019-76c223376cdf.png" width ="300">
できた、糞UI!

で、実はdeleteの処理がまだ。上で書いたのはModelのdelete処理を作っただけ。Application側にdeleteの処理を書いてないので実装する。

```Java:Application.java
	public static Result deleteTask(Long id) {
		Task.delete(id); // delete
		
		return redirect(routes.Application.showTasks()); // 再表示
	}
```

わあ簡単。
うーん、最初からScalaでやりゃあよかったかなー。どうもテンプレート的にScala必須っぽいし。とりあえずチュートリアル終わり、後はテキトーになんか書いてみようかなー。