playとSquerylでフォームの内容をDBに反映させるまで

前回はDBの操作をひと通り行ったので、今回は前々回で定義したdb.Postを使ってフォームの内容をDBに反映させます。
前々回の記事
play frameworkでSqueryl Schema及びentity定義するまで - hikonori07’s blog

これから簡易ブログシステムを作成します。
その簡易ブログシステムのページ構成はこのようになっています。
・ブログ一覧
→indexページ

・投稿ページ
→postページ

・記事の更新
→updateページ

今回は上の3つのうちの1つ、postページを作成していきます。

・フォームの内容をDBのpostテーブルに反映させる

なおplay2.3とSquerylを使って作成していきます。

最初にviewの設定

app/views/post.scala.htmlファイル

@(form: play.api.data.Form[db.Post])

//@helper.formでフォームを宣言
@helper.form(action = routes.Application.addPost) {
@helper.inputText(form("title"))
@helper.inputText(form("content"))
<input type="submit"/>

}

helperを使ってHTMLフォームを出力するのは↓のページを参考に
Play2.0(Scala)のForm HelperでHTMLフォームを作る | mwSoft

conf/routesの設定

//フォーム画面
GET      /post                        controllers.Application.post

//フォーム内容を送信したときのアクション先として
POST    /addPost                   controllers.Application.addPost

Getting Started with Play 2, Scala, and Squeryl

フォーム内容のアクション先として指定しているaddPostを作成

app/controllers/Application.scalaファイル

 def addPost = Action { implicit request =>
    val result = postForm.bindFromRequest.fold(
      errors => { BadRequest },
      post => { 
//       inTransaction(AppDB.postTable.insert(new Post(post.title, post.content)))
//上の記述をのせていましたが、下の記述でも大丈夫なようです
        inTransaction(AppDB.postTable.insert(post))
        Redirect(routes.Application.index())
      }
    )
    result
  }

リクエストを受け取り参照する方法や、暗黙的な引数とする方法が↓で説明されています。
https://www.playframework.com/documentation/2.3.x/ScalaActions


bindFromRequestの使い方とimplicitしている理由
→「Controllerロジックの実装」に説明あり
http://www.colibrifw.org/chapter03/section05/databaseapp.html

また今回error時の処理も行う必要があるためbindFromRequest.foldを使用しています。
error時にはBadRequestを投げ、正常時にはinsert文を実行してリダイレクト処理を行っています。
またerrorsやpostは束縛され変数としてその後の処理で参照できるため、分かりやすい変数名をしっかりつけるようにしたほうがいいでしょう。
最初そのことを分からず、errorとsuccessにしていました。。。

フォーム入力画面の作成

indexページにはpostの一覧を表示する予定なので今回は、controllerにpostアクションを追加。
app/controllers/Application.scalaファイル

//フォームの定義
  val postForm = Form(
    mapping(
      "title" -> text,
      "content" -> text
    )(Post.apply)(Post.unapply)
  )

//postFormを渡す
  def post = Action {
    Ok(views.html.post(postForm))
  }


ひと通りできたところで、Loggerでフォームで渡された内容(request.body)を表示
request.bodyにはこんな内容が飛んできていました。

AnyContentAsFormUrlEncoded(Map(title -> ArrayBuffer(hello Rails), content -> ArrayBuffer(play with friends)))


参考ページ
Getting Started with Play 2, Scala, and Squeryl

Squerylで簡単なDBの操作をやってみた

今回はDBの操作(Select,Insert,Update,Delete)をSquerylを使って行ったのでメモ。

まず今回はDBの操作だけを目的としているため、フォームなどは使わずにあるページにアクセスしたらDBにある操作が実行されるという形式で行いました。
例えば、localhost:9000/indexというページを表示するとpostテーブルにinsert文が実行されるといった感じです。

また参考にしたページは↓です。
Select - Squeryl - A Scala ORM for SQL Databases
Insert, Update and Delete - Squeryl - A Scala ORM for SQL Databases

