Projekt “prosty bash.org w jeden dzień” ciągnie się już drugi dzień 😀 W dwóch wpisach przybliżyłem już jak się ma sprawa z modelem danych w PF. Dziś opiszę jak od podstaw stworzyć kontroler i widok do jego obsługi. Generalnie zasady są proste. Rozszerzamy klasę Controller i dodajemy mapowania w pliku routes. W praktyce jest tylko trochę trudniej 😉

Założenia

Nasz kontroler będzie pozwalał na dodanie cytatu do bazy. Cytat, będzie niewidoczny na stronie głównej dopóki administrator go nie zaakceptuje. Proste 🙂

AddQuoteController i co z tego wynika

Nasz kontroler będzie posiadał akcję addQuoteAction oraz metodę addQuote, która będzie renderować ekran z formularzem. Na początek wyrenderujemy sobie ekran z formularzem.

Listing 1. AddQuoteController.addQuote()

package controllers;

import play.mvc.Controller;

public class AddQuoteController extends Controller {

	public static void addQuote() {
		render();
	}
}

W sumie tak wygląda najprostszy kontroler w Play Framework. Metoda render przyjmuje zmienną liczbę parametrów, które są bindowane do zmiennych w szablonie. To jednak za chwilę. Najpierw dodamy jeszcze jedną metodę.

Listing 2.

package controllers;

import java.util.GregorianCalendar;

import net.sf.oval.constraint.MinLength;

import models.Quote;
import play.data.validation.Min;
import play.data.validation.Required;
import play.mvc.Controller;

public class AddQuoteController extends Controller {

	public static void addQuote() {
		render();
	}

	public static void addQuoteAction(
			@Required(message = "Wymagane!") @MinLength(value = 10, message = "Conajmniej 10 znaków!") String content) {
		if (validation.hasErrors()) {
			render("AddQuoteController/addQuote.html");
		}
		Quote newQuote = new Quote();
		newQuote.content = content;
		newQuote.accepted = false;
		newQuote.addTime = new GregorianCalendar();
		newQuote.avgNote = 0;
		newQuote.numberOfNotes = 0;
		newQuote.save();
		Application.index();
	}
}

Dodaliśmy metodę addQuoteAction. Jako parametr przyjmuje ona klucz content. Klucza tego będziemy używać za chwilę w szablonie. Przy okazji zastosowałem dwie adnotacje. Pierwsza @Required oznacza, że parametr jest wymagany. Druga @MinLenght to minimalna wielkość parametru, przy czym w przypadku obiektów String oznacza to długość napisu. W ten magiczny sposób odrobiliśmy walidację. Jeżeli w walidatorze są błędy to przekierowujemy całość na stronę dodawania cytatu. I tu kolejna rzecz z dupy trochę.
W Springu była sobie klasa ModelAndView, która pozwalała na wykonywanie różniastych ekscesów z przekazywaniem sterowania. W skrajnym przypadku pozwalała ona na przekierowanie obsługi do innego kontrolera, pobranie wyników przechowywanych właśnie w ModelAndView zwróconym przez ten kontroler i przekazanie ich dalej do renderera. Tu niestety musimy wywoływać metodę render z parametrem w postaci String. Dla mało kumatych podpowiem, że nie można tego sprawdzić w trakcie kompilacji więc tracimy wszystkie zyski kontroli typów. No, ale jak się nie ma co się lubi to się lubi co się ma… jak mawiał pewien stary syfilityk.
Jeżeli nie ma błędów to tworzymy nowy cytat, dodajemy go do bazy i renderujemy stronę główną. Banalnie proste. Tak proste, że każdy 13-nastoletni specjalista od php z adhd będzie wstanie coś podobnego zrobić (i stać się specjalistą od PF).
Na obecnym etapie mamy gotowy kontroler, który należało by jeszcze przetestować, ale na razie sobie to odpuszczę. Testowanie w PF jest mocno dziwne, a zatem na kolejny raz się temu przyjrzymy. Przy okazji będę narzekał na gówniany pomysł na wywoływanie akcji kontrolerów (chętni mogą już w teraz w komentarzach wpisywać swoje pomysły, dlaczego ten model jest gówniany).
O czym to ja… a właśnie mamy kontroler, ale nie mamy jeszcze odpowiadającego mu widoku. Stwórzmy zatem prosty widok, który będzie zawierał pole do wpisywania komentarza i przycisk wyślij.

Widok AddQuoteController/addQuote

Play Framework generuje widoki w oparciu o język szablonów, który znowuż korzysta z GroovyW. Nie jest on trudny. Pozwala na tworzenie całkiem zgrabnych stron, a i można własne makra tworzyć.
Tworzymy zatem nowy plik w katalogu app/views/AddQuoteController i nazywamy go addQuote.html. Jego zawartość jest prosta, a to dzięki mechanizmowi rozszerzania szablonów oferowanym przez PF.

Listing 3. kompletny addQuote.html

#{extends 'main.html' /} #{set title:'Cytaty - dodaj własny' /}
#{form @AddQuoteController.addQuoteAction()}
<div>
<textarea name="content" id="content" cols="40" rows="5"></textarea>
#{ifErrors}
  #{list items:errors, as:'error'}
    #{if "content".equals(error.getKey())}
      <p class="error">
        ${error}
      </p>
    #{/if}
  #{/list}
#{/ifErrors}
</div>
<input type="submit" value="Dodaj!" />
#{/form}

Elegancko. W pierwszej linii importujemy się do szablonu strony main.html (o szablonach kiedy indziej) i nadajemy tytuł naszej stronie. Następnie tworzymy formularz, który jako akcję wykona AddQuoteController.addQuoteAction(). Nie podoba mi się obsługa błędów. Z drugiej strony kilkukrotne przelecenie przez krótką listę można wybaczyć. No chyba, że coś w API przeoczyłem (na pewno można zrobić to ładniej).
Wszystko ładnie pięknie, ale jeszcze to nie koniec naszej pracy. Najpierw musimy dodać link do strony głównej (i ją napisać), ale pominę to, ponieważ sposób walki ze stroną główną chciałbym omówić przy okazji szablonów. No właśnie link…

Routing w PF

Jest sobie plik conf/routes, który zawiera informacje na temat tego jak wygląda mapowanie pomiędzy adresami URL, a kontrolerami. Normalnie wykonalibyśmy taką operację w pliku web.xml, ale PF upraszcza życie. Idea jest prosta. Programista definiuje sposób zasady routingu, a PF odpowiednio robi dobrze. Dodanie naszego mapowania jest bardzo proste. Zresztą była już o tym mowa przy okazji modułu CRUD. Nasza reguła wygląda tak:

Listing 4. Reguła dla AddQuoteController

GET		/addQuote			AddQuoteController.addQuote

Podsumowanie

Play Framework udostępnia programistom stosunkowo dobry i wydajny sposób tworzenia aplikacji. Dzięki zastosowaniu zasady COC i sztywnemu się jej trzymaniu (co niepomiernie potrafi wkurwić) można w miarę szybko prototypować aplikację. Dodatkowym plusem jest bardzo prosty, ale elastyczny język szablonów widoku. Dzięki czemu przyuczenie do zawodu nawet debilnego programisty php z adhd nie zajmuje za dużo czasu.
Obecnie chcę rozczaić jak PF współpracuje z GWT.