Play ! Framework 
Introduction & Highlights 
Malti Yadav 
malti@knoldus.com
Quick Overview 
 Stateless framework 
 Integration of JSON 
 Full stack web framework 
 Compilation and Exact Error 
 Lots of build in feature for fast developement 
 Urls are entry poin to application 
 RESTful by default
Architecture
Setup play project
Standard Application Layout 
app → Application sources 
└ assets → Compiled asset sources 
└ stylesheets → Typically LESS CSS sources 
└ javascripts → Typically CoffeeScript sources 
└ controllers → Application controllers 
└ models → Application business layer 
└ views → Templates 
build.sbt → Application build script 
conf → Configurations files and other non-compiled resources (on classpath) 
└ application.conf → Main configuration file 
└ routes → Routes definition 
public → Public assets 
└ stylesheets → CSS files 
└ javascripts → Javascript files 
└ images → Image files 
project → sbt configuration files 
└ build.properties → Marker for sbt project 
└ plugins.sbt → sbt plugins including the declaration for Play itself 
lib → Unmanaged libraries dependencies 
logs → Standard logs folder 
└ application.log → Default log file 
target → Generated stuff 
└ scala-2.10.0 
└ cache 
└ classes → Compiled class files 
└ classes_managed → Managed class files (templates, ...) 
└ resource_managed → Managed resources (less, ...) 
└ src_managed → Generated sources (templates, ...) 
test → source folder for unit or functional tests
The conf/routes file 
# Routes 
# This file defines all application routes (Higher priority routes first) 
# ~~~~ 
# Home page 
GET / controllers.Application.index 
# Registration page 
GET /registration controllers.Application.registration 
# Login page 
GET /login controllers.Application.login 
# logout 
GET /logout controllers.Application.logout 
#save userdata 
POST /registration controllers.Application.createUser 
# loginAuthentication 
POST /login controllers.Application.loginAuthentication 
# Dashboard page 
GET /dashboard controllers.Application.dashboard 
# Map static resources from the /public folder to the /assets URL path 
GET /assets/*file controllers.Assets.at(path="/public", file)
The conf/application file 
 Configure database access 
 mongodb.servers=[“localhost:27017”] 
 mongodb.db=”test” 
 Logger 
 Router 
 Specific modules
Designing a domain models 
package models 
import java.util.Date 
object Domains { 
/* 
* Case class for UserForm 
*/ 
case class UserForm(name:String,email:String,password: 
(String,String),joinDate: Date) 
/* 
* Case class for LoginForm 
*/ 
case class LoginForm(email:String,password: String) 
}
Designing a QueryBuilder models 
package models 
import play.api._ 
import play.api.mvc._ 
import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 
import play.api.libs.concurrent.Execution.Implicits.defaultContext 
import scala.concurrent.Future 
import reactivemongo.api._ 
import play.modules.reactivemongo.MongoController 
import play.modules.reactivemongo.json.collection.JSONCollection 
object QueryBuilder extends Controller with MongoController { 
/* 
* Get a JSONCollection (a Collection implementation that is designed to work 
* with JsObject, Reads and Writes.) 
* Note that the `collection` is not a `val`, but a `def`. We do _not_ store 
* the collection reference to avoid potential problems in development with 
* Play hot-reloading. 
*/ 
def collection(collectionname: String): JSONCollection = 
db.collection[JSONCollection](collectionname)
Cont... 
def getDocumentsByQuery(collectionname: String, query: JsObject): 
Future[List[JsObject]] = { 
val cursor: Cursor[JsObject] = 
collection(collectionname).find(query).cursor[JsObject] 
val futureresult: Future[List[JsObject]] = cursor.collect[List]() 
futureresult 
} 
def insertDocuments(collectionname: String, documents: JsObject) { 
collection(collectionname).insert(documents) 
} 
}
Designing a helper class 
package utils 
import play.api.mvc.RequestHeader 
object Helper { 
/* 
* find URI 
* @param request 
*/ 
def findUri(request: RequestHeader): String = { 
request.uri 
}
Cont... 
/* 
* find session values 
* @param request 
* @param element - find session value stored in element 
*/ 
def findSession(request: RequestHeader, element: String): String = { 
request.session.get(element).map { sessionvalue => 
sessionvalue 
}.getOrElse { 
" " 
} 
}
Cont... 
/* 
* find flash values 
* @param request - HTTP request 
* @param element - find flash value stored in element 
*/ 
def findFlash(request: RequestHeader, element: String): String = { 
request.flash.get(element).getOrElse { 
" " 
} 
} 
}
Designing a constants class 
package utils 
object Constants { 
val USER_COLLECTION_NAME = "user" 
val MIN_LENGTH_OF_PASSWORD = 6 
val PASSWORD_NOT_MATCHED = "Password must be matched" 
val HOME_PAGE_TITLE = "Welcome to home page" 
val WRONG_LOGIN_DETAILS = "The email or password you entered is incorrect" 
val DASHBOARD_TITLE = "Welcome to dashboard" 
val ENTERED_EMAIL_EXISTS = "Entered email id already exist in database" 
val DEFAULT_PROFILE_IMAGE = "profile.jpeg" 
}
Calling business logic (Controller) 
package controllers 
import models.Domains._ 
import models.QueryBuilder._ 
import utils.Helper._ 
import utils.Constants._ 
import play.api._ 
import play.api.mvc._ 
import play.api.data.Form 
import play.api.data.Forms._ 
import play.api.libs.concurrent.Execution.Implicits.defaultContext 
import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 
import java.util.Date 
import scala.concurrent.Future 
import views.html 
object Application extends Controller {
Cont... 
/* 
* userForm 
*/ 
val userForm = Form( 
mapping( 
"name" -> nonEmptyText, "email" -> email, "password" -> tuple("main" -> 
nonEmptyText(MIN_LENGTH_OF_PASSWORD), "confirm" -> nonEmptyText). 
verifying(PASSWORD_NOT_MATCHED, passwords => passwords._1 == 
passwords._2), "joinDate" -> date("yyyy-MM-dd"))(UserForm.apply) 
(UserForm.unapply)) 
/* 
* Login Form 
*/ 
val loginForm = Form( 
mapping( 
"email" -> email, 
"password" -> nonEmptyText)(LoginForm.apply)(LoginForm.unapply))
Cont..... 
/* 
* Redirect Home Page 
*/ 
def index = Action { request => 
Ok(html.index(HOME_PAGE_TITLE, findUri(request))) 
} 
/* 
* Redirect Registration Page 
*/ 
def registration = Action { implicit request => 
val name = findFlash(request, "name") 
val email = findFlash(request, "email") 
if (email != " ") { 
val userForm = Application.userForm.fill(UserForm(name, email, (" ", " 
"), new Date)) 
} 
Ok(html.registration(userForm, findUri(request))) 
}
Cont... 
/* 
* Redirect Login Page 
*/ 
def login = Action { implicit request => 
val email = findFlash(request, "email") 
if (email != " ") { 
val loginForm = Application.loginForm.fill(LoginForm(email, " ")) 
} 
Ok(html.login(loginForm, findUri(request))) 
} 
/* 
* Logout and clean the session. 
*/ 
def logout = Action { implicit request => 
Redirect(routes.Application.login).withNewSession 
}
Cont... 
/* 
* Handle UserForm Submission 
*/ 
def createUser = Action.async { implicit request => 
userForm.bindFromRequest.fold( 
error => Future(BadRequest(html.registration(error, findUri(request)))), 
userForm => { 
val name = userForm.name 
val email = userForm.email 
val (password, _) = userForm.password 
val joinDate = userForm.joinDate 
val saveRecord = Json.obj("name" -> name, "email" -> email, "password" -> password, 
"joinDate" -> joinDate) 
val jsonuserdata = Json.obj("email" -> email) 
val futureUserdetails = getDocumentsByQuery(USER_COLLECTION_NAME, jsonuserdata) 
futureUserdetails.map { result => 
if (result.size > 0) { 
Redirect(routes.Application.registration).flashing( 
"message" -> ENTERED_EMAIL_EXISTS, "name" -> name, "email" -> email) 
} else { 
val futuresaveUser = insertDocuments(USER_COLLECTION_NAME, saveRecord) 
Redirect(routes.Application.dashboard).withSession( 
"email" -> email) 
} 
} 
}) 
}
Cont... 
/* 
* Login Authentication 
*/ 
def loginAuthentication = Action.async { implicit request => 
loginForm.bindFromRequest.fold( 
error => Future(BadRequest(html.login(error, findUri(request)))), 
loginForm => { 
val email = loginForm.email 
val password = loginForm.password 
val jsonuserdata = Json.obj("email" -> email, "password" -> password) 
val futureUserdetails = getDocumentsByQuery(USER_COLLECTION_NAME, 
jsonuserdata) 
futureUserdetails.map { result => 
if (result.size > 0) { 
Redirect(routes.Application.dashboard).withSession( 
"email" -> email) 
} else { 
Redirect(routes.Application.login).flashing( 
"message" -> WRONG_LOGIN_DETAILS, "email" -> email) 
} 
} 
}) 
}
Cont... 
/* 
* Redirect Dashboard 
*/ 
def dashboard = Action.async { implicit request => 
if (findSession(request, "email") == " ") { 
Future(Redirect(routes.Application.login)) 
} else { 
val jsonuserdata = Json.obj("email" -> findSession(request, "email")) 
val userfutureresult = getDocumentsByQuery(USER_COLLECTION_NAME, 
jsonuserdata) 
userfutureresult.map { result => 
Ok(html.dashboard(DASHBOARD_TITLE, findUri(request),result)) 
} 
} 
} 
}
The templating system 
(main.scala.html) 
@(title:String)(content:Html) 
<!DOCTYPE html> 
<html lang="en"> 
<head> 
<title>@title</title> 
<meta charset="utf-8"> 
<meta http-equiv="X-UA-Compatible" content="IE=edge"> 
<meta name="viewport" content="width=device-width, initial-scale=1"> 
<link rel="stylesheet" media="screen" 
href="@routes.Assets.at("Bootstrap/css/bootstrap.min.css")"> 
<link rel="stylesheet" media="screen" 
href="@routes.Assets.at("Bootstrap/css/bootstrap-theme.min.css")"> 
<link rel="stylesheet" media="screen" 
href="@routes.Assets.at("css/custom.css")"> 
<script src="@routes.Assets.at("jquery/jquery-1.11.1.min.js")" 
type="text/javascript"></script> 
<script src="@routes.Assets.at("Bootstrap/js/bootstrap.min.js")" 
type="text/javascript"></script> 
<script src="@routes.Assets.at("Bootstrap/js/bootstrap.js")" 
type="text/javascript"></script> 
</head> 
<body>@content 
</body> 
</html>
Cont... 
registration.scala.html 
@import models.Domains._ 
@(userForm:Form[UserForm],path:String)(implicit flash: Flash) 
@import helper._ 
@main("Welcome to Registration Page"){ 
<div class="container"> 
@header(path) 
<div class="jumbotron"> 
@form(routes.Application.createUser){ 
@registrationformElements(userForm) 
<div class="registration-error">@flash.get("message")</div> 
<input type="submit" name="submit" value="Submit" class="btn btn-primary"> 
} 
</div> 
@footer() 
</div> 
}
Cont... 
registrationformElements.scala.html 
@import models.Domains._ 
@(userForm: Form[UserForm]) 
@import helper._ 
@implicitField = @{ helper.FieldConstructor(BootstrapInput.f) } 
@inputText(userForm("name"),'_label -> "Name", '_showConstraints -> false,'class 
-> "form-control") 
@inputText(userForm("email"),'_label -> "Email", '_showConstraints -> 
false,'class -> "form-control") 
@inputPassword(userForm("password.main"),'_label -> "Password", '_help 
->"Password should be minimum 6 character",'class -> "form-control") 
@inputPassword(userForm("password.confirm"),'_error 
->userForm.error("password"), '_label -> "Confirm Password", '_showConstraints 
-> false,'class -> "form-control") 
@inputText(userForm("joinDate"),'_label -> "Joining Date", '_showConstraints -> 
false,'class -> "form-control")
Questions ?
THANK YOU

Intoduction on Playframework

  • 1.
    Play ! Framework Introduction & Highlights Malti Yadav malti@knoldus.com
  • 2.
    Quick Overview Stateless framework  Integration of JSON  Full stack web framework  Compilation and Exact Error  Lots of build in feature for fast developement  Urls are entry poin to application  RESTful by default
  • 3.
  • 4.
  • 5.
    Standard Application Layout app → Application sources └ assets → Compiled asset sources └ stylesheets → Typically LESS CSS sources └ javascripts → Typically CoffeeScript sources └ controllers → Application controllers └ models → Application business layer └ views → Templates build.sbt → Application build script conf → Configurations files and other non-compiled resources (on classpath) └ application.conf → Main configuration file └ routes → Routes definition public → Public assets └ stylesheets → CSS files └ javascripts → Javascript files └ images → Image files project → sbt configuration files └ build.properties → Marker for sbt project └ plugins.sbt → sbt plugins including the declaration for Play itself lib → Unmanaged libraries dependencies logs → Standard logs folder └ application.log → Default log file target → Generated stuff └ scala-2.10.0 └ cache └ classes → Compiled class files └ classes_managed → Managed class files (templates, ...) └ resource_managed → Managed resources (less, ...) └ src_managed → Generated sources (templates, ...) test → source folder for unit or functional tests
  • 6.
    The conf/routes file # Routes # This file defines all application routes (Higher priority routes first) # ~~~~ # Home page GET / controllers.Application.index # Registration page GET /registration controllers.Application.registration # Login page GET /login controllers.Application.login # logout GET /logout controllers.Application.logout #save userdata POST /registration controllers.Application.createUser # loginAuthentication POST /login controllers.Application.loginAuthentication # Dashboard page GET /dashboard controllers.Application.dashboard # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.at(path="/public", file)
  • 7.
    The conf/application file  Configure database access  mongodb.servers=[“localhost:27017”]  mongodb.db=”test”  Logger  Router  Specific modules
  • 8.
    Designing a domainmodels package models import java.util.Date object Domains { /* * Case class for UserForm */ case class UserForm(name:String,email:String,password: (String,String),joinDate: Date) /* * Case class for LoginForm */ case class LoginForm(email:String,password: String) }
  • 9.
    Designing a QueryBuildermodels package models import play.api._ import play.api.mvc._ import play.api.libs.functional.syntax._ import play.api.libs.json._ import play.api.libs.concurrent.Execution.Implicits.defaultContext import scala.concurrent.Future import reactivemongo.api._ import play.modules.reactivemongo.MongoController import play.modules.reactivemongo.json.collection.JSONCollection object QueryBuilder extends Controller with MongoController { /* * Get a JSONCollection (a Collection implementation that is designed to work * with JsObject, Reads and Writes.) * Note that the `collection` is not a `val`, but a `def`. We do _not_ store * the collection reference to avoid potential problems in development with * Play hot-reloading. */ def collection(collectionname: String): JSONCollection = db.collection[JSONCollection](collectionname)
  • 10.
    Cont... def getDocumentsByQuery(collectionname:String, query: JsObject): Future[List[JsObject]] = { val cursor: Cursor[JsObject] = collection(collectionname).find(query).cursor[JsObject] val futureresult: Future[List[JsObject]] = cursor.collect[List]() futureresult } def insertDocuments(collectionname: String, documents: JsObject) { collection(collectionname).insert(documents) } }
  • 11.
    Designing a helperclass package utils import play.api.mvc.RequestHeader object Helper { /* * find URI * @param request */ def findUri(request: RequestHeader): String = { request.uri }
  • 12.
    Cont... /* *find session values * @param request * @param element - find session value stored in element */ def findSession(request: RequestHeader, element: String): String = { request.session.get(element).map { sessionvalue => sessionvalue }.getOrElse { " " } }
  • 13.
    Cont... /* *find flash values * @param request - HTTP request * @param element - find flash value stored in element */ def findFlash(request: RequestHeader, element: String): String = { request.flash.get(element).getOrElse { " " } } }
  • 14.
    Designing a constantsclass package utils object Constants { val USER_COLLECTION_NAME = "user" val MIN_LENGTH_OF_PASSWORD = 6 val PASSWORD_NOT_MATCHED = "Password must be matched" val HOME_PAGE_TITLE = "Welcome to home page" val WRONG_LOGIN_DETAILS = "The email or password you entered is incorrect" val DASHBOARD_TITLE = "Welcome to dashboard" val ENTERED_EMAIL_EXISTS = "Entered email id already exist in database" val DEFAULT_PROFILE_IMAGE = "profile.jpeg" }
  • 15.
    Calling business logic(Controller) package controllers import models.Domains._ import models.QueryBuilder._ import utils.Helper._ import utils.Constants._ import play.api._ import play.api.mvc._ import play.api.data.Form import play.api.data.Forms._ import play.api.libs.concurrent.Execution.Implicits.defaultContext import play.api.libs.functional.syntax._ import play.api.libs.json._ import java.util.Date import scala.concurrent.Future import views.html object Application extends Controller {
  • 16.
    Cont... /* *userForm */ val userForm = Form( mapping( "name" -> nonEmptyText, "email" -> email, "password" -> tuple("main" -> nonEmptyText(MIN_LENGTH_OF_PASSWORD), "confirm" -> nonEmptyText). verifying(PASSWORD_NOT_MATCHED, passwords => passwords._1 == passwords._2), "joinDate" -> date("yyyy-MM-dd"))(UserForm.apply) (UserForm.unapply)) /* * Login Form */ val loginForm = Form( mapping( "email" -> email, "password" -> nonEmptyText)(LoginForm.apply)(LoginForm.unapply))
  • 17.
    Cont..... /* *Redirect Home Page */ def index = Action { request => Ok(html.index(HOME_PAGE_TITLE, findUri(request))) } /* * Redirect Registration Page */ def registration = Action { implicit request => val name = findFlash(request, "name") val email = findFlash(request, "email") if (email != " ") { val userForm = Application.userForm.fill(UserForm(name, email, (" ", " "), new Date)) } Ok(html.registration(userForm, findUri(request))) }
  • 18.
    Cont... /* *Redirect Login Page */ def login = Action { implicit request => val email = findFlash(request, "email") if (email != " ") { val loginForm = Application.loginForm.fill(LoginForm(email, " ")) } Ok(html.login(loginForm, findUri(request))) } /* * Logout and clean the session. */ def logout = Action { implicit request => Redirect(routes.Application.login).withNewSession }
  • 19.
    Cont... /* *Handle UserForm Submission */ def createUser = Action.async { implicit request => userForm.bindFromRequest.fold( error => Future(BadRequest(html.registration(error, findUri(request)))), userForm => { val name = userForm.name val email = userForm.email val (password, _) = userForm.password val joinDate = userForm.joinDate val saveRecord = Json.obj("name" -> name, "email" -> email, "password" -> password, "joinDate" -> joinDate) val jsonuserdata = Json.obj("email" -> email) val futureUserdetails = getDocumentsByQuery(USER_COLLECTION_NAME, jsonuserdata) futureUserdetails.map { result => if (result.size > 0) { Redirect(routes.Application.registration).flashing( "message" -> ENTERED_EMAIL_EXISTS, "name" -> name, "email" -> email) } else { val futuresaveUser = insertDocuments(USER_COLLECTION_NAME, saveRecord) Redirect(routes.Application.dashboard).withSession( "email" -> email) } } }) }
  • 20.
    Cont... /* *Login Authentication */ def loginAuthentication = Action.async { implicit request => loginForm.bindFromRequest.fold( error => Future(BadRequest(html.login(error, findUri(request)))), loginForm => { val email = loginForm.email val password = loginForm.password val jsonuserdata = Json.obj("email" -> email, "password" -> password) val futureUserdetails = getDocumentsByQuery(USER_COLLECTION_NAME, jsonuserdata) futureUserdetails.map { result => if (result.size > 0) { Redirect(routes.Application.dashboard).withSession( "email" -> email) } else { Redirect(routes.Application.login).flashing( "message" -> WRONG_LOGIN_DETAILS, "email" -> email) } } }) }
  • 21.
    Cont... /* *Redirect Dashboard */ def dashboard = Action.async { implicit request => if (findSession(request, "email") == " ") { Future(Redirect(routes.Application.login)) } else { val jsonuserdata = Json.obj("email" -> findSession(request, "email")) val userfutureresult = getDocumentsByQuery(USER_COLLECTION_NAME, jsonuserdata) userfutureresult.map { result => Ok(html.dashboard(DASHBOARD_TITLE, findUri(request),result)) } } } }
  • 22.
    The templating system (main.scala.html) @(title:String)(content:Html) <!DOCTYPE html> <html lang="en"> <head> <title>@title</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" media="screen" href="@routes.Assets.at("Bootstrap/css/bootstrap.min.css")"> <link rel="stylesheet" media="screen" href="@routes.Assets.at("Bootstrap/css/bootstrap-theme.min.css")"> <link rel="stylesheet" media="screen" href="@routes.Assets.at("css/custom.css")"> <script src="@routes.Assets.at("jquery/jquery-1.11.1.min.js")" type="text/javascript"></script> <script src="@routes.Assets.at("Bootstrap/js/bootstrap.min.js")" type="text/javascript"></script> <script src="@routes.Assets.at("Bootstrap/js/bootstrap.js")" type="text/javascript"></script> </head> <body>@content </body> </html>
  • 23.
    Cont... registration.scala.html @importmodels.Domains._ @(userForm:Form[UserForm],path:String)(implicit flash: Flash) @import helper._ @main("Welcome to Registration Page"){ <div class="container"> @header(path) <div class="jumbotron"> @form(routes.Application.createUser){ @registrationformElements(userForm) <div class="registration-error">@flash.get("message")</div> <input type="submit" name="submit" value="Submit" class="btn btn-primary"> } </div> @footer() </div> }
  • 24.
    Cont... registrationformElements.scala.html @importmodels.Domains._ @(userForm: Form[UserForm]) @import helper._ @implicitField = @{ helper.FieldConstructor(BootstrapInput.f) } @inputText(userForm("name"),'_label -> "Name", '_showConstraints -> false,'class -> "form-control") @inputText(userForm("email"),'_label -> "Email", '_showConstraints -> false,'class -> "form-control") @inputPassword(userForm("password.main"),'_label -> "Password", '_help ->"Password should be minimum 6 character",'class -> "form-control") @inputPassword(userForm("password.confirm"),'_error ->userForm.error("password"), '_label -> "Confirm Password", '_showConstraints -> false,'class -> "form-control") @inputText(userForm("joinDate"),'_label -> "Joining Date", '_showConstraints -> false,'class -> "form-control")
  • 25.
  • 26.