Play framework 2.0 のためのブログです。
Comments


Slick2.1にて、ManyToMany(多対多)を実現する方法例
今回の実装では、User *---* Project というリレーションを実現します。

val user = Users.findById(1).get
val projects = user.projects // SQL走る
val projects2 = user.projects // ここでは、キャッシュされた結果が返るためSQLは走らない

上記のように、アクセス可能で、初期アクセス時のみにSQLが発行されるような使用としています。

■Dao.scala
package models

import scala.slick.lifted.TableQuery

private[models] trait DAO {
  val Users = TableQuery[Users]
  val Projects = TableQuery[Projects]
  val ProjectUserMap = TableQuery[ProjectUserMap]
}

■User.scala
package models

import java.sql.Timestamp
import play.api.db.slick.Config.driver.simple._

// define dto
case class User(id: Long, name: String){
  // ##############################
  //  user.projects という形で、アクセス可能
  //  また、キャッシュされるため、2度めのアクセスの際にはSQLは走らない
  // ##############################
  val projectCache = collection.mutable.Map[Long, List[Product]]()
  def projects(implicit session: Session) = {
    projectCache.getOrElseUpdate(this.id, ProjectUserMap.projects(this.id))
  }
}

// define table schema
class Users(tag: Tag) extends Table[User](tag, "users") with DAO{
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name", O.NotNull)
  def * = (id, name) <> (User.tupled, User.unapply)
  def idx = index("idx_token", token ,unique = true)
}

object Users extends DAO {
  def findById(id: Long)(implicit s: Session): Option[User] = {
    Users.filter { _.id === id }.firstOption
  }

  def insert(user: User)(implicit s: Session) {
    Users += user
  }
}

■Project.scala
package models

import java.sql.Timestamp
import play.api.db.slick.Config.driver.simple._

// define dto
case class Project(id: Long, name: String, description: String){
  // ##############################
  //  project.users という形で、アクセス可能
  //  また、キャッシュされるため、2度めのアクセスの際にはSQLは走らない
  // ##############################
  val userCache = collection.mutable.Map[Long, List[Product]]()
  def users(implicit session: Session) = {
    userCache.getOrElseUpdate(this.id, ProjectUserMap.users(this.id))
  }
}

// define table schema
class Projects(tag: Tag) extends Table[Project](tag, "projects") with DAO{
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name", O.NotNull)
  def description = column[String]("description", O.NotNull)
  def * = (id, name, description) <> (Project.tupled, Project.unapply)
}

// define companion Object
object Projects extends DAO {
  def findById(id: Long)(implicit s: Session): Option[Project] = {
    Projects.filter { _.id === id }.firstOption
  }
}

■ProjectUser.scala
package models

import java.sql.Timestamp
import models.Projects._
import play.api.db.slick.Config.driver.simple._

// define dto
case class ProjectUser(user_id: Long, project_id: Long)

// define table schema
class ProjectUserMap(tag: Tag) extends Table[ProjectUser](tag, "projects_users") with DAO{
  def user_id = column[Long]("user_id")
  def project_id = column[Long]("project_id")
  def * = (user_id, project_id) <> (ProjectUser.tupled, ProjectUser.unapply)
  def userFK = foreignKey("user2projectFK", user_id, Users)(u => u.id)
  def projectFK = foreignKey("project2userFK", project_id, Projects)(p => p.id)
}


// define companion Object
object ProjectUserMap extends DAO {
  def projects(user_id: Long)(implicit s: Session) = {
    ProjectUserMap.filter { _.user_id === user_id }.flatMap(_.projectFK).list
  }
  def users(project_id: Long)(implicit s: Session) = {
    ProjectUserMap.filter { _.project_id === project_id }.flatMap(_.userFK).list
  }
}

↓以下、使い方
■Application.scala
package controllers

import models._
import play.api.mvc._
import play.api.db.slick._
import play.api.Play.current


object Application extends Controller{