SQL文を実行する前に↓をimportする

import models._
import org.squeryl.PrimitiveTypeMode._

ではまだデータがない状態なのでInsertから。

  transaction {
    schema_name.insert(new model_name(column1, column2))
  }

これでDBに値が入っていたらOK

あとここで記述されているtransactionは一般的なトランザクションとほぼ同義です。
→transactionはブロック内でトランザクションを開始し、ブロックが終了するとコミットする。(ブロック内でもし例外が発生するとロールバックする)
引用:SquerylのSessionとTransaction - Develop with pleasure!


次はUpdateです。

//update
  transaction {
    update(schema_name)(p =>
      where(p.id === 1)
      set(p.column1:= "Hello Squeryl !!")
  }

SQLがわかれば、大体なにをやっているかはわかりますよね。

今度はDeleteを見ていきます。
今回はidは決め打ちで。

//delete
  transaction {
      schema_name.deleteWhere(sch => sch.id === 1)
  }

最後にSelectですがちょっと引っ掛け!?
自分だけなんでしょうか。

//select
  transaction {
      from(schema)(s => where(sch.id === 1) select(sch))
  }

これだけではselect文は実行されません。(定義したのみの状態)
参考にしていたページにはそれ以上のことは書いていない(探せないだけ?)。
select文を実行するためには、toListやheadOptionをつけてあげたらいいとのこと。

こんな感じ↓

//select
  transaction {
    val sql = from(schema)(s => where(sch.id === 1) select(sch)).headOption
    //ちゃんと実行できているかlogに出力
    Logger.debug(sql.toString)
  }

これでできるようになりました。
ログにもちゃんと出力されるようになりました↓

[info] play - database [default] connected at jdbc:postgresql://~
[info] play - Application started (Dev)
[debug] application - List(Post(hello world,今日は快晴です!!))

*ログの出力方法については別途記事作成します。

以上Squerylを使った簡単なDB操作にでした。

追記

transactionとinTransactionの違い

・transactionはブロック内でトランザクションを開始し、ブロックが終了するとコミットする。
トランザクションを実行する際は、例え既存のトランザクションコンテキスト内で呼ばれたとしても、いつも新規にトランザクションが生成される。

・inTransaction
inTransactionは実行中のトランザクションが無い場合に新規にトランザクションを生成する。既にトランザクションが存在する場合はそのトランザクションコンテキストが利用される。

この両者の使い分けって妨げられたくない処理がある時は、transactionを使えってことでいいのかな!?

Active Recordパターン

Active RecordっぽくDBの操作もできるようなので、その方法も書いてみます。
尚今回の使うpostのスキーマ定義は↓のようになっています。

package db {

import org.squeryl.{Schema, KeyedEntity}

    case class Post(
        title: String,
        content: String) extends KeyedEntity[Long] {
            val id: Long = 0
    }

    object AppDB extends Schema {
      val postTable = table[Post]("post")
    }
}
//schemaオブジェクトのコンテンツをimport
//dbパッケージのimportではないことに注意(汗)
  import AppDB._

  transaction {
    new Post("Hello Scala", "scala scala").save
  }

updateも試しましたが苦労しました。
最初にDBからupdateしたいフィールドをとってくる必要があるって当たり前のことに気づかず。。。
updateはこんな感じで行います。

  import AppDB._

    transaction {
//selectでテーブルから情報取ってきてる
//今回は該当idがあることが前提なのでgetメソッド使用
      val helloScala = from(AppDB.postTable)(p => 
         where(p.id === 12) select(p)).headOption.get
//copyメソッドでタイトルだけ違うインスタンス生成
      val updatePost = helloScala.copy(title = "Hello !! Scala !!")
//update処理
      updatePost.update

ちなみに上ではgetメソッドを使用していますが、値が確実に返ってくるか分からない場合は下のようにmapを使います。

//idがあるかどうか分からない場合はmapを使って書いた方が良い
      from(AppDB.postTable)(p => 
        where(p.id === 12) select(p)).headOption.map{ post => 
          val newPost = post.copy(title = "Hello !! Scala !!")
          newPost.update
        }.getOrElse(/*Noneの場合の処理*/)

getメソッドを使った場合だと、値がNoneの場合errorになります。
↑の場合だとNoneのときはgetOrElseのあとの処理が返されます。

また下のリンクページにも書いているのですが、Entityに部分はこのように変更しないとupdateできません。
変更前だとidが常に0になるためです。insertの場合はそのままでOKなのですがupdateの場合はid=0では正常に動作しません。当たり前ですが。。。該当idをちゃんと渡せるようにするためには下のように変更してください。

 //変更前
    case class Post(
      title: String,
      content: String) extends KeyedEntity[Long] {
        val id: Long = 0

    }

//変更後
    case class Post(
      id: Long,
      title: String,
      content: String) extends KeyedEntity[Long] {
    }

Play2.3でCRUDを使った一覧ページの作成してみる - hikonori07’s blog


もっと複雑なsql文を書きたいという人は最初にも説明しましたが、
Select - Squeryl - A Scala ORM for SQL Databases
Insert, Update and Delete - Squeryl - A Scala ORM for SQL Databases
これらのページを参考ください。当たり前ですがselectはさらに色んなバリエーションがありますね。


Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

Guide to ScalaーScalaプログラミング入門

Guide to ScalaーScalaプログラミング入門

Scala逆引きレシピ (PROGRAMMER’S RECiPE)

Scala逆引きレシピ (PROGRAMMER’S RECiPE)

play frameworkでSqueryl Schema及びentity定義するまで

今回は前回の続き、Squeryl Schema及びentity定義をやっていこうと思います。
playとsqueryで簡単ブログ機能を作ってみます。
*DBには予めpostテーブルが作成されています。
こんな感じ。
id | integer | not null default nextval('s_post_id'::regclass)
title | character varying(30) |
content | text |

下のリンクページを見ながら作業をすすめていきます。
Getting Started with Play 2, Scala, and Squeryl

entityって?

entityと言われてどういうことか分からなかったので調べてみました。

エンティティとは 〔 実体 〕 【 entity 】 - 意味/解説/説明/定義 : IT用語辞典
→具体的にどういったデータがエンティティとして扱われるかは分野によって異なるため、注意が必要である。
うむ、これじゃ細かいとこ分かりません。。。

[SQL] 16. データベースの設計 1 | TECHSCORE(テックスコア)
→実世界に存在するものの中で、データベースとして表現すべき対象物をエンティティ (entity:実体) と呼びます。

playの標準設定システムを使ったsquerylのDB接続

大体理解できたところで先ほどのページへ、
conf/application.confの設定は前回終わったので、
app/Global.scalaファイルの作成を始めます。

import org.squeryl.adapters.{H2Adapter, PostgreSqlAdapter}
import org.squeryl.internals.DatabaseAdapter
import org.squeryl.{Session, SessionFactory}
import play.api.db.DB
import play.api.GlobalSettings

import play.api.Application

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    SessionFactory.concreteFactory = app.configuration.getString("db.default.driver") match {
      case Some("org.h2.Driver") => Some(() => getSession(new H2Adapter, app))
      case Some("org.postgresql.Driver") => Some(() => getSession(new PostgreSqlAdapter, app))
      case _ => sys.error("Database driver must be either org.h2.Driver or org.postgresql.Driver")
    }
  }

  def getSession(adapter:DatabaseAdapter, app: Application) = Session.create(DB.getConnection()(app), adapter)

}

