13 марта состоялся релиз второй версии scala/java-фреймворка Play. На хабре уже был обзор новых фич Play 2.0. В этой же статье я хочу восполнить пробел в отсутствии мануалов на русском языке по этому интересному фреймворку на примере создания простого приложения на Java, состоящего из списка категорий и привязанных к ним вакансий.
Статья рассчитана на тех, кто совсем не знаком с Play и хотел бы его «пощупать», не тратя много времени.
Для работы с Play 2.0 вам понадобиться JDK 6 и выше. Для своей ubuntu 11.10 я поставил JDK 7.
Создаем проект
1. Скачиваем Play 2.0 и распаковываем туда, где у нас есть права и на запись и на чтение, у меня это /var/www/play20
.
2. Переходим в директорию, где вы держите сайты и выполняем скрипт:
/var/www/play20/play new jobs
Ответ на первый вопрос оставляем по умолчанию, на второй выбираем «2 — Create a simple Java application». Для Windows все аналогично, только вы выполняете не shell-скрипт, а .bat.
3. Переходим в созданный проект, входим в консоль play и запускаем проект:
cd jobs
/var/www/play20/play
run
4. Проверяем, что у нас получилось: http://localhost:9000.
5. Теперь для начала разработки на Play 2.0 осталось настроить IDE. Останавливаем сервер и возвращаемся в консоль командой CTRL+D и выполняем eclipsify или idea в зависимости от вашей среды разработки. Для netbeans автоматического создания проекта пока нет.
6. Открываем проект, я использую Eclipse: File > Import > General > Existing Projects into Workspace.
Список категорий
Создаем модель категории app/models/Category.java:
package models;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import play.data.validation.*;
import play.db.ebean.Model;
@Entity
public class Category extends Model {
@Id
public Long id;
@Constraints.Required
public String title;
public static Finder<Long,Category> find = new Finder (
Long.class, Category.class
);
public static List<Category> all() {
return find.all();
}
}
В шаблоне главной страницы views/index.scala.html выводим все категории:
@(categories: List30.09.2024НовостиПосле начала приёма предварительных заказов на новую «шестёрку» Mazda издание autohome.com.cn опубликовало подборку качественных фотографий экстерьера и салона нового авто.
)
@main("Jobs") {
<ul>
@for(category <- categories) {
<li>@category.title</li>
}
</ul>
}
Шаблоны в play 2.0 представляют собой html + блоки кода на Scala, которые начинаются с символа '@'. Также стоит обратить внимание, что шаблоны похожи на функции и имеют параметры, которые описываются в начале файла.
Передаем из контроллера приложения app/controllers/Application.java в отредактированный нами выше шаблон список категорий:
import models.Category;
...
public class Application extends Controller {
public static Result index() {
return ok(index.render( Category.all()));
}
}
Данные для начала будем хранить для простоты просто в памяти, для этого расскоментируем строчки в conf/application.conf:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
...
ebean.default="models.*"
Жмем F5 в браузере, нам предложат выполнить SQL-запрос. Кликаем на «Apply this script now!» и мы должны увидеть пустой список ul: у нас пока нет ни одной категории.
Добавление категорий
Добавляем в шаблон app/views/index.scala.html форму добавления категории:
@(categories: ListНовостиПосле начала приёма предварительных заказов на новую «шестёрку» Mazda издание autohome.com.cn опубликовало подборку качественных фотографий экстерьера и салона нового авто.
, categoryForm: FormНовостиПосле начала приёма предварительных заказов на новую «шестёрку» Mazda издание autohome.com.cn опубликовало подборку качественных фотографий экстерьера и салона нового авто.
)
@import helper._
@main("Jobs") {
<ul>
@for(category <- categories) {
<li>@category.title</li>
}
</ul>
@form(routes.Application.add()) {
@inputText(categoryForm("title"))
<input type="submit" value="Создать">
}
}
В шаблоне мы импользуем функции из helper._: функция form создает HTML-форму с заполненными полями action и method, функция inputText генерит текстовый input
Прописываем маршрут в conf/routes:
POST /add controllers.Application.add()
Добавляем в app/controllers/Application.java экшн, который будет создавать новую категорию используя данные, полученные из формы и обновим вызов рендеринга шаблона в экшене index, добавив второй параметр (форму):
import play.data.Form;
...
public static Result index() {
return ok(index.render(Category.all(), form(Category.class)));
}
public static Result add() {
Form<Category> filledForm = form(Category.class).bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(index.render(Category.find.all(), filledForm));
}
Category category = filledForm.get();
category.save();
return redirect(routes.Application.index());
}
Обновляем http://localhost:9000 и пробуем добавлять категории.
На этом месте у меня проект сломался. Чтобы все пересобрать выходим из консоли, вызываем /var/www/play20/play clean-all и заново запускаем.
Вакансии
Модель вакансий app/models/Job.java, связанная один-ко-многим с категориями:
package models;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import play.data.validation.*;
import play.db.ebean.Model;
import play.db.ebean.Model.Finder;
@Entity
public class Job extends Model {
@Id
public Long id;
@Constraints.Required
public String title;
@ManyToOne
public Category category;
public static Finder<Long,Job> find = new Finder(Long.class, Job.class);
}
Добавляем обратную связь в модель категорий app/models/Category.java:
import javax.persistence.OneToMany;
...
@OneToMany(mappedBy="category")
public List<Job> jobs;
Жмем F5, применяем новый SQL.
Теперь мы можем создать шаблон вывода категории views/show.scala.html со всеми ее вакансиями:
@(category: Category, jobForm: Form[Job])
@import helper._
@main("Jobs") {
<h1>@category.title</h1>
<h2>Вакансии:</h2>
<ul>
@for(job <- category.jobs) {
<li>@job.title</li>
}
</ul>
<h2>Добавить вакансию</h2>
@form(routes.Jobs.аdd(category.id)) {
@inputText(jobForm("title"))
<input type="submit" value="Создать">
}
}
Добавляем контроллер вакансий app/controllers/Jobs.java, позволяющий добавить новую вакансию, вывод вакансии оставим на потом:
package controllers;
import java.util.List;
import play.*;
import play.data.Form;
import play.mvc.*;
import views.html.*;
import models.*;
public class Jobs extends Controller {
public static Result add(Long categoryId) {
Form<Job> filledForm = form(Job.class).bindFromRequest();
if (filledForm.hasErrors()) {
Category category = Category.find.byId(categoryId);
return badRequest(show.render(category, filledForm));
}
Job job = filledForm.get();
job.category = Category.find.ref(categoryId);
job.save();
return redirect(routes.Categories.show(categoryId));
}
public static Result show(Long id) {
return TODO;
}
}
Рефакторим категории
Осталось реализовать страницу вывода категории и всех ее вакансий, заодно вынесем из Application-контроллера добавление категорий.
Создаем отдельный контроллер для категорий app/controllers/Categories.java:
package controllers;
import java.util.List;
import play.*;
import play.data.Form;
import play.mvc.*;
import views.html.*;
import models.*;
public class Categories extends Controller {
public static Result add() {
Form<Category> filledForm = form(Category.class).bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(index.render(Category.find.all(), filledForm));
}
Category category = filledForm.get();
category.save();
return redirect(routes.Application.index());
}
public static Result show(Long id) {
Category category = Category.find.byId(id);
return ok(show.render(category, form(Job.class)));
}
}
Не забываем добавить роуты добавления вакансии, вывода категории и меняем маршрут добавления новых категорий в conf/routes:
GET /category/:id controllers.Categories.show(id: Long)
POST /category/add controllers.Categories.add()
POST /job/add controllers.Jobs.add(categoryId: Long)
В шаблоне главной страницы views/index.scala.html меняем параметр функции форм, добавленяем ссылки на категории:
@(categories: ListНовостиПосле начала приёма предварительных заказов на новую «шестёрку» Mazda издание autohome.com.cn опубликовало подборку качественных фотографий экстерьера и салона нового авто.
, categoryForm: FormНовостиПосле начала приёма предварительных заказов на новую «шестёрку» Mazda издание autohome.com.cn опубликовало подборку качественных фотографий экстерьера и салона нового авто.
)
@import helper._
@main("Jobs") {
<ul>
@for(category <- categories) {
<li><a href="@routes.Categories.show(category.id)">@category.title</a></li>
}
</ul>
@form(routes.Categories.add()) {
@inputText(categoryForm("title"))
<input type="submit" value="Создать">
}
}
На этом шаге я еще раз пересобрал проект командой play clean-all.
Ресурсы для дальнейшего изучения Play 2.0:
- Официальная документация
- FAQ на stackoverflow.com
- Документация и пример проекта с использованием ebean
- Google group
Автор: faost