  def index = DBAction { implicit rc =>
    val user = Users.findById(1).get
    val projects = user.projects // SQL走る
    val projects2 = user.projects // ここでは、キャッシュされた結果が返るためSQLは走らない
    Ok("test")
  }

}

取得する列の指定など、拡張は今回は盛り込んでません!
ご指摘、などあればコメントお願い致します!!
[...]

Categories: ,
Comments

認証のFilter処理で、TraitとActionでデータを渡す方法を実装
http://play-gf.blogspot.jp/2015/03/actionfilter.html の続編的記事です

■ AuthTrait.scala
package controllers.components.actions

import models._
import play.api.data.Form
import play.api.data.Forms._
import play.api.db.slick._
import play.api.mvc._
import play.api.Play.current
import scala.collection.mutable.HashMap

import scala.concurrent.Future

/**
 * Created by yusuke on 2015/01/23.
 */
trait AuthTrait extends Controller{

  class AuthRequest[A](val user: User, request: Request[A]) extends WrappedRequest[A](request)

  // ログインページヘのリダイレクト
  def redirectToLogin = Action{
    Redirect(controllers.routes.Secure.login())
  }

  case class Auth[A](action: Action[A]) extends Action[A] {

    case class AuthEntity(token: String)
    val auth_form = Form(mapping("token" -> text)(AuthEntity.apply)(AuthEntity.unapply))

    def apply(request: Request[A]): Future[Result] = {
      DB.withSession {implicit session =>
        val authEntity = auth_form.bindFromRequest()(request).get
        val userdata: Option[User] = Users.findByToken(authEntity.token)

        userdata match {
          case Some(u) =>action(new AuthRequest(u, request)) // ユーザーが見った場合、リクエストをラップして渡す
          case None => redirectToLogin(request.asInstanceOf[Request[play.api.mvc.AnyContent]]) // ユーザーが見つかっていない場合は、ログインページヘリダイレクト
        }
      }
    }
    lazy val parser = action.parser
  }

}

■ Application.Scala
package controllers

import controllers.components.actions.AuthTrait
import models._
import play.api.mvc._
import play.api.db.slick._
import play.api.Play.current


object Application extends Controller with AuthTrait{

  // 通常のActionを利用した場合
  def index = Auth {
    Action { implicit request =>
      val user = request.asInstanceOf[AuthRequest[AnyContent]].user
      Ok(user.toString)
    }
  }

  // SlickのDBActionを利用した場合
  def index2 = Auth {
    DBAction { implicit rc =>
      val user = rc.request.asInstanceOf[AuthRequest[AnyContent]].user
      Ok( user.toString )
    }
  }

}

こんな形で、Traitで定義したFilterから、アクションへパラメータを渡します。
[...]

Categories: , ,
Comments

以下、メソッドを関数オブジェクトに変換したり、カリーかしたりのメモ

object Test {
  def main(args: Array[String]){
 
 var add2 = add _ // メソッドを関数オブジェクトに変換
 var addCurried = add2.curried // 関数オブジェクトをカリー化
 val addCurried2 = (add _).curried // こんな風にまとめてもOK
 
    print(add(2,3))
    print(add2(2,3))
    print(addCurried(2)(3))
    print(addCurried2(2)(3))
  }

  def add(a:Int, b:Int) = a + b 
}

[...]

Categories:
Comments

ActionのFilter処理を実行するためのTips。

ドキュメントだと、ActionsComposition のこと↓
https://www.playframework.com/documentation/2.3.x/ScalaActionsComposition

今回、Filterの処理内容をTraitとして実装する方法としました。
また、DBアクセスにSlickを使用し、FilterからもDBアクセスできるような実装となっております。

AuthTrait (Filter実装部)
package controllers.components.actions

import models.Users
import play.api.db.slick._
import play.api.mvc.{Result, Request, Action}
import play.api.Play.current

import scala.concurrent.Future

/**
 * Created by yusuke on 2015/01/23.
 */
trait AuthTrait {