上のファイルを追加したら、activator runした状態で
http://localhost:9000/にアクセスします。
コマンドに下のような表示がされればDB接続が上手くいったということになります。

[info] play - database [default] connected at jdbc:postgresql://ip_address/db_name

entityの作成

ここまできてようやくentityの作成です。

package db {

import org.squeryl.{Schema, KeyedEntity}

    case class Post(
        title: String,
        content: String) extends KeyedEntity[Long] {
            val id: Long = 0
    }

    object AppDB extends Schema {
      val postTable = table[Post]("post")
    }
}

Twiwt:Blog / jugyo : Squeryl の使い方 - セットアップ, モデルの定義, テーブル作成
→KeyedEntity[Long] を extends しているのがポイントで、こうすると Long 型の id という名前のフィールドをプライマリキーとして扱うようになります。

上のように書いていたので、今回も同様idというLong型のフィールドを定義しています。


モデルは定義するだけではダメで、スキーマに登録する必要があります。
以下ではその処理を行っています。

    object AppDB extends Schema {
      val postTable = table[Post]("post")
    }

ここまできたらDBにアクセスする準備は整いました。
次回は実際にDBを操作する方法いわゆるCRUDについて書いていきます。

Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

Guide to ScalaーScalaプログラミング入門

Guide to ScalaーScalaプログラミング入門

