Tech Sketch Bucket of Technical Chips by TIS Inc.

Scala/Play2でWebアプリケーション開発~(2)Slick2.0で簡単アプリ開発

Pocket

前回の Scala/Play2でWebアプリケーション開発~(1)環境構築&デフォルトアプリの解説 ではPlay2の環境構築方法とデフォルトアプリについて紹介しました。今回はこの環境を元に簡単なWebアプリケーションを作っていきます。データベースに対して簡単なCRUD操作を行うアプリケーションを作るために、データベースはすぐに使える H2Database (以下、H2)を利用し、データベースへのアクセスは Slick を利用することにします。

アプリケーションを開発する事前準備としてH2DatabaseとSlickを使うための設定をEclipseと設定ファイルに少しだけ追加した後、「C:登録機能」、「R:参照機能」、「U:更新機能」、「D:削除機能」の順に作っていきます。


開発前の準備:データベースへの接続設定

前回構築した環境に対して、EclipseからH2を利用するためのプラグイン「DBLauncher」のインストールを行います。加えて、Play2のアプリケーションからSlickを利用するための設定とデータベースへの接続設定を行います。

(1) Eclipseプラグインのインストール

「ヘルプ」->「ソフトウェアのインストール」を選択し「インストール」ウィンドウを表示します。作業対象に「 http://eclipse.seasar.org/updates/3.2/ 」を入力し表示された一覧から「DBLauncher」を選択します。「次へ」を押下し、あとは流れに沿ってインストール作業を完了させて下さい。
次にEclipseからH2を利用するための設定を行います。H2のデータベースファイルを配備する任意のディレクトリを作成します。筆者はプロジェクト直下(techApp配下)に「data」とういディレクトリを作成しました。続いて、プロジェクトのプロパティ(プロジェクトを右クリック->「プロパティ」を選択)からDBLauncherの設定をします。「H2データベースを使用」をチェックし、ベース・ディレクトリに先ほど作成したデータファイル配備用ディレクトリ(/techApp/data)を指定して下さい。

play-eclipsh2.png

インストールが完了したら、プラグインが利用できることを確認しておきましょう。パッケージ・エクスプローラーでプロジェクトを右クリックすると「H2」というメニューが追加されていますので、「H2サーバの起動」を選択するとH2が起動されます。(「H2サーバの停止」で停止することが出来ます。)

(2) Slickライブラリの設定

Play2のビルドは「 SBT 」で行われますので、新たにライブラリを使用するためはSBTのビルドスクリプトに依存関係を追加する必要があります。Slickを利用するため、「project」ディレクトリ配下に以下のように「Build.scala」を作成し依存設定を行います。

・Build.scala (/project)

Slickはつい先日(2013/1/21)、 GA版がリリースされた ばかりの バージョン2.0 を使用します。
appNameとappVersionはbuild.sbt(プロジェクト直下)の内容に合わせて、それぞれ「techApp」「1.0-SNAPSHOT」としておきます。appDependenciesにSlickへの依存設定を行いました。依存設定は 「グループ名」%「アーティファクト名」%「リビジョン」 の形式で設定します。 「グループ名」%%「アーティファクト名」%「リビジョン」 のように「グループ名」の後ろに「 % 」を2つ重ねるとアーティファクト名にScalaのバージョンを追加します。
今回使用しているPlay2.2.1では、Scalaのバージョンは2.10ですので、ダウンロードするSlickのアーティファクト名は「slick_2.10」となります。

(3) データベースへの接続設定

データベースへの接続設定は「application.conf」に対して行います。H2へ接続するよう次のように設定します。

・application.conf (/conf)

JDBCドライバーに「org.h2.Driver」、データベース接続URLに「jdbc:h2:tcp://localhost:9092/demo」を指定し、ユーザは「sa」、パスワードは「なし」としました。

簡単アプリの開発

開発前の準備作業が完了したので、ここからソースコードの実装に入ります。今回は「イベントの情報」を登録・検索・更新・削除する簡単なアプリケーションを開発します。

(1) イベント登録機能

イベント登録機能は画面から「イベントID」と「イベント名」を入力し「登録」ボタンを押下すると、入力した内容がデータベースのEVENTテーブルに登録されるというシンプルな機能にします。イベント登録画面の初期表示は「EventCreate」オブジェクトの「index」で行い、登録は「create」で行います。