  case class Auth[A](action: Action[A]) extends Action[A] {
    def apply(request: Request[A]): Future[Result] = {
      DB.withSession {implicit session =>

        // DB.withSession 内 であれば、Slick経由でDBアクセス可能
        // ここでFilter処理を記載する
        // println(Users.findById(1));

      }

      action(request)
    }
    lazy val parser = action.parser
  }
}


Application (Controller)
package controllers

import controllers.components.actions.AuthTrait
import models.Users
import play.api.mvc._
import play.api.db.slick._
import play.api.Play.current

object Application extends Controller with AuthTrait{

  // 通常アクション
  def index = Action{
    Ok(views.html.index("Your new application is ready."))
  }

  // Authフィルターを使用 (中身はAuthTraitに実装)
  def test = Auth {
    // アクションでDBアクセスする場合はDBAction, しない場合はActionでもOK
    DBAction { implicit rc =>
      Ok(Users.findById(1).toString)
    }
  }

}

複数のFilterをかけたい場合は、同様の形でTraitを定義し、ActionにWrapする形で重ねがけすればOK。重ねる順番で、外側から順番にFilter処理が走ります。
以下の例だと、Filter1実行 → Filter2実行 → DBAction実行
def hoge = Filter1{
  Filter2{
    DBAction{
    }
  }
}
以上です。


 --------------以下追記---------------
ちなみに、POSTデータを取得するには、以下のようにする

① クラスを定義してバインドするパターン

trait AuthTrait {

  // フォームに対応するエンティティを定義
  case class AuthEntity(uid: String, name: String)
  val auth_form = Form(mapping("uid" -> text, "name" -> text)(AuthEntity.apply)(AuthEntity.unapply))

  case class Auth[A](action: Action[A]) extends Action[A] {
    def apply(request: Request[A]): Future[Result] = {
      DB.withSession {implicit session =>

        // リクエストをフォームにバインドして、値を取り出す
        println(auth_form.bindFromRequest()(request).get.name)
      
      }
      action(request)
    }
    lazy val parser = action.parser
  }
}
② バインドせずに、取り出すパターン
val postData: Map[String, Seq[String]] = request.body.asInstanceOf[AnyContentAsFormUrlEncoded].data
println(postData.get("uid").get(0))






[...]

Categories: , ,
Comments

基本的な使い方はドキュメントを見るのが早いです。

日本語ドキュメントもあります。

次回からは、自分の躓いたところや、Tipsみたいなのを紹介していきます。


[...]

Categories:
Comments

インストールが完了して、プログラムも動いてブラウザで見れるようになったので、あとはデータベース周りをセッティングします。

Play framework 2.0 では、デフォルトでH2 databaseを使用します。
H2 databaseには、ブラウザ上で使用出来る管理画面が用意されているので、それを利用するところまで解説をします。

まず、
conf/application.conf を開き、以下をコメントアウトします。
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.user=
db.default.password=

そして http://~:9000/ に一度アクセスすると、play が h2dbにアクセスし、ホームディレクトリに .h2.server.properties というファイルが作成されます (作成されなかったら、自分で以下の内容で作成しても問題無いです)