playとSquerylの設定

playとSquerylを使う機会があったのでメモ。
ちなみにplayはplay2.3です。

play側のDBアクセス設定
ScalaDatabase
my_project/conf/application.confに↓の設定を行う

# Database configuration
 db.default.driver=org.postgresql.Driver
 db.default.url="jdbc:postgresql://ip_address/db_name"
 db.default.user=user_name
 db.default.password=pass_word

今回はORMにSquerylを使います。
Introduction - Squeryl - A Scala ORM for SQL Databases

ではこのページを参考に作業を進めていきます。
Getting Started - Squeryl - A Scala ORM for SQL Databases

sbtファイルの設定。

今回はDBはpostgreSQL、ORMにSquerylを使用するので、それぞれをSBTファイルに書き込みます。
built.sbtと書かれたファイルに↓のように記述

libraryDependencies ++= Seq(
  jdbc,
  "org.squeryl" %% "squeryl" % "0.9.5-7",
  "postgresql" % "postgresql" % "9.1-901.jdbc4"
)

ちなみに上の各ライブラリのバージョンは、squerylの場合「squeryl sbt」みたいにググればでてきました。
Maven Repository: org.squeryl » squeryl_2.10 » 0.9.5-6


compileコマンドでerrorが出なかったらひとまずOK

[my-project] $ compile

*私はjdbcの記述を抜かしていたことでerrorが出ていました。
JDBCって今回初めて知り。。。(汗)
JDBC - Wikipedia

次の記事ではスキーマの定義をしていきます。

参考ページ
https://www.playframework.com/documentation/ja/2.2.x/NewApplication

Scalaでメソッドのオーバーロード

今回はメソッドオーバーロードについてメモ。

メソッドオーバーロードとは?
オーバーロードとは同じメソッド名で異なる個数の引数や異なる型の引数をとるメソッド複数定義することです。

上の説明だけでは分かりにくい部分もあるのでさっそくコードで見てみます。

scala> class Person(name:String, age:Int){
     | def hello() = println("Hello " + name)
     | def hello(dialogue:String) = println("Hello " + name + "," + dialogue )
     | def olderTha(n:Int):Boolean = { age > n }
     | }
defined class Person

scala> val tanaka = new Person("Tanaka", 28)
tanaka: Person = Person@1a93a7ca

scala> tanaka.hello
Hello Tanaka

scala> tanaka.hello("Nice to meet you!")
Hello Tanaka,Nice to meet you!

Personクラスではhelloメソッドが2つ定義されていますが、呼び出し側の引数の与え方によって、それぞれ違うhelloメソッドが呼び出されていることが確認できます。

ちょっと気になったので調べてみました。


Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

Guide to ScalaーScalaプログラミング入門

Guide to ScalaーScalaプログラミング入門

ScalaのOption型について

Scalaにはオプションの値のためのOption型がある。
今回はこのOption型についてブログに書く。

