This is the model we wish to finalise:
These are the new classes:
package models;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import play.db.jpa.Model;
@Entity
public class Division extends Model
{
public String name;
@OneToMany(cascade=CascadeType.ALL)
public List<Club> members = new ArrayList<Club>();
public Division(String name)
{
this.name = name;
}
public void addClub(Club club)
{
members.add(club);
}
public String toString()
{
return name;
}
public static Division findByName(String name)
{
return find("name", name).first();
}
}
package models;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import play.db.jpa.Model;
@Entity
public class Sponsor extends Model
{
public String name;
@ManyToMany (mappedBy="sponsors")
public List<Club> support = new ArrayList<Club>();;
public Sponsor(String name)
{
this.name = name;
}
public void addSuport(Club club)
{
support.add(club);
}
public String toString()
{
return name;
}
}
//...
@ManyToMany
public List<Sponsor> sponsors = new ArrayList<Sponsor>();
//...
public void addSponsor(Sponsor company)
{
sponsors.add(company);
}
We can significantly simplify aspects of the testing if we invest some time in setting up test objects in yaml first.
Open 'test/data.yaml' and replace its contents with the following:
Club(dunmore):
name: dunmore
Club(tramore):
name: tramore
Club(fenor):
name: fenor
Player(jim):
name: jim
club: dunmore
Player(mary):
name: mary
club: dunmore
Player(sam):
name: sam
club: tramore
Player(john):
name: john
club: tramore
Player(mike):
name: mike
club: fenor
Player(linda):
name: linda
club: fenor
Division(senior):
name: senior
members:
- tramore
- dunmore
Division(junior):
name: junior
members:
- fenor
Sponsor(newsagent):
name: newsagent
Sponsor(pub):
name: pub
Now bring in a new test class that will exercise this model:
import org.junit.*;
import java.util.*;
import play.Logger;
import play.test.*;
import models.*;
public class ComprehensiveTest extends UnitTest
{
@BeforeClass
public static void loadDB()
{
Fixtures.deleteAllModels();
}
public static void loadSponsorships()
{
Club tramore = Club.find("byName", "tramore").first();
Club dunmore = Club.find("byName", "dunmore").first();
Sponsor newsagent = Sponsor.find("byName", "newsagent").first();
tramore.addSponsor(newsagent);
dunmore.addSponsor(newsagent);
newsagent.addSuport(tramore);
newsagent.addSuport(dunmore);
tramore.save();
dunmore.save();
newsagent.save();
}
@Before
public void setup()
{
Fixtures.loadModels("data.yml");
loadSponsorships();
}
@After
public void teardown()
{
Fixtures.deleteAllModels();
}
@Test
public void testPlayerClubLong()
{
Player jim;
Club dunmore;
jim = Player.find("byName", "jim").first();
assertNotNull(jim);
assertEquals(jim.name, "jim");
dunmore = jim.club;
assertEquals("dunmore", dunmore.name);
dunmore = Club.find("byName", "dunmore").first();
assertNotNull(dunmore);
assertEquals("dunmore", dunmore.name);
assertEquals(2, dunmore.players.size());
Player p1 = dunmore.players.get(0);
assertTrue (p1.name.equals("jim") || p1.name.equals("mary"));
Player p2 = dunmore.players.get(1);
assertTrue (p2.name.equals("jim") || p2.name.equals("mary"));
}
@Test
public void testDivisionClubLong()
{
Division senior = Division.find("byName", "senior").first();
assertNotNull(senior);
assertEquals(2, senior.members.size());
Club c1 = senior.members.get(0);
Club c2 = senior.members.get(1);
assertTrue (c1.name.equals("tramore") || c1.name.equals("dunmore"));
assertTrue (c2.name.equals("tramore") || c2.name.equals("dunmore"));
}
//----------------------------------------------------------------------
@Test
public void testPlayerClub()
{
Club dunmore = Club.find("byName", "dunmore").first();
Player jim = Player.find("byName", "jim").first();
Player mary = Player.find("byName", "mary").first();
assertNotNull(mary);
assertTrue (dunmore.players.contains(jim));
assertTrue (dunmore.players.contains(mary));
}
@Test
public void testDivisionClub()
{
Division senior = Division.find("byName", "senior").first();
Club dunmore = Club.find("byName", "dunmore").first();
Club tramore = Club.find("byName", "tramore").first();
assertTrue (senior.members.contains(dunmore));
assertTrue (senior.members.contains(tramore));
}
@Test
public void testClubSponsorShort()
{
Sponsor newsagent = Sponsor.find("byName", "newsagent").first();
Club dunmore = Club.find("byName", "dunmore").first();
Club tramore = Club.find("byName", "tramore").first();
assertTrue(newsagent.support.contains(dunmore));
assertTrue(newsagent.support.contains(tramore));
assertTrue(dunmore.sponsors.contains(newsagent));
assertTrue(tramore.sponsors.contains(newsagent));
}
@Test
public void testEditPlayerClub()
{
Club dunmore = Club.find("byName", "dunmore").first();
Player jim = Player.find("byName", "jim").first();
Player mary = Player.find("byName", "mary").first();
dunmore.players.remove(mary);
mary.delete();
dunmore.save();
assertEquals (dunmore.players.size(), 1);
assertTrue (dunmore.players.contains(jim));
assertEquals(0, Player.find("byName", "mary").fetch().size());
Player sara = new Player("sara");
dunmore.addPlayer(sara);
dunmore.save();
assertEquals (dunmore.players.size(), 2);
}
@Test
public void testEditClubSponsor()
{
Sponsor newsagent = Sponsor.find("byName", "newsagent").first();
Club dunmore = Club.find("byName", "dunmore").first();
assertEquals(2, newsagent.support.size());
newsagent.support.remove(dunmore);
dunmore.sponsors.remove(newsagent);
newsagent.save();
dunmore.save();
assertEquals(1, newsagent.support.size());
}
}
Run the app in test mode, and run this new test suite. All of these tests should run.
Incorporate all of these new artifacts:
package controllers;
import java.util.List;
import models.Player;
import play.mvc.Controller;
public class Players extends Controller
{
public static void index()
{
List<Player> players = Player.findAll();
render (players);
}
}
package controllers;
import java.util.List;
import models.Club;
import play.mvc.Controller;
public class Clubs extends Controller
{
public static void index()
{
List<Club> clubs = Club.findAll();
render (clubs);
}
}
package controllers;
import java.util.List;
import models.Sponsor;
import play.mvc.Controller;
public class Sponsors extends Controller
{
public static void index()
{
List<Sponsor> sponsors = Sponsor.findAll();
render (sponsors);
}
}
package controllers;
import java.util.List;
import models.Division;
import play.mvc.Controller;
public class Division extends Controller
{
public static void index()
{
List<Division> divisions = Division.findAll();
render (divisions);
}
}
You will also need the corresponding views - each one called index.html
:
#{extends 'main.html' /}
#{set title:'Players' /}
<nav>
<a class="item" href="/divisions"> Divisions </a>
<a class="item" href="/clubs"> Clubs </a>
<a class="active item" href="/players"> Players </a>
<a class="item" href="/sponsors"> Sponsors </a>
</nav>
<section>
<h1>Players</h1>
<table>
<thead>
<tr>
<th>Player</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
#{list items:players, as:'player'}
<tr>
<td>${player.name}</td>
<td></td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
#{extends 'main.html' /}
#{set title:'Clubs' /}
<nav>
<a class="item" href="/divisions"> Divisions </a>
<a class="active item" href="/clubs"> Clubs </a>
<a class="item" href="/players"> Players </a>
<a class="item" href="/sponsors"> Sponsors </a>
</nav>
<section>
<h1>Clubs</h1>
<table>
<thead>
<tr>
<th>Club</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
#{list items:clubs, as:'club'}
<tr>
<td>${club.name}</td>
<td></td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
#{extends 'main.html' /}
#{set title:'Sponsors'/}
<nav>
<a class="item" href="/divisions"> Divisions </a>
<a class="item" href="/clubs"> Clubs </a>
<a class="item" href="/players"> Players </a>
<a class="active item" href="/sponsors"> Sponsors </a>
</nav>
<section>
<h1>Sponsors</h1>
<table>
<thead>
<tr>
<th>Sponsor</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
#{list items:sponsors, as:'sponsor'}
<tr>
<td>${sponsor.name}</td>
<td></td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
#{extends 'main.html' /}
#{set title:'Divisions' /}
<nav>
<a class="active item" href="/divisions"> Divisions </a>
<a class="item" href="/clubs"> Clubs </a>
<a class="item" href="/players"> Players </a>
<a class="item" href="/sponsors"> Sponsors </a>
</nav>
<section>
<h1>Divisions</h1>
<table>
<thead>
<tr>
<th>Division</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
#{list items:divisions, as:'division'}
<tr>
<td>${division.name}</td>
<td></td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
Now incorporate the following routes:
# Home page
GET / Divisions.index
GET /divisions Divisions.index
GET /clubs Clubs.index
GET /players Players.index
GET /sponsors Sponsors.index
Save everything and run (not in test mode). Browse to
It views will be largely bank and unformatted.
There is a data.yaml file in your test folder. Copy this to the conf folder (there will now me two files of this name).
Create a class called 'BootStrap.java' in you 'app' folder, and replace its content with this source here:
import java.util.List;
import play.*;
import play.jobs.*;
import play.test.*;
import models.*;
@OnApplicationStart
public class Bootstrap extends Job
{
public void doJob()
{
Fixtures.deleteDatabase();
Fixtures.loadModels("data.yml");
}
}
Run the app again, and you should start to see some data.
Currently UI for the model if read only - and we can only see each model in isolation (we cant see relationships).
First, we tackle the relationships. Update the <table>
element of the following views with this version:
<section>
<h1>Players</h1>
<table>
<thead>
<tr>
<th> Player</th>
<th> Club </th>
<th> </th>
</tr>
</thead>
<tbody>
#{list items:players, as:'player'}
<tr>
<td>${player.name}</td>
<td>${player.club.name}</td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
<section>
<h1>Clubs</h1>
<table>
<thead>
<tr>
<th>Club</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#{list items:clubs, as:'club'}
<tr>
<td>${club.name}</td>
<td>
<table>
<tr>
#{list items:club.players, as:'player'}
<td>${player.name}</td> </tr>
#{/list}
</tr>
</table>
</td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
<section>
<h1>Divisions</h1>
<table>
<thead>
<tr>
<th>Division</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
#{list items:divisions, as:'division'}
<tr>
<td>${division.name}</td>
<td>
<table class="table">
<tr>
#{list items:division.members, as:'club'}
<td>${club.name}</td> </tr>
#{/list}
</tr>
</table>
</td>
<td></td>
</tr>
#{/list}
</tbody>
</table>
</section>
See if you can make sense of the above templates. Save everything and reload. You should be able to see what players belong to which clubs, and also what divisions the clubs are in.
Introduce the following method into the controllers/Players:
public static void delete(Long id)
{
Player player = Player.findById(id);
if (player != null)
{
if (player.club != null)
{
player.club.removePlayer(player);
player.club.save();
}
player.delete();
}
index();
}
Which requires this method in models/Club.java
public void removePlayer(Player player)
{
players.remove(player);
}
Now change the loop in the 'views/Players/index.html' file:
#{list items:players, as:'player'}
<tr>
<td>${player.name}</td>
<td>${player.club.name}</td>
<td> <a href="/players/delete/${player.id}">Delete</a> </td>
<td></td>
</tr>
#{/list}
(just one line changed in the above).
Run the app - players now have a delete button, when you press it, you get an error. This is because we need to establish a route for the delete link. Introduce this in to conf/routes now:
GET /players/delete/{id} Players.delete
And try again. You should be able to delete players.
Try to incorporate the latest version of the semantic ui css framework into your application, and restyle all of the views to take advantage of the framework. This is to include making the current <nav>
work as expected.
We have a 'delete' link for players, introduce a similar delete link for all of the other model elements.
On the player view, introduce a new button at the end of the view (not in the menu) called 'New Player'. This should present a simple form to register a new player and add to the database.
Challenge: How will you allow a player to be associated with a club?