Malti Yadav
malti@knoldus.com
Play ! Framework
Introduction & Highlights
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

Intorduction of Playframework

  • 1.
    Malti Yadav malti@knoldus.com Play !Framework Introduction & Highlights
  • 2.
    Quick Overview  Stateless framework  Integrationof 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  Configuredatabase 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 sessionvalues * @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 flashvalues * @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 HomePage */ 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 LoginPage */ 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 UserFormSubmission */ 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 */ defloginAuthentication = 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 */ defdashboard = 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) <!DOCTYPEhtml> <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 @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> }
  • 24.
    Cont... registrationformElements.scala.html @import models.Domains._ @(userForm: Form[UserForm]) @importhelper._ @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.