In this article, I will show you how to build your first Play Framework web application. We will be building a CRUD app (Create, Read, Update and Delete) together with Play framework (2.x). This guide was first used at the BT Developer Conference (BT DevCon 2015) on 27 November 2015.
Play is based on a lightweight, stateless, web-friendly architecture. Built on Akka, Play provides predictable and minimal resource consumption (CPU, memory, threads) for highly-scalable applications. Play makes it easy to build web applications with Java & Scala based on the MVC model. The Play framework is used by many well-known organisations in the world such as LinkedIn, GOV.UK, The guardian and Ocado. You can read more about the Play Framework on their website here.
Play Framework introduction video (Java version)
What I like about Play is that:
Okay enough of the introduction, let's get our hands dirty and do some coding. This is what we are here for :)
In this session, I will be using:
How to install the play framework: https://www.playframework.com/documentation/2.0/Installing
Please note Play 2.1.5 does not have the activator feature, hence the console command will be slightly different from the video above.
IDE: Play supports both Eclipse and IntelliJ (I will be using Eclipse for the demo) https://www.playframework.com/documentation/2.0/IDE
Since it is getting close to the Christmas holiday season, let's build a database to record the actors/actresses in a pantomime.
Create a new play project
play new pantomime <enter>
<enter>
Press "2" (for java project)
Go to the project folder
cd pantomime
Link it with IDE (I will be using Eclipse here)
play eclipse
Start Eclipse
Import the project
File -> Import -> General -> Existing projects into Workspace
Browse and select the project folder e.g. pantomime
Click finish to import
app.controllers (location for your controllers)
app.views (location for your view templates)
Written in scala / HTML
test (location for your test modules, a few examples have been provided)
conf (
application.conf
routes
public (to store your assets)
stylesheets, images, javascripts etc...
To run it at port 8080
play “~run 8080”
Test it
Open a browser and go to http://localhost:8080/
If it is working, you will see some Play help webpages.
What we are going to do next is to add a model, then add a view template and some controller functions. We are going to do the view and controller in multiple parts.
So let's start by adding the “Actor” model.
It needs to be placed in a package called models.
To create the java file, right click on app, select New, Click on Other... select java class
package: models
Name: Actor
Click finish
In Actor.java, import these libraries
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import play.data.validation.Constraints;
import play.db.ebean.*;
import java.util.*;
Actor needs to “extends Model”
Add @Entity on top of the class
@Entity
public class Actor extends Model {
}
Add data model code
@Id
@GeneratedValue
public Long id;
@Constraints.Required
public String firstname;
@Constraints.Required
public String lastname;
public Integer age;
public String role;
Need to add default constructor to make it work properly.
public Actor() {
}
Add code to help us debug
public String toString()
{
return "id: " + id + " firstname:" + firstname + " lastname:" + lastname + " age:" + age + " role:" + role;
}
Enable Queries
// -- Queries
public static Model.Finder<Long,Actor> find = new Model.Finder(Long.class, Actor.class);
// Find by id:
public static Actor findById( Long _id )
{
return find.where()
.eq( "id", _id )
.findUnique();
}
// Find all:
public static List<Actor> findAll() {
return find.all();
}
See no need to write a single line of SQL so far!
Let's create an HTML form so that we can add actors
right click on views, select New, click on file
enter filename: addactorform.scala.html
In the file, at the top, we add the input the parameters. Let's add the following
@(theform: Form[models.Actor])(message: String)
Let's take a look at main.scala.html. It has most of the ingredients we need for constructing an HTML document, so let's reuse that by calling the main function.
@main("Add Actor"){
<p>Hello World</p>
<p>@message</p>
}
Let's add a controller function to show the form and a controller function to process and add an Actor (create).
Go to controllers.Application and import these libraries:
import models.*;
import static play.data.Form.form;
import play.*;
import play.data.Form;
import play.mvc.*;
import views.html.*;
For showing the form, add:
public static Result showAddActorForm() {
Form<Actor> theform = form(Actor.class).bindFromRequest();
return ok( addactorform.render(theform,"") );
}
For processing the form POST, add:
public static Result addActor() {
return ok("add actor");
}
Now go to conf -> routes
Add:
GET /addactor controllers.Application.showAddActorForm()
POST /addactor controllers.Application.addActor()
Before we move to the second part, let's do a test
Goto: http://localhost:8080/addactor
Go back the addactorform.scala.html, let's add a function to help us create Bootstrap style input fields
@myInputField(fieldname: String, label: String = "", placeholder: String = "" ) = {
<div class="form-group @if(theform(fieldname).hasErrors){ has-error}">
<label class="col-sm-2 control-label" for="@fieldname">@label</label>
<div class="col-sm-6">
<input class="form-control" type="text" name="@fieldname" id="@fieldname" placeholder="@placeholder" value="@theform(fieldname).value">
@if(theform(fieldname).hasErrors) {
<span class="help-inline">@theform(fieldname).errors.head.message</span>
}
</div>
</div>
}
Add some form code using the play framework helper:
<h1>Add Actor</h1>
@helper.form(action = routes.Application.addActor, 'id -> "addactorform", 'class -> "form-horizontal") {
@myInputField("firstname","Firstname","Enter firstname")
@myInputField("lastname","Lastname","Enter lastname")
@myInputField("age","Age","Enter age")
@myInputField("role","Role","Enter role")
<div class="form-group">
<label class="col-sm-2 control-label"></label>
<div class="col-sm-6">
<button class="btn" type="submit">Submit</button>
</div>
</div>
}
Let's do another test
Goto: http://localhost:8080/addactor
How come there is an error “error.required” on the page? Because the form was not initialised properly. So let's go back to the controller and make the following modification:
return ok( addactorform.render(theform.fill(new Actor()),"") );
Let's test it again. The error is gone! Great! Let's test the form post... Any suggestions of names?
Next, let's put more code in the controller.
Let's add some more code to process the POST part:
public static Result addActor() {
Form<Actor> theform = form(Actor.class).bindFromRequest();
if(theform.hasErrors()) {
Logger.error("Error: " + theform.errorsAsJson() );
return badRequest( addactorform.render(theform, "Error: " + theform.errorsAsJson() ) );
}
else
{
Actor actor = theform.get();
actor.save();
Logger.debug( actor.toString() );
return ok( addactorform.render(theform,"success") );
}
}
Let's do another test.
Error!!!!! [PersistenceException: The default EbeanServer has not been defined?]
What's going on? We didn't enable the ORM properly yet. We need to do that. So let's check the application.conf file
Unmark:
ebean.default="models.*"
Enable the in memory database by unmarking the db config lines too:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.user=sa
db.default.password=""
Now let's try it again. It is asking whether we want to create the table actor. As you can see, Play is helping us to create the required table. We don't need to type in a single line of SQL. Let's click on “Apply this script now!” to continue.
Let's upgrade the current view template so that it can show the list of entries.
Add the following parameter in the template, so that we can pass the data from the controller to the template.
@(theform: Form[models.Actor])(thelist: List[models.Actor])(message: String)
List it out, add the following code in the body after the add actor code:
<h1>The list</h1>
<ul>
@for( actor <- thelist ){
<li>@actor.toString()</li>
}
</ul>
Add the additional input parameter to the controller functions (in 3 places):
, Actor.findAll()
Let's add some code in the controller to perform the delete function.
public static Result deleteActor(Long _id) {
Form<Actor> theform = form(Actor.class).bindFromRequest();
Actor actor = Actor.findById(_id);
if ( actor == null)
{
return badRequest( addactorform.render(theform.fill(new Actor()), Actor.findAll(), "Actor not found" ) );
}
else
{
actor.delete();
return ok( addactorform.render(theform.fill(new Actor()), Actor.findAll(), "Actor deleted" ) );
}
}
In the routes file add:
GET /deleteactor controllers.Application.deleteActor( id: Long )
In the template: add the link after @actor.toString():
<li>@actor.toString() <a href="@routes.Application.deleteActor( actor.id )">Delete</a></li>
Now let's test it!
Let's add some code in the controller to perform the edit function.
public static Result editActor(Long _id) {
Form<Actor> theform = form(Actor.class).bindFromRequest();
Actor actor = Actor.findById(_id);
if ( actor == null)
{
return badRequest( addactorform.render(theform.fill(new Actor()), Actor.findAll(), "Actor not found" ) );
}
else
{
return ok( addactorform.render(theform.fill(actor), Actor.findAll(), "" ) );
}
}
In the routes file, add:
GET /editactor controllers.Application.editActor( id: Long )
In the template file, after @actor.toString(), add:
<li>@actor.toString() <a href="@routes.Application.editActor( actor.id )">Edit</a> <a href="@routes.Application.deleteActor( actor.id )">Delete</a></li>
Now let's test it again. How come it is making duplicate? Because the controller can't tell whether it is in create mode or edit mode. Let's add a few tweaks to make it work:
In the view, inside the helper form, add the following. This will help us to distinguish between a new object and an old object:
<input type="hidden" name="id" value="@theform("id").value">
In the controller, addActor, replace/add the following code code:
Actor actor = theform.get();
if ( actor.id == null )
{
// it's a new object
actor.save();
Logger.debug( actor.toString() );
return ok( addactorform.render(theform, Actor.findAll(), "success") );
}
else
{
// it's an old object
actor.update();
Logger.debug( actor.toString() );
return ok( addactorform.render(theform, Actor.findAll(), "updated") );
}
How come the form looks a bit poorly styled? Let's enable the Bootstrap styling.
Copy the css, javascript and font files in the appropriate folders in <project folder>/public/
In the main.scala.html file, locate the header and add:
<link rel="stylesheet" href="@routes.Assets.at("stylesheets/bootstrap.min.css")" type="text/css">
Currently we are using Play in memory database. Certainly we can change the config to point to a SQL database such as MySQL.
Do a clean up first:
play clean-all
In the application.conf
# Link to my SQL database
db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://localhost:3306/pantomimedb?characterEncoding=UTF-8"
db.default.user="username"
db.default.pass="password"
In Build.scala, add the following line in the middle to import the mysql connector library. Don't forget to add a comma at the end of the previous item.
"mysql" % "mysql-connector-java" % "5.1.18"
Great! We have created a CRUD webapp in this session. I hope this gives you an idea of how easy it is to create a webapp with Play. I hope you like it and will continue using it!
Have fun!
I will upload a copy of the project code in the next few days.
In the meantime, enjoy coding. If you have any questions or comments, please feel free to contact me here or leave a comment below
Having a business website is an essential part of running a successful business in this information age. Most businesses, no matter how big or small, have a website in one form or another. You can spend little or you can spend a fortune on it. You can use off-the-shelf tools or build one from scratch. You can make one yourself or you can get someone to do it for you. However, whichever option you take does not guarantee that you get an effective business website. So what makes a website good or just a complete waste of time and money?