なぜnullではなくOptionなのか?

他の言語ではnullを使うことが多いけど、Scalaではnullは使わない。使うことはできるけど暗黙の了解というかOptionを使うことが推奨されている。
このおかげでnullチェックの煩わしさが減る。Option型の変数として示すことでnullになる可能性のあるということをコードの読み手に伝えることができる。

Optionとは?

Optionとは値があるかどうかわからない状態を表すための型です。
Optionのサブクラスには値があるときのSome(value)と、値がないことを表すNoneがあります。
Optionは値がnull以外の時あるときはSome(value)、値がない場合nullの場合はNoneを返します。

Optionの値を取り出す方法

Optionの値を取り出す例を見てみます。

scala> val something : Option[String] = Option("test")
something: Option[String] = Some(test)

//Optionのgetメソッド
//getメソッドは値があるわかっているときに使う(逆に確かではない時はgetOrElseメソッドなどを使う)
scala> val result = something.get
result: String = test

//OptionのisEmptyメソッド
scala> val result = something.isEmpty
result: Boolean = false

//Noneのとき
scala> val none : Option[String] = None
none: Option[String] = None

scala> none.get
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:322)
  at scala.None$.get(Option.scala:320)
  ... 32 elided

//getOrElseメソッドでnullの場合を指定することも可能
scala> none.getOrElse("Null")
res3: String = Null

Mapのgetメソッドで引数のキーに対応する値あるかどうか下のように確認できます。

scala> val fruitsColor = Map("apple" -> "red", "banana" -> "yellow", "mango" -> "orange")
fruitsColor: scala.collection.immutable.Map[String,String] = Map(apple -> red, banana -> yellow, mango -> orange)

//Mapのgetメソッド
scala> fruitsColor get "apple"
res1: Option[String] = Some(red)

scala> fruitsColor get "melon"
res2: Option[String] = None


OptionをパターンマッチでSomeまたはNoneにマッチさせることで値を取り出すことも可能です。

//パターンマッチでOptionの値を取得する
scala> def something(x: Option[String]) = x match {
     | case Some(value) => value
     | case None => "empty"
     | }
something: (x: Option[String])String

scala> something(Option("test"))
res1: String = test

scala> something(None)
res2: String = empty

Option確かに便利です。
他の言語でもこれ使えたらいいのに!

追記
Optionのforeachメソッドとmapメソッドも便利ということで調べてみました。

//Optionは基本値があるかないかの時に使う(同じ属性の値が複数の時はListを使用)
scala> val myPhone = Option("iPhone5S")
myPhone: Option[String] = Some(iPhone5S)

//Optionのforeachメソッド
scala> myPhone.foreach(println)
iPhone5S

//Noneの場合はなにもしません。
scala> None.foreach(println)
//mapメソッド
scala> myPhone.map { x => x.size }
res1: Option[Int] = Some(8)

scala> myPhone.map { x => x.length }
res2: Option[Int] = Some(8)

scala> myPhone.map { x => x.toString }
res3: Option[String] = Some(iPhone5S)

scala> None.map { x => x.toString }
res4: Option[String] = None

参考記事
ScalaのOptionステキさについてアツく語ってみる - ( ꒪⌓꒪) ゆるよろ日記


Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

Guide to ScalaーScalaプログラミング入門

Guide to ScalaーScalaプログラミング入門

scalaのパターンマッチについて

今回はscalaのパターンマッチについてメモ
ケースクラスとパターンマッチの組み合わせはだいぶ強力なことができるということで、ちゃんと覚えておくためのブログに残しておきます。

パターンマッチで使うmatch式はこんな感じになります。

<セレクター式> match { <選択肢> }

・match式のマッチング方法
match式では上から順番にマッチするかどうかがチェックされ、どれか1つの条件にマッチした場合、それ以降の条件に対するマッチングは行われません。

