13 марта состоялся релиз второй версии scala/java-фреймворка Play. На хабре уже был обзор новых фич Play 2.0. В этой же статье я хочу восполнить пробел в отсутствии мануалов на русском языке по этому интересному фреймворку на примере создания простого приложения на Java, состоящего из списка категорий и привязанных к ним вакансий.
Статья рассчитана на тех, кто совсем не знаком с Play и хотел бы его «пощупать», не тратя много времени.
Для работы с Play 2.0 вам понадобиться JDK 6 и выше. Для своей ubuntu 11.10 я поставил JDK 7.
1. Скачиваем Play 2.0 и распаковываем туда, где у нас есть права и на запись и на чтение, у меня это
2. Переходим в директорию, где вы держите сайты и выполняем скрипт:
Ответ на первый вопрос оставляем по умолчанию, на второй выбираем «2 — Create a simple Java application». Для Windows все аналогично, только вы выполняете не shell-скрипт, а .bat.
3. Переходим в созданный проект, входим в консоль play и запускаем проект:
4. Проверяем, что у нас получилось: http://localhost:9000. Для запуска проекта на другом порте используем «run номер_порта» (кавычки обязательны).
5. Теперь для начала разработки на Play 2.0 осталось настроить IDE. Останавливаем сервер и возвращаемся в консоль командой CTRL+D и выполняем eclipsify или idea в зависимости от вашей среды разработки. Для netbeans автоматического создания проекта пока нет.
Чтобы подключить сорсы (удобно смотреть через open declaration исходники) и javadoc выполняем «eclipsify with-source=true» (не забываем про кавычки).
6. Открываем проект, я использую Eclipse: File > Import > General > Existing Projects into Workspace.
Создаем модель категории app/models/Category.java:
В шаблоне главной страницы views/index.scala.html выводим все категории:
Шаблоны в play 2.0 представляют собой html + блоки кода на Scala, которые начинаются с символа '@'. Также стоит обратить внимание, что шаблоны похожи на функции и имеют параметры, которые описываются в начале файла.
Чтобы в эклипсе работало автодополнение для вновь созданных шаблонов проект надо запускать через тильду: "~run", также в настройках Windows — Preferences — General/Workspace убрать галочку «Build automatically», поставить галочки «Refresh using native hooks or polling», «Refresh on access».
Передаем из контроллера приложения app/controllers/Application.java в отредактированный нами выше шаблон список категорий:
Данные для начала будем хранить для простоты просто в памяти, для этого расскоментируем строчки в conf/application.conf:
Жмем F5 в браузере, нам предложат выполнить SQL-запрос. Кликаем на «Apply this script now!» и мы должны увидеть пустой список ul: у нас пока нет ни одной категории.
Добавляем в шаблон app/views/index.scala.html форму добавления категории:
В шаблоне мы импользуем функции из helper._: функция form создает HTML-форму с заполненными полями action и method, функция inputText генерит текстовый input.
Прописываем маршрут в conf/routes:
Добавляем в app/controllers/Application.java экшн, который будет создавать новую категорию используя данные, полученные из формы и обновим вызов рендеринга шаблона в экшене index, добавив второй параметр (форму):
Обновляем http://localhost:9000 и пробуем добавлять категории.
На этом месте у меня проект сломался. Чтобы все пересобрать выходим из консоли, вызываем /var/www/play20/play clean-all и заново запускаем.
Модель вакансий app/models/Job.java, связанная один-ко-многим с категориями:
Добавляем обратную связь в модель категорий app/models/Category.java:
Жмем F5, применяем новый SQL.
Теперь мы можем создать шаблон вывода категории views/show.scala.html со всеми ее вакансиями:
Добавляем контроллер вакансий app/controllers/Jobs.java, позволяющий добавить новую вакансию, вывод вакансии оставим на потом:
Осталось реализовать страницу вывода категории и всех ее вакансий, заодно вынесем из Application-контроллера добавление категорий.
Создаем отдельный контроллер для категорий app/controllers/Categories.java:
Не забываем добавить роуты добавления вакансии, вывода категории и меняем маршрут добавления новых категорий в conf/routes:
В шаблоне главной страницы views/index.scala.html меняем параметр функции форм, добавленяем ссылки на категории:
На этом шаге я еще раз пересобрал проект командой play clean-all.
Ресурсы для дальнейшего изучения Play 2.0:
Статья рассчитана на тех, кто совсем не знаком с 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. Для запуска проекта на другом порте используем «run номер_порта» (кавычки обязательны).
5. Теперь для начала разработки на Play 2.0 осталось настроить IDE. Останавливаем сервер и возвращаемся в консоль командой CTRL+D и выполняем eclipsify или idea в зависимости от вашей среды разработки. Для netbeans автоматического создания проекта пока нет.
Чтобы подключить сорсы (удобно смотреть через open declaration исходники) и javadoc выполняем «eclipsify with-source=true» (не забываем про кавычки).
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: List[Category]) @main("Jobs") { <ul> @for(category <- categories) { <li>@category.title</li> } </ul> }
Шаблоны в play 2.0 представляют собой html + блоки кода на Scala, которые начинаются с символа '@'. Также стоит обратить внимание, что шаблоны похожи на функции и имеют параметры, которые описываются в начале файла.
Чтобы в эклипсе работало автодополнение для вновь созданных шаблонов проект надо запускать через тильду: "~run", также в настройках Windows — Preferences — General/Workspace убрать галочку «Build automatically», поставить галочки «Refresh using native hooks or polling», «Refresh on access».
Передаем из контроллера приложения 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[Category], categoryForm: Form[Category]) @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[Category], categoryForm: Form[Category]) @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