play-createflow.png

最初に、イベント登録画面の初期表示機能を実装します。routesファイルに次のように初期表示用のURLを定義します。

・routes (/conf)

「/event/create/」にGETリクエストがあると、「controllers.event.EventCreate.index」を呼び出します。

「/event/create/」で呼び出される「EventCreate」オブジェクトを「controllers.event」パッケージに作成します。「EventCreate」オブジェクトには「/event/create/」で呼び出される「index」関数と画面の入力項目を移送するフォームを実装します。

・[Controller] EventCreate.scala (/app/controllers/event)

「index」はイベント登録画面を初期表示するため「eventCreate.scala.html」でHTMLを生成し、「OK」でステータスコード200のレスポンスを返します。フォーム「eventFrom」は「eventId」と「eventNm」を「EventForm」という名前のCaseクラスにマッピングします。

次に、Caseクラスを作成します。引数にはControllerでマッピング指定した「eventId」と「eventNm」をString型で定義します。

・[Model] Form.scala (/app/models)

次に、イベント登録画面のHTMLを作成します。

・[View] eventCreate.scala.html (/app/views)

引数にはControllerから「EventForm」を受け取り、「form」タグと「input type="text"」タグはヘルパーを使用します。「form」の属性として「action」には「登録」ボタン押下によりSubmitするURLを指定しますが、まだ登録用の関数を作成していませんのでここでは「routes」に定義した初期表示の「controllers.event.routes.EventCreate.index()」を指定します。

では、ブラウザで「 http://localhost:9000/event/create/ 」にアクセスしてイベント登録画面を表示してみましょう。

play-eventcreate1.png

イベント登録画面が表示されました。実装を続ける前に、見栄えがよくなうようにBootstrapを適用しておきます。Bootstrapは、「main.scala.html」で「bootstrap.min.css」と「bootstrap-glyphicons.css」にリンク設定を行い、HTMLでインポートする「helper」パッケージをBootstrap用の「helper.twitterBootstrap」に変更するだけで適用されます。

・[View] main.scala.html (/app/views)

・[View] eventCreate.scala.html (/app/views)

もう一度イベント登録画面を表示すると、Bootstrapが適用されていることが分かります。今回はBootstrapのバージョン3.0.3を利用しましたが、出力するHTMLをBootstrapのバージョン3に対応するには「twitterBootstrapFieldConstructor.scala.html」のカスタマイズが必要になるので、そのあたりは次回以降に紹介します。

play-eventcreate2.png

 
次に「登録」ボタンをクリックした時の処理を実装していきます。

routesファイルに登録処理用のURLを定義します。

・routes (/conf)

formの内容をSubmitするのでリクエストはPOSTメソッドを指定します。呼び出されるのは「EventCreate」オブジェクトの「create」関数です。

「EventCreate」オブジェクトには、「create」「createTable」「dropTable」の3つの関数を追加します。

[Controller] EventCreate.scala (/app/controllers)

「create」関数は、フォームの内容をCaseクラス「Event」にセットしてEventsオブジェクトの「create」関数に渡し、画面で入力された情報をデータベースのEVENTテーブルに登録します。画面から入力されたフォームの値は、「implicit request =>」を定義することで「eventForm.bindFromRequest.get」によって取得することができます。登録が完了したら初期表示の「index」と同様に「eventCreate.scala.html」を返します。

また、画面からテーブルの作成と削除が出来るようにcreateTableとdropTableを作成しておきます。それぞれ、Eventsオブジェクトの「createTable」「dropTable」でテーブルの作成、削除を行い、初期表示画面に戻ります。

次に、画面から入力されたイベント情報をデータベースに登録するため、モデルを作成します。

・[Model] Event.scala (/app/models)

「EVENT」テーブルに対応するCaseクラス「Event」と「Events」オブジェクトを定義します。「Events」オブジェクトはデータベースへのコネクション、テーブルにマッピングするフィールドを定義した「EventTag」クラスとデータベースアクセス関数を定義します。
「Database.forDataSource()」でデータベースへのコネクションを作成し、「EventTag」クラスにはテーブルのカラムに対応するフィールド「id」「eventId」「eventNm」を定義します。「id」をプライマリーキーとし自動採番(AutoInc)を設定しておきます。「*」にはCaseクラス「Event」と同じ型を返す変数群を設定する必要があります。

データベースへのINSERT処理は「create」関数で行います。「id」を自動採番し、引数で受け取った「Event」クラスから「イベントID」と「イベント名」をINSERTします。テーブルの作成、削除はそれぞれ、「createTable」と「dropTable」関数に実装します。

イベント登録画面からテーブル作成・削除機能を呼び出せるように「CREATE TABLE」と「DROP TABLE」というリンクを作成しておきます。また、「登録」ボタン押下時に、登録用のURLにSubmitするようにformタグのaction属性は「controllers.event.routes.EventCreate.create()」に変更しておきます。

・[View] eventCreate.scala.html (/app/views)

「createTable」「dropTable」のURLをroutesに追加します。

  • ・routes (/conf)

以上で登録機能が実装できましたので、実際にテーブルを作成してイベントを登録してみましょう。
そのまま登録ボタンをクリックとテーブルがないためエラーとなります。「CREATE TABLE」を行ったあとにもう一度登録ボタンをクリックしてみましょう。

play-eventcreate3.png

登録出来ました。しかし、まだ登録したデータを確認する手段がありません。登録したデータを確認するためのイベント情報の検索画面を作成しましょう。

(2) イベント検索機能

イベント検索機能は、検索条件に「イベントID」と「イベント名」を指定し「検索」ボタンを押下すると、入力した検索条件に一致するデータをデータベースのEVENTテーブルから検索し、結果を一覧で表示させるという機能とします。なお、「イベントID」は完全一致、「イベント名」は部分一致で検索出来るようにします。

まずはroutesに検索画面の初期表示URLと検索処理のURLを定義します。

・routes (/conf)

次に画面を実装します。入力項目は「イベントID」「イベント名」、検索結果として表示する項目も同様とします。

・[View] eventSearch.scala.html (/app/views)

検索条件入力用のテキストボックスはイベント登録画面と同様に「@helper.inputText」で作成し、検索結果は引数で受け取った「events:List[Event]」から表示します。Scalaテンプレートでは「@if」や「@for」のように「@」を利用してif文やfor文を記述することが出来ます。

コントローラは画面入力情報の受け取りにイベント登録と同じ「EventForm」を使用し、初期表示は「index」に検索処理は「search」に実装します。「index」は「eventSearch.scala.html」で初期画面を表示し、「search」は「eventForm.bindFromRequest」で受け取ったFormを検索条件として「Events」オブジェクトの「find」関数でEVENTテーブルを検索し、その結果を「eventSearch.scala.html」に結果を渡します。

・[Controller] EventSearch.scala (/app/controllers)

「Events」オブジェクトに「find」関数を実装します。「eventId」と「eventNm」はそれぞれ入力された場合のみ検索条件に追加し、検索結果は「eventNm」の昇順でソートします。「eventId」は完全一致なので「===」、「eventNm」は部分一致なので「like」で比較し、検索した結果を「Event」オブジェクトのListで返却します。

・[Model] Event.scala (/app/models)

以上でイベント検索画面が実装できましたので、「http://localhost:9000/event/」にアクセスし、画面を表示してみましょう。

play-eventsearch1.png

検索ボタンで検索すると先ほど登録したデータが表示されました。

(3) イベント更新機能

イベント更新機能はイベント検索画面の検索結果一覧から更新したいデータを選択することでイベント更新画面を表示します。イベント更新画面には選択したイベントの「イベントID」と「イベント名」が表示され、値を編集し「更新」ボタンを押下すると、その内容でデータベースのEVENTテーブルを更新するという機能とします。

まずはroutesに更新画面の初期表示URLと更新処理のURLを定義します。更新対象データを特定するため、それぞれデータのキーにあたる「id」を引数とします。

routes (/conf)

次にイベント更新画面を作成します。入力項目は「イベントID」と「イベント名」です。

・[View] eventUpdate.scala.html (/app/views)

更新するためのキーとして「id」を引数として受け取り、「更新」ボタンを押下すると、「EventUpdate」オブジェクトの「update」に「id」を引数としてSubmitします。

コントローラには初期表示の「index」とイベント更新の「update」を作成します。

・[Controller] EventUpdate.scala (/app/controllers)

「index」は「id」を引数として受け取り、「Events」オブジェクトの「findById」で更新対象のイベントデータを取得、そのデータを「EventForm」に詰めて、「eventUpdate.scala.html」に渡します。
「update」は「id」と更新対象データを画面から受け取り「Events」オブジェクトの「update」関数でイベントデータを更新し、元の画面を表示します。

「Events」オブジェクトに「findById」と「update」関数を実装します。「findById」はEVENTテーブルのキーである「id」を引数として受け取り、EVENTテーブルを検索し、得た結果を「Event」クラスで返します。「update」は「Event」クラスを引数で受け取り、その内容で「id」をキーとしてEVENTテーブルを更新します。

・[Model] Event.scala (/app/models)

イベント検索画面から更新したいデータを選択し更新画面に遷移できるように、検索結果一覧の右端にリンク用のアイコンを作成しておきます。

・[View] eventSearch.scala.html (/app/views)

以上でイベント更新画面が実装できましたので、画面から確認してみましょう。まずはイベント検索画面を表示し検索を行います。

play-eventsearch2.png

先ほど登録したデータが表示されましたので、検索結果一覧の一番右の列に表示された更新アイコンをクリックし更新画面を表示します。イベント更新画面が表示されたらデータ編集し更新してみましょう。

play-eventupdate1.png

更新が終わったら、イベント検索画面で結果を確認してみましょう。

(4) イベント削除機能

イベント削除機能はイベント検索画面の検索結果一覧から削除したいデータ行の削除アイコンを押下することで、データベースのEVENTテーブルから該当データを削除するという機能とします。

まずは、routesにイベント削除用のURLを定義します。

・routes (/conf)

イベント削除用の画面は設けず、検索画面の一覧から直接削除するので、「EventSearch」オブジェクトに「delete」関数を追加します。

・[Controller] EventSearch.scala (/app/controllers)

「delete」は「Events」オブジェクトの「delete」関数で対象データを削除し、再び検索画面を初期状態で表示します。

「Events」オブジェクトに「delete」関数を実装します。「delete」は引数に対象データの「id」を受け取り、受け取った「id」をキーにEVENTテーブルから該当行を削除します。

・[Model] Event.scala (/app/models)

イベント検索画面の検索結果一覧には、先ほど追加した更新アイコンの横に削除アイコンを作成し、データの削除が出来るようにします。

・[View] eventSearch.scala.html (/app/views)

以上でイベント削除機能が実装できましたので、画面で確認してみましょう。イベント検索画面で検索すると結果一覧にに削除アイコンが追加されていますので、クリックしデータを削除します。

play-eventsearch3.png

削除したら再度検索を行い確認してみましょう。データが削除されました。

以上で、イベント情報に対する登録・検索・更新・削除機能が完成しました。

(5) ラベルの日本語化

画面を見てみると表示されているラベルが「eventId」「eventNm」のように変数名がそのまま出力されています。これを日本語化しておきましょう。
変数名を日本語に置き換えるには「conf」ディレクトリに「messages」というファイルを作成し、変数名に対して日本語名を定義します。

・messages (/conf)

再度画面を確認するとラベルが日本語になりました。

play-eventsearch4.png

(6) ナビゲータの追加

最後に、各画面を行き来出来るように画面上部にナビゲータを追加しておきましょう。

イベント登録画面には「検索」「登録」のラベルを表示し、「登録」をアクティブにします。

・[View] eventCreatescala.html (/app/views)

play-eventcreate4.png

イベント検索画面にも「検索」「登録」のラベルを表示し、「検索」をアクティブにします。

・[View] eventSearch.scala.html (/app/views)

play-eventsearch5.png

イベント更新画面には「検索」「登録」「更新」のラベルを表示し、「更新」をアクティブにします。

・[View] eventUpdate.scala.html (/app/views)

play-eventupdate2.png

これでイベント管理アプリが完成しました。

しかし、お気付きの通り、今回は登録・検索・更新・削除のみを行う最低限の機能だけを実装しました。入力チェックもなければ、処理結果のメッセージ表示もありません。データベーストランザクション境界はどこにあって、異常終了した時のテーブルのデータはどうなっているのか?実際のアプリケーションとして利用するために考慮しておかなければいけないこと、実装しておくべきことはたくさんあります。見た目ももう少し良くしたいですね。
そのあたりは今後、少しずつ紹介して行きたいと考えています。

エンジニア採用中!私たちと一緒に働いてみませんか?