パターンマッチのパターンはいくつかありますが、その中からいくつかをまとめてみました。
1,定数パターン
2,変数パターン
3,ワイルドカードパターン
4,コンストラクタパターン
5,型付きパターン

・定数パターン
定数パターンは自分自身としかマッチしません。
ようは決め打ちの値。定数としては任意のリテラルを使えます。(string,boolean,intなど)

scala> def const(x: Any) = x match {
     | case 1 => "number"
     | case true => "true"
     | case "constant" => "String"
     | case Nil => "empty"
     | }
const: (x: Any)String

scala> const(1)
res0: String = number

scala> const("constant")
res1: String = String

scala> const(100)
res2: String = something

・変数パターン

scala> def expr(x: Any) = x match {
     | case 0 => "ZERO"
     | case something => "not ZERO:" + something
     | }
expr: (x: Any)String

//マッチした変数を参照することも可能
scala> expr("hello")
res1: String = not ZERO:hello

コンストラクタパターン
前回のケースクラスについての記事で、ケースクラスに対してもパターンマッチができると書きました。下がその例です。

scala> case class Iphone(color:String, price:Int)
defined class Iphone

scala> def caseClassMatch(p: Iphone) = p match {
     | case Iphone( _, 900)=>{
     | println("price is $900")
     | }
     | case Iphone( "green", _)=>{
     | println("Color is green")
     | }
     | }
caseClassMatch: (p: Iphone)Unit

scala> val myIphone = Iphone("red", 900)
myIphone: Iphone = Iphone(red,900)

scala> caseClassMatch(myIphone)
price is $900

ケースクラスのコンストラクタに対してパターンマッチングされていることが確認できると思います。

*ケースクラスのコンストラクタの引数にさらにケースクラスがある場合も、match式でネストされたケースクラスにもパターンマッチが可能ということ。

型でマッチさせることも可能です。
5,型付きパターン

scala> def patternMatch(n:Any) = n match {
     | case n:Int => println("int")
     | case n:String => println("string")
     | case n:Boolean => println("boolean")
     | }
patternMatch: (n: Any)Unit

scala> patternMatch(1)
int

scala> patternMatch("Good")
string

以上、他の言語よりも柔軟な使い方ができるscalaのパターンマッチでした。


追記

変数を束縛(bind)する方法

scala> case class Iphone(color: String, price: Int)
defined class Iphone

scala> def caseClassMatch(p: Iphone) = p match {
     |   case Iphone(_, 900) => println("900 yen")
     |   case Iphone("green", _) => println("green!")
     |   case Iphone(color, _) => println("color is " + color)
     | }
caseClassMatch: (p: Iphone)Unit

scala> caseClassMatch(Iphone("red", 300))
color is red

変数を束縛するという表現がピンとこなかったけど、
ワイルドカードパターンと比べてみたら理解できたので書いてみる。

ワイルドカードのパターン
任意の条件にマッチした場合、予め決めていた処理を行う。

・変数を束縛する変数パターン
ワイルドカードと同じように任意の条件にマッチする。ここまで一緒。
違うのはこれからで、パターン変数xに値が束縛されて参照が可能になります。
上の式だとcolorに値が束縛され、参照できるようになっています。

パターンがマッチしなかった場合どうなるか?

上の例でどのパターンにもマッチしなかった場合どうなるか?という指摘があったため追記です。
結論から言うとerrorになります。下記参照

scala> case class Iphone(color:String, price:Int)
defined class Iphone

scala> def caseClassMatch(p: Iphone) = p match {
     | case Iphone( _, 900)=>{
     | println("price is $900")
     | }
     | case Iphone( "green", _)=>{
     | println("Color is green")
     | }
     | }
caseClassMatch: (p: Iphone)Unit

scala> val myPhone = Iphone("red", 100)
myPhone: Iphone = Iphone(red,100)

scala> caseClassMatch(myPhone)
scala.MatchError: Iphone(red,100) (of class Iphone)
  at .caseClassMatch(<console>:9)
  ... 32 elided