Pull to refresh

Пишем первый проект на Play Framework 2.0

Website development *Java *Scala *
Sandbox
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. Для запуска проекта на другом порте используем «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:
Tags:
Hubs:
Total votes 21: ↑18 and ↓3 +15
Views 38K
Comments Comments 15