vim ~/.h2.server.properties
#H2 Server Properties
#Tue Oct 09 15:35:58 JST 2012
0=Generic JNDI Data Source|javax.naming.InitialContext|java\:comp/env/jdbc/Test|sa
1=Generic Firebird Server|org.firebirdsql.jdbc.FBDriver|jdbc\:firebirdsql\:localhost\:c\:/temp/firebird/test|sysdba
10=Generic Derby (Server)|org.apache.derby.jdbc.ClientDriver|jdbc\:derby\://localhost\:1527/test;create\=true|sa
11=Generic Derby (Embedded)|org.apache.derby.jdbc.EmbeddedDriver|jdbc\:derby\:test;create\=true|sa
12=Generic H2 (Server)|org.h2.Driver|jdbc\:h2\:tcp\://localhost/~/test|sa
13=Generic H2 (Embedded)|org.h2.Driver|jdbc\:h2\:~/test|sa
2=Generic SQLite|org.sqlite.JDBC|jdbc\:sqlite\:test|sa
3=Generic DB2|COM.ibm.db2.jdbc.net.DB2Driver|jdbc\:db2\://localhost/test|
4=Generic Oracle|oracle.jdbc.driver.OracleDriver|jdbc\:oracle\:thin\:@localhost\:1521\:XE|sa
5=Generic MS SQL Server 2000|com.microsoft.jdbc.sqlserver.SQLServerDriver|jdbc\:microsoft\:sqlserver\://localhost\:1433;DatabaseName\=sqlexpress|sa
6=Generic MS SQL Server 2005|com.microsoft.sqlserver.jdbc.SQLServerDriver|jdbc\:sqlserver\://localhost;DatabaseName\=test|sa
7=Generic PostgreSQL|org.postgresql.Driver|jdbc\:postgresql\:test|
8=Generic MySQL|com.mysql.jdbc.Driver|jdbc\:mysql\://localhost\:3306/test|
9=Generic HSQLDB|org.hsqldb.jdbcDriver|jdbc\:hsqldb\:test;hsqldb.default_table_type\=cached|sa
webAllowOthers=false
webPort=8082
webSSL=false

ここで注目してほしいのが、
webAllowOthers=false
webPort=8082
webSSL=false
の部分。

webAllowOthers を true にすれば、localhost以外のホストからもアクセス可能になります。あとはアクセスするためのポート番号の指定と、httpsを使用するか否かの設定です。

webAllowOthersをtrueにして、
../play
[アプリ名] $ h2-browser
Web Console server running at http://192.168.11.2:8082 (others can connect)
TCP server running at tcp://192.168.11.2:9092 (only local connections)
PG server running at pg://192.168.11.2:5435 (only local connections)

とすれば、設定したポートでブラウザで管理画面にアクセスできます。


webAllowOthers=trueにしておけば、あとは管理画面の[設定]の部分で、ポート番号などの設定をGUIでも行えます。

[...]

Categories:
Comments

今回インストールするバージョンは 2.0.4 です。

① http://www.playframework.org/ より、ソースコードをダウンロードして解凍
# wget http://download.playframework.org/releases/play-2.0.4.zip
# unzip play-2.0.4.zip
# mv play-2.0.4 PLAY_HOME

② playのディレクトリへ移動し、playコマンドでアプリケーション作成
# cd [PLAY_HOME]
# ./play new アプリ名

③ コンパイルと起動
# cd [PLAY_HOME]/アプリ名
# ../play run

これで、http://~~:9000/ にアクセスすればOKです。welcomeページが見れれば成功。


ちなみにwelcomeページはこんなの。

また、コンソールモードも存在する。

# ./play
           _                      _ 
 _ __ |  |   __ _ _  _  |  |
| '_   \|  |/   _'   |   |   |  |_|
|  __/ |_|\____|\__ (_)
|_|                     |__/ 
             
play! 2.0.4, http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[アプリ名] $ 



ここで、help play と打てば、使用出来るコマンド一覧がみれます。

help play


Welcome to Play 2.0!

These commands are available:
-----------------------------
classpath                  Display the project classpath.
clean                      Clean all generated files.
compile                    Compile the current application.
console                    Launch the interactive Scala console (use :quit to exit).
dependencies               Display the dependencies summary.
dist                       Construct standalone application package.
exit                       Exit the console.
h2-browser                 Launch the H2 Web browser.
license                    Display licensing informations.
package                    Package your application as a JAR.
play-version               Display the Play version.
publish                    Publish your application in a remote repository.
publish-local              Publish your application in the local repository.
reload                     Reload the current application build file.
run                  Run the current application in DEV mode.
test                       Run Junit tests and/or Specs from the command line
eclipsify                  generate eclipse project file
idea                       generate Intellij IDEA project file
sh         execute a shell command 
start                Start the current application in another JVM in PROD mode.
update                     Update application dependencies.

Type `help` to get the standard sbt help.

[...]

Categories: