【#Play】続・Play Framework 2.3 (Scala) を使った Web システム開発入門 #Play_ja #Scala #rpscala

Play Framework 2.3.x と Scala を使った Web システム開発について勉強した際の内容を自分用のメモとしてまとめました。

この記事は、[【#Play】Play Framework 2.3 (Scala) を使った Web システム開発入門](http://qiita.com/kukita/items/6326f1439abec013a479) の続きです。

「顧客情報の登録/検索/更新/削除」、「商品情報の登録/検索/更新/削除」、そして、「受注登録」を行うための簡単な 受注管理システムを作成することを目指します。

## 3: 「商品情報管理」機能の実装

前回までの部分で 「顧客情報管理」 機能の実装が完了しました。

同じ要領で 「商品情報管理」 機能を実装していくとともに、Home ページが  「Hello World」 のままなのでこれを修正していきます。

### 手順 3-1: ファイル `app/controllers/Application.scala` を編集します

```scala:app/controllers/Application.scala
package controllers

import play.api._
import play.api.mvc._

object Application extends Controller {

  def index = Action {
    Ok(views.html.index())
  }

}

```

### 手順 3-2: ファイル `app/views/index.scala.html` を編集します

```html:app/views/index.scala.html
@main(title = "受発注管理システム") {
 
<!-- ヘッダー -->
@header()

<div class="row">
  <div class="container">
    <!-- コンテンツ -->
    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
      <ul class="nav nav-tabs nav-stacked">
        <li><h2>顧客情報管理</h2></a></li>
        <li><a href="/customers/search"><h2>顧客情報一覧（検索/更新/削除）</h2></a></li>
        <li><a href="/customers/create"><h2>顧客情報登録</h2></a></li>
        <li><h2>商品情報管理</h2></a></li>
        <li><a href="/items/search"><h2>商品情報一覧（検索/更新/削除）</h2></a></li>
        <li><a href="/items/create"><h2>商品情報登録</h2></a></li>
        <li><h2>受注管理</h2></a></li>
        <li><a href="/orderings/search"><h2>受注情報一覧（検索/削除）</h2></a></li>
        <li><a href="/orderings/create"><h2>受注登録</h2></a></li>
      </ul>
    </div>
  </div>
</div>
}

```

ホームページには、各画面へのリンクの一覧を表示することにします。

### 手順 3-3: ファイル `app/views/header.scala.html` を編集します

```html:app/views/header.scala.html
<div class="navbar navbar-inverse navbar-static-top">
  <div class="container">
    <div class="navbar-header">
      <a class="navbar-brand" href="/">受注管理システム</a>
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
    </div>
    <div class="navbar-collapse collapse">
      <ul class="nav navbar-nav" role="menu">
        <li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown">顧客情報管理</a>
          <ul class="dropdown-menu">
            <li><a role="menuitem" href="/customers/search">顧客情報一覧（検索/更新/削除）</a></li>
            <li><a role="menuitem" href="/customers/create">顧客情報登録</a></li>
          </ul>
        </li>
        <li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown">商品情報管理</a>
          <ul class="dropdown-menu">
            <li><a href="/items/search">商品情報一覧（検索/更新/削除）</a></li>
            <li><a href="/items/create">商品情報登録</a></li>
          </ul>
        </li>
        <li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown">受注管理</a>
          <ul class="dropdown-menu">
            <li><a href="/orderings/search">受注情報一覧（検索/削除）</a></li>
            <li><a href="/orderings/create">受注登録</a></li>
          </ul>
        </li>
      </ul>
    </div>
  </div>
</div>

```

ヘッダー部分のメニューに「商品情報管理」機能、「受注管理」機能を追加します。

### 手順 3-4: ブラウザで、`http://localhost:9000/` にアクセスしホームページが表示されることを確かめます

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180215.png" class="hatena-fotolife" itemprop="image"></span></p>

ホームページが出来上がりました。続いて 「商品情報管理」 機能を実装していきます。

### 手順 3-5: ファイル `conf/route` を編集します

```scala:conf/route
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index

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

#
# Customers Management
#

# Create customer infomation.
GET     /customers/create           controllers.CustomerController.showCreateForm()
POST    /customers/create           controllers.CustomerController.create()

# Search customer infomation.
GET     /customers/search           controllers.CustomerController.search(word: String ?= "")

# Update customer infomation.
GET     /customers/:id/update       controllers.CustomerController.showUpdateForm(id: Long)
POST    /customers/:id/update       controllers.CustomerController.update(id: Long)

# Remove customer infomation
GET     /customers/:id/remove       controllers.CustomerController.remove(id: Long)

#
# Items Management
#

# Create item infomation.
GET     /items/create               controllers.ItemController.showCreateForm()
POST    /items/create               controllers.ItemController.create()

# Search item infomation.
GET     /items/search               controllers.ItemController.search(word: String ?= "")

# Update item infomation.
GET     /items/:id/update           controllers.ItemController.showUpdateForm(id: Long)
POST    /items/:id/update           controllers.ItemController.update(id: Long)

# Remove item infomation.
GET     /items/:id/remove           controllers.ItemController.remove(id: Long)

```

### 手順 3-6: ファイル `app/models/Item.scala` を編集します

```scala:app/models/Item.scala
package models

import play.api.db.slick.Config.driver.simple._

/**
 * DTO の定義
 */
case class Item(ID: Long, name: String, price: Long, comment: String)

/**
 * テーブルの定義
 */
class ItemTable(tag: Tag) extends Table[Item](tag, "items") {
  def ID = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name", O.NotNull)
  def price = column[Long]("price", O.NotNull)
  def comment = column[String]("comment", O.NotNull)
  def * = (ID, name, price, comment) <> (Item.tupled, Item.unapply)
}

/**
 * DAO の定義
 */
object ItemDAO {
  lazy val itemQuery = TableQuery[ItemTable]
  
  /**
   * キーワード検索
   * @param word
   */
  def search(word: String)(implicit s: Session): List[Item] = {
    itemQuery.filter(row => (row.name like "%"+word+"%") || (row.comment like "%"+word+"%")).list
  }
  
  /**
   * ID検索
   * @param ID
   */
  def searchByID(ID: Long)(implicit s: Session): Item = {
    itemQuery.filter(_.ID === ID).first
  }
  
  /**
   * 作成
   * @param item
   */
  def create(item: Item)(implicit s: Session) {
    itemQuery.insert(item)
  }
  
  /**
   * 更新
   * @param item
   */
  def update(item: Item)(implicit s: Session) {
    itemQuery.filter(_.ID === item.ID).update(item)
  }
  
  /**
   * 削除
   * @param item
   */
  def remove(item: Item)(implicit s: Session) {
    itemQuery.filter(_.ID === item.ID).delete
  }
}

```


### 手順 3-7: ファイル `app/controllers/ItemController.scala` を編集します

```scala:app/controllers/ItemController.scala
package controllers

/**
 * ① パッケージのインポート
 */
import play.api._
import play.api.mvc._

import play.api.data._
import play.api.data.Forms._

import play.api.db.slick._
import models._

/**
 * ② コントローラーオブジェクトの定義
 */
object CustomerController extends Controller {

  /**
   * ③ HTTP フォームデータの定義
   */
  val customerForm = Form(
    mapping(
      "ID" -> longNumber,
      "name" ->  nonEmptyText(maxLength = 140),
      "email" ->  nonEmptyText(maxLength = 140),
      "tel" ->  nonEmptyText(maxLength = 140),
      "address" ->  nonEmptyText(maxLength = 140),
      "comment" -> text(maxLength = 140)
    )(Customer.apply)(Customer.unapply)
  )
  
  /**
   * ④ 顧客情報登録フォーム表示アクションメソッドの定義
   */
  def showCreateForm() = Action { request =>
    Ok(views.html.customerCreateForm(customerForm))
  }
  
  /**
   * ⑤ 顧客情報登録アクションメソッドの定義
   */
  def create() = DBAction { implicit rs =>
    customerForm.bindFromRequest.fold(
      errors => BadRequest(views.html.customerCreateForm(errors)),
      customer => {
        CustomerDAO.create(customer)
        Redirect(routes.CustomerController.search())
      }
    )
  }
  
  /**
   * ⑥ 顧客情報検索アクションメソッドの定義
   */
  def search(word: String) = DBAction { implicit rs =>
    Ok(views.html.customerSearch(word, CustomerDAO.search(word)))
  }
  
  /**
   * ⑦ 顧客情報更新フォーム表示アクションメソッドの定義
   */
  def showUpdateForm(ID: Long) = DBAction { implicit rs =>
    Ok(views.html.customerUpdateForm(ID, customerForm.fill(CustomerDAO.searchByID(ID))))
  }

  /**
   * ⑧ 顧客情報更新アクションメソッドの定義
   */
  def update(ID: Long) = DBAction { implicit rs =>
    customerForm.bindFromRequest.fold(
      errors => BadRequest(views.html.customerUpdateForm(ID, errors)),
      customer => {
        CustomerDAO.update(customer)
        Redirect(routes.CustomerController.search())
      }
    )
  }

  /**
   * ⑨ 顧客情報削除アクションメソッドの定義
   */
  def remove(ID: Long) = DBAction { implicit rs =>
    CustomerDAO.remove(CustomerDAO.searchByID(ID))
    Redirect(routes.CustomerController.search())
  }
}

```

### 手順 3-8: ファイル `app/views/itemCreateForm.scala.html` を編集します

```html:app/views/itemCreateForm.scala.html
@(itemForm: Form[Item])

@import helper._

@main(title = "受発注管理システム - 商品情報登録") {

<!-- ヘッダー -->
@header()

<div class="row">
  <div class="container">
  
    <!-- サイドメニュー -->
    <div class="hidden-xs col-sm-3 col-md-3 col-lg-3">
      <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <ul class="nav nav-pills nav-stacked">
            <li><a href="/items/search">商品情報一覧（検索/更新/削除）</a></li>
            <li><a href="/items/create">商品情報登録</a></li>
          </ul>
        </div>
      </div>
    </div>
    
    <!-- コンテンツ -->
    <div class="col-xs-12 col-sm-9 col-md-9 col-lg-9">
      <div class="row">
      
        <!-- 見出し -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <h1>商品情報登録</h1>
        </div>
        
        <!-- フォーム -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          @form(routes.ItemController.create()) {
            <fieldset>
              <input type="hidden" name="ID" value="0">
              @inputText(itemForm("name"), '_label -> "商品名", 'size -> 30)
              @inputText(itemForm("price"), '_label -> "商品価格", 'size -> 30)
              @inputText(itemForm("comment"), '_label -> "備考", 'size -> 30)
            </fieldset>
            <div class="actions">
              <input type="submit" class="btn btn-primary">
              <a href="/items/search" class="btn btn-danger">キャンセル</a>
            </div>
          }
        </div>
      </div>
    </div>
 </div>
}

```

### 手順 3-9: ファイル `app/views/itemSearch.scala.html` を編集します

```html:app/views/itemSearch.scala.html
@(word: String, items: List[Item])

@main(title = "受発注管理システム - 商品情報一覧") {
 
<!-- ヘッダー -->
@header()

<div class="row">
  <div class="container">
  
    <!-- サイドメニュー -->
    <div class="hidden-xs col-sm-3 col-md-3 col-lg-3">
      <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <ul class="nav nav-pills nav-stacked">
            <li><a href="/items/search">商品情報一覧（検索/更新/削除）</a></li>
            <li><a href="/items/create">商品情報登録</a></li>
          </ul>
        </div>
      </div>
    </div>
    
    <!-- コンテンツ -->
    <div class="col-xs-12 col-sm-9 col-md-9 col-lg-9">
      <div class="row">
        
        <!-- 見出し -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <h1>商品情報一覧（検索/更新/削除）</h1> 
        </div>
        
        <!-- 検索フォーム -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
        @helper.form(action=routes.ItemController.search()) {
          <input type="search" name="word" value="@word">
          <input type="submit" value="キーワード検索" class="byn btn-primary">
        }
        </div>
        
        <!-- テーブル -->
        <div class="table-responsive col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <table class="table table-bordered table-striped ">
            <thead>
              <tr>
                <th>商品番号</th>
                <th>商品名</th>
                <th>商品価格</th>
                <th>備考</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
            @items.map { item =>
              <tr>
                <td align="right">@("%3d".format(item.ID))</td>
                <td>@item.name</td>
                <td align="right">@("%,d".format(item.price))</td>
                <td>@item.comment</td>
                <td align="center">
                  <a href="/items/@item.ID/update" class="btn btn-primary">更新</a>
                  <input type="button" class="btn btn-danger" value="削除" onClick='if(confirm("本当に削除してよろしいですか？")) {location.href="/items/@item.ID/remove"}'>
                </td>
              </tr>
            }
            </tbody>
          </table>
        </div>
      </div>
    </div>
 </div>
}

```

### 手順 3-10: ファイル `app/views/itemSearch.scala.html` を編集します

```html:app/views/itemSearch.scala.html
@(ID: Long, itemForm: Form[Item])

@import helper._

@main(title = "受発注管理システム - 商品情報更新") {
 
<!-- ヘッダー -->
@header()

<div class="row">
  <div class="container">
  
    <!-- サイドメニュー -->
    <div class="hidden-xs col-sm-3 col-md-3 col-lg-3">
      <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <ul class="nav nav-pills nav-stacked">
            <li><a href="/items/search">商品情報一覧（検索/更新/削除）</a></li>
            <li><a href="/items/create">商品情報登録</a></li>
          </ul>
        </div>
      </div>
    </div>
    
    <!-- コンテンツ -->
    <div class="col-xs-12 col-sm-9 col-md-9 col-lg-9">
      <div class="row">
        
        <!-- 見出し -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <h1>商品情報更新</h1> 
        </div>
                
        <!-- フォーム -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          @form(routes.ItemController.update(ID)) {
            <fieldset>
              <input type="hidden" name="ID" value="@ID">
              @inputText(itemForm("name"), '_label -> "商品名", 'size -> 30)
              @inputText(itemForm("price"), '_label -> "商品価格", 'size -> 30)
              @inputText(itemForm("comment"), '_label -> "備考", 'size -> 30)
            </fieldset>
            <div class="actions">
              <input type="submit" class="btn btn-primary">
              <a href="/items/search" class="btn btn-danger">キャンセル</a>
            </div>
          }
        </div>
      </div>
    </div>
 </div>
}

```

### 手順 3-11: ブラウザで、`http://localhost:9000/` にアクセスします

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180242.png" class="hatena-fotolife" itemprop="image"></span></p>

モデル層に変更を加えたため、テーブルスキーマの変更（DROP TABLE → CREATE TABLE）が必要になります。

### 手順 3-12: `Apply this script now!` ボタンをクリックします

「商品情報管理」機能が実装できました。

#### 商品情報登録

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180300.png" class="hatena-fotolife" itemprop="image"></span></p>

#### 商品情報一覧

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180325.png" class="hatena-fotolife" itemprop="image"></span></p>

#### 商品情報更新

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180342.png" class="hatena-fotolife" itemprop="image"></span></p>

## 4: Slick を使った "外部キーの設定" と "テーブルの結合"

「受注管理」機能を実装していきます。

「受注管理」機能では、`受注テーブル` 上に `顧客テーブル`、`商品テーブル` を参照するための ”外部キーの設定" をしたり、情報を参照する際に "テーブルの結合" をしたりする必要があります。

ここでは、Slick を使って、これらをどう実装するかを中心に説明していきます。

### 手順 4-1: ファイル `conf/route` を編集します

```scala:conf/route
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index

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

#
# Customers Management
#

# Create customer infomation.
GET     /customers/create           controllers.CustomerController.showCreateForm()
POST    /customers/create           controllers.CustomerController.create()

# Search customer infomation.
GET     /customers/search           controllers.CustomerController.search(word: String ?= "")

# Update customer infomation.
GET     /customers/:id/update       controllers.CustomerController.showUpdateForm(id: Long)
POST    /customers/:id/update       controllers.CustomerController.update(id: Long)

# Remove customer infomation
GET     /customers/:id/remove       controllers.CustomerController.remove(id: Long)

#
# Items Management
#

# Create item infomation.
GET     /items/create               controllers.ItemController.showCreateForm()
POST    /items/create               controllers.ItemController.create()

# Search item infomation.
GET     /items/search               controllers.ItemController.search(word: String ?= "")

# Update item infomation.
GET     /items/:id/update           controllers.ItemController.showUpdateForm(id: Long)
POST    /items/:id/update           controllers.ItemController.update(id: Long)

# Remove item infomation.
GET     /items/:id/remove           controllers.ItemController.remove(id: Long)

#
# Orderings Management
#

# Create ordering infomation.
GET     /orderings/create           controllers.OrderingController.showCreateForm()
POST    /orderings/create           controllers.OrderingController.create()

# Search ordering infomation.
GET     /orderings/search           controllers.OrderingController.search(word: String ?= "")

# Remove ordering infomation.
GET     /orderings/:id/remove       controllers.OrderingController.remove(id: Long)

```

### 手順 4-2: ファイル `app/models/Ordering.scala` を作成します

```scala:app/models/Ordering.scala
package models

/**
 * Slick 関連のパッケージのインポート
 */
import play.api.db.slick.Config.driver.simple._

/**
 * DTO の定義
 */
case class Ordering(ID: Long, createdAt: Long, customerID: Long, itemID: Long, itemCount: Long, comment: String)

/**
 * テーブルの定義
 */
class OrderingTable(tag: Tag) extends Table[Ordering](tag, "orderings") {
  def ID = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def createdAt = column[Long]("created_at", O.NotNull)
  def customerID = column[Long]("customer_id", O.NotNull)
  def itemID = column[Long]("item_id", O.NotNull )
  def itemCount = column[Long]("item_count", O.NotNull)
  def comment = column[String]("commnet", O.NotNull)
  def * = (ID, createdAt, customerID, itemID, itemCount, comment) <> (Ordering.tupled, Ordering.unapply)
  def customer = foreignKey("customer_id", customerID, OrderingDAO.customerQuery)(_.ID)
  def item = foreignKey("item_id", itemID, OrderingDAO.itemQuery)(_.ID)
}

/**
 * DAO の定義
 */
object OrderingDAO {

  /**
   * scala.slick.lifted.TableQuery オブジェクトを 3 つ作成
   */  
  lazy val customerQuery = CustomerDAO.customerQuery
  lazy val itemQuery = ItemDAO.itemQuery
  lazy val orderingQuery = TableQuery[OrderingTable]

  /**
   * 3 つテーブルを結合した上でキーワード検索
   * @param word
   */
  def search(word: String)(implicit s: Session): List[(Long, Long, Long, String, String, String, String, String, Long, String, Long, String, Long, String)] = {
    val query = for {
      ordering <- orderingQuery
      customer <- customerQuery.filter(_.ID === ordering.customerID)
      item <- itemQuery.filter(_.ID === ordering.itemID)
    } yield (ordering.ID, ordering.createdAt, customer.ID, customer.name, customer.email, customer.tel, customer.address, customer.comment, item.ID, item.name, item.price, item.comment, ordering.itemCount, ordering.comment)
    query.filter(row => ((row._4 like "%"+word+"%") || (row._5 like "%"+word+"%") || (row._6 like "%"+word+"%") || (row._7 like "%"+word+"%") || (row._8 like "%"+word+"%") || (row._10 like "%"+word+"%") || (row._12 like "%"+word+"%") || (row._14 like "%"+word+"%"))).list
  }

  /**
   * ID検索
   * @param ID
   */
  def searchByID(ID: Long)(implicit s: Session): Ordering = {
    orderingQuery.filter(_.ID === ID).first
  }

  /**
   * 作成
   * @param ordering
   */
  def create(ordering: Ordering)(implicit s: Session) {
    orderingQuery.insert(ordering)
  }

  /**
   * 更新
   * @param ordering
   */
  def update(ordering: Ordering)(implicit s: Session) {
    orderingQuery.filter(_.ID === ordering.ID).update(ordering)
  }

  /**
   * 削除
   * @param ordering
   */
  def remove(ordering: Ordering)(implicit s: Session) {
    orderingQuery.filter(_.ID === ordering.ID).delete
  }
}

```

基本的には、他のモデル層の実装と同じですので、特徴的な部分のみ説明していきます。

#### ① テーブルの定義

```scala
/**
 * テーブルの定義
 */
class OrderingTable(tag: Tag) extends Table[Ordering](tag, "orderings") {
  def ID = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def createdAt = column[Long]("created_at", O.NotNull)
  def customerID = column[Long]("customer_id", O.NotNull)
  def itemID = column[Long]("item_id", O.NotNull )
  def itemCount = column[Long]("item_count", O.NotNull)
  def comment = column[String]("commnet", O.NotNull)
  def * = (ID, createdAt, customerID, itemID, itemCount, comment) <> (Ordering.tupled, Ordering.unapply)
  def customer = foreignKey("customer_id", customerID, OrderingDAO.customerQuery)(_.ID)
  def item = foreignKey("item_id", itemID, OrderingDAO.itemQuery)(_.ID)
}
```

最後の 2 行で、外部キーの設定をしています。

外部キー制約は Table オブジェクトの foreignKey 関数を用いて定義します。

#### ② scala.slick.lifted.TableQuery オブジェクトを 3 つ作成

```scala
 ：
中略
 ：
/**
 * DAO の定義
 */
object OrderingDAO {

  /**
   * scala.slick.lifted.TableQuery オブジェクトを 3 つ作成
   */  
  lazy val customerQuery = CustomerDAO.customerQuery
  lazy val itemQuery = ItemDAO.itemQuery
  lazy val orderingQuery = TableQuery[OrderingTable]
 ：
中略
 ：
```

scala.slick.lifted.TableQuery オブジェクトを 3 つ作成しています。

こうすることで、複数のテーブルにまたがったクエリーを生成することが可能になります。


#### ③ 3 つテーブルを結合した上でキーワード検索

```scala
  /**
   * 3 つテーブルを結合した上でキーワード検索
   * @param word
   */
  def search(word: String)(implicit s: Session): List[(Long, Long, Long, String, String, String, String, String, Long, String, Long, String, Long, String)] = {
    val query = for {
      ordering <- orderingQuery
      customer <- customerQuery.filter(_.ID === ordering.customerID)
      item <- itemQuery.filter(_.ID === ordering.itemID)
    } yield (ordering.ID, ordering.createdAt, customer.ID, customer.name, customer.email, customer.tel, customer.address, customer.comment, item.ID, item.name, item.price, item.comment, ordering.itemCount, ordering.comment)
    query.filter(row => ((row._4 like "%"+word+"%") || (row._5 like "%"+word+"%") || (row._6 like "%"+word+"%") || (row._7 like "%"+word+"%") || (row._8 like "%"+word+"%") || (row._10 like "%"+word+"%") || (row._12 like "%"+word+"%") || (row._14 like "%"+word+"%"))).list
  }
 ：
中略
 ：
```

Slick では、Query に対し for 式を用いて flatMap 操作を行うことでテーブルの結合（Join）を行います。

結合の際に if 文を用いてフィルタリングを行うことで、内部結合（Inner Join）とする事ができます。

Slick における結合等の方法の詳細については、以下の公式ドキュメントを参照してください。

[http://slick.typesafe.com/doc/2.0.0/gettingstarted.html](http://slick.typesafe.com/doc/2.0.0/gettingstarted.html)

### 手順 4-3: ファイル `app/contrallers/OrderingController.scala` を作成します

```scala:app/contrallers/OrderingController.scala
package controllers

import play.api._
import play.api.mvc._

import play.api.data._
import play.api.data.Forms._

import play.api.db.slick._
import models._

object OrderingController extends Controller {

  /**
   * 受注情報入力フォームの定義
   */
  val orderingForm = Form(
    mapping(
      "ID" -> longNumber,
      "createdAt" -> longNumber,
      "customerID" ->  longNumber,
      "itemID" -> longNumber,
      "itemCount" -> longNumber,
      "comment" -> text(maxLength = 140)
    )(Ordering.apply)(Ordering.unapply)
  )

  /**
   * 受注登録フォーム表示アクションメソッドの定義
   */
  def showCreateForm() = Action { request =>
    Ok(views.html.orderingCreateForm(orderingForm))
  }

  /**
   * 受注登録アクションメソッドの定義
   */
  def create() = DBAction { implicit rs =>
    orderingForm.bindFromRequest.fold(
      errors => BadRequest(views.html.orderingCreateForm(errors)),
      ordering => {
        OrderingDAO.create(ordering)
        Redirect(routes.OrderingController.search())
      }
    )
  }

  /**
   * 受注情報検索アクションメソッドの定義
   */
  def search(word: String) = DBAction { implicit rs =>
    Ok(views.html.orderingSearch(word, OrderingDAO.search(word)))
  }

  /**
   * 受注情報削除アクションメソッドの定義
   */
  def remove(ID: Long) = DBAction { implicit rs =>
    OrderingDAO.remove(OrderingDAO.searchByID(ID))
    Redirect(routes.OrderingController.search())
  }
}

```

### 手順 4-4: ファイル `app/views/orderingCreateForm.scala.html` を作成します

```html:app/views/orderingCreateForm.scala.html
@(orderingForm: Form[Ordering])

@import helper._

@main(title = "受発注管理システム - 受注登録") {

<!-- ヘッダー -->
@header()

<div class="row">
  <div class="container">
  
    <!-- サイドメニュー -->
    <div class="hidden-xs col-sm-3 col-md-3 col-lg-3">
      <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <ul class="nav nav-pills nav-stacked">
            <li><a href="/orderings/search">受注情報一覧（検索/削除）</a></li>
            <li><a href="/orderings/create">受注登録</a></li>
          </ul>
        </div>
      </div>
    </div>
    
    <!-- コンテンツ -->
    <div class="col-xs-12 col-sm-9 col-md-9 col-lg-9">
      <div class="row">
      
        <!-- 見出し -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <h1>受注登録</h1>
        </div>
        
        <!-- フォーム -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          @form(routes.OrderingController.create()) {
            <fieldset>
              <input type="hidden" name="ID" value="0">
              <input type="hidden" name="createdAt" value="@System.currentTimeMillis()">
              @inputText(orderingForm("customerID"), '_label -> "顧客番号", 'size -> 30)
              @inputText(orderingForm("itemID"), '_label -> "商品番号", 'size -> 30)
              @select(
                orderingForm("itemCount"),
                options(
                  "1" -> "1",
                  "2" -> "2",
                  "3" -> "3",
                  "4" -> "4",
                  "5" -> "5",
                  "6" -> "6",
                  "7" -> "7",
                  "8" -> "8",
                  "9" -> "9",
                  "10" -> "10",
                  "11" -> "11",
                  "12" -> "12",
                  "13" -> "13",
                  "14" -> "14",
                  "15" -> "15",
                  "16" -> "16",
                  "17" -> "17",
                  "18" -> "18",
                  "19" -> "19",
                  "20" -> "20"
                ),
                '_label -> "商品個数"
              )
              @inputText(orderingForm("comment"), '_label -> "備考", 'size -> 30)
            </fieldset>
            <div class="actions">
              <input type="submit" class="btn btn-primary">
              <a href="/orderings/search" class="btn btn-danger">キャンセル</a>
            </div>
          }
        </div>
      </div>
    </div>
 </div>
}

```

### 手順 4-5: ファイル `app/views/orderingSearch.scala.html` を作成します

```html:app/views/orderingCreateForm.scala.html
@(word: String, orderings: List[(Long, Long, Long, String, String, String, String, String, Long, String, Long, String, Long, String)])

@import java.util.Date

@main(title = "受発注管理システム - 受注情報一覧") {

<!-- ヘッダー -->
@header()

<div class="row">
  <div class="container">

    <!-- サイドメニュー -->
    <div class="hidden-xs col-sm-3 col-md-3 col-lg-3">
      <div class="row">
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <ul class="nav nav-pills nav-stacked">
            <li><a href="/orderings/search">受注情報一覧（検索/削除）</a></li>
            <li><a href="/orderings/create">受注登録</a></li>
          </ul>
        </div>
      </div>
    </div>

    <!-- コンテンツ -->
    <div class="col-xs-12 col-sm-9 col-md-9 col-lg-9">
      <div class="row">

        <!-- 見出し -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <h1>受注情報一覧（検索/削除）</h1> 
        </div>

        <!-- 検索フォーム -->
        <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
        @helper.form(action=routes.OrderingController.search()) {
          <input type="search" name="word" value="@word">
          <input type="submit" value="キーワード検索" class="byn btn-primary">
        }
        </div>

        <!-- テーブル -->
        <div class="table-responsive col-xs-12 col-sm-12 col-md-12 col-lg-12">
          <table class="table table-bordered table-striped ">
            <thead>
              <tr>
                <th>受注番号</th>
                <th>受注日時</th>
                <th>顧客氏名</th>
                <th>メールアドレス</th>
                <th>商品名</th>
                <th>単価</th>
                <th>個数</th>
                <th>合計価格</th>
                <th>備考</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
            @orderings.map { ordering =>
              <tr>
                <td align="right">@("%9d".format(ordering._1))</td>
                <td align="right">@("%tY/%<tm/%<td %<tH:%<tM:%<tS".format(new Date(ordering._2)))</td>
                <td>@ordering._4</td>
                <td>@ordering._5</td>
                <td>@ordering._10</td>
                <td align="right">@("%,d".format(ordering._11))</td>
                <td align="right">@("%,d".format(ordering._13))</td>
                <td align="right">@("%,d".format((ordering._11 * ordering._13)))</td>
                <td>@ordering._14</td>
                <td align="center"><input type="button" class="btn btn-danger" value="削除" onClick='if(confirm("本当に削除してよろしいですか？")) {location.href="/orderings/@ordering._1/remove"}'></td>
              </tr>
            }
            </tbody>
          </table>
        </div>
      </div>
    </div>
 </div>
}

```

### 手順 4-6: ブラウザで、`http://localhost:9000/` にアクセスします

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180417.png" class="hatena-fotolife" itemprop="image"></span></p>

再びモデル層に変更を加えたため、テーブルスキーマの変更（DROP TABLE → CREATE TABLE）が必要になります。

`CREATE TABLE` 文に加えて、外部キー制約を与えるための `ALTER TABLE` 文も生成されていることがわかります。

### 手順 4-7: `Apply this script now!` ボタンをクリックします

「受注管理システム」 の完成です。

#### 受注登録

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180432.png" class="hatena-fotolife" itemprop="image"></span></p>

#### 受注情報一覧

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/k/kukita/20140806/20140806180448.png" class="hatena-fotolife" itemprop="image"></span></p>

---

作成した 「受注管理システム」 のソースコードは以下に保存していますので参考にしてください。

[https://github.com/kukita/play-scala-order-management-system](https://github.com/kukita/play-scala-order-management-system)

以上