Dziś jest sobota, 31 lipca 2010 roku (z kalendarza...)

OPF się uruchamia

Icon

31.08.2009, 21:55

Projekty

Komentarze (4)

Powrót

Przez ostatnie kilka dni starałem się doprowadzić kod Open Power Forms do stanu uruchamialności, aby był w stanie przetworzyć prosty formularz. Było to niezbędne do dalszego kontynuowania prac i wymagało zaprojektowania wszystkich podstawowych mechanizmów przynajmniej w szczątkowej formie, ale udało się - kilkanaście minut temu OPF przetworzył swój pierwszy formularz od początku do końca.

Czym jest OPF?

Krótko o projekcie - Open Power Forms to biblioteka służąca do przetwarzania formularzy HTML. Jej główną zaletą ma być pełna integracja z systemem szablonów Open Power Template, co uczyni projektowanie wyglądu formularzy dużo łatwiejszym, niż w przypadku czystego PHP, szczególnie przy zaawansowanych układach. Projekt jest niemal równie stary, jak OPT (pomysł i pierwsze kawałki kodu powstały już w 2005 roku) i choć wykorzystałem go z powodzeniem na paru stronach, nie doczekał się nigdy stabilnego, a nawet przyzwoitego publicznego wydania. Wraz z ukazaniem się OPT 2.0, było tylko kwestią czasu rozpoczęcie prac nad OPF 2.0, dużo lepiej zaprojektowanym i zintegrowanym z nową wersją.

Pierwsze uruchomienie

Najtrudniejszą rzeczą na początku prac jest uruchomienie wszystkich podstawowych mechanizmów tak, aby uzyskać jakiś punkt startowy dla całej reszty zadań. Celem przetestowania, stworzyłem prosty formularz:

<?php
/**
 * The development test file used in the implementation process.
 *
 * @author Tomasz Jęrzejewski <http://www.zyxist.com/>
 */
 
require('./init.php');
 
class My_Form extends Opf_Form
{
	// An event
	public function onInit()
	{
		$item = $this->itemFactory('title');
		$item->setRequired(true);
		$item->addValidator(new Opf_Validator_Length(5), 'The length is invalid');
		$item->setWidget(new Opf_Widget_Input);
 
		$item = $this->itemFactory('countries');
		$item->setRequired(true);
		$item->addValidator(new Opf_Validator_Type(Opf_Validator_Type::INTEGER), 'The field type is invalid.');
		$item->setWidget(new Opf_Widget_Select);
	} // end onInit();
 
	// An event
	public function onRender()
	{
		$view = $this->getView();
		$view->setFormat('default', 'Form/Form');
		$item = $this->itemFactory('countries');
		$item->getWidget()->setOptions(array(0 =>
			'China',
			'France',
			'Germany',
			'Great Britain',
			'Poland',
			'Russia',
			'Spain',
			'United States'
		));
	} // end onRender();
 
	// An event
	public function onAccept()
	{
		$view = $this->getView();
		$view->setTemplate('results.tpl');
		$results = array();
		foreach($this->getValues() as $name => $value)
		{
			$results[] = array('name' => $name, 'value' => $value);
		}
		$view->results = $results;
	} // end onAccept();
} // end MyForm;
 
try
{
	$tpl = new Opt_Class;
	$opf = new Opf_Class;
	$tpl->sourceDir = './templates/';
	$tpl->compileDir = './templates_c/';
	$tpl->compileMode = Opt_Class::CM_REBUILD;
	$tpl->setup();	
 
	$view = new Opt_View('situation_1.tpl');
	$view->devFile = 'situation_1.php';
 
	$form = new My_Form('form1');
	$form->setView($view);
 
	$form->execute();
 
	$output = new Opt_Output_Http;
	$output->render($view);
}
catch(Opf_Exception $exception)
{
	$handler = new Opf_ErrorHandler;
	$handler->display($exception);
}
catch(Opt_Exception $exception)
{
	$handler = new Opt_ErrorHandler;
	$handler->display($exception);
}
catch(Opl_Exception $exception)
{
	$handler = new Opl_ErrorHandler;
	$handler->display($exception);
}

W OPF, formularz będzie można zaprojektować na kilka sposobów. Jednym z nich, prezentowanym na przykładzie, jest rozszerzenie klasy Opf_Form i nadpisanie kilku zdarzeń w stylu onInit() czy onAccept(). Jeśli nie chcemy tworzyć dziesiątek klas, zawsze możemy przechwycić wynik metody execute() i odpowiednio nań zareagować, konfigurując cały formularz w używającym go kodzie.

Formularz składa się z tzw. elementów (ang. items). Element to nazwany zbiór filtrów i sprawdzarek, któremu możemy przypisać widget. Widgety odpowiadają za wyświetlenie kontrolki formularza, za pomocą której użytkownik będzie wprowadzał dane do elementu i od strony technicznej są zwyczajnymi komponentami OPT. W tym przypadku wybrałem budowanie formularza w całości po stronie skryptu. Szablon dostaje już gotowe widgety, które musi tylko wyświetlić przy pomocy sekcji. Tu ujawnia się pierwsza rzecz, która ma wyróżniać OPF spośród innych systemów. W skrypcie PHP nie będziemy musieli wywoływać setWidget() na elemencie, aby zdefiniować, jak będzie mógł się wyświetlić. Zadanie to będzie mogło być przerzucone w całości na szablon, gdzie użyjemy zwykłego znacznika XHTML odpowiadającego danemu widgetowi. System automatycznie skojarzy go z odpowiednim elementem, zaś my będziemy mogli go ustawić dokładnie tak, jak chcemy, bez bawienia się skomplikowaną obiektówką.

Oczywiście na tym zalety wykorzystania OPT się nie kończą. Nawet definiując widgety po stronie skryptu, wciąż możemy w bardzo intuicyjny sposób manipulować otoczeniem każdego pola dzięki dostępnym w OPT snippetom. Oto fragment szablonu dla powyższego przykładu:

<opf:form name="form1">
  <p opt:if="not $system.form.valid">The form is invalid.</p>
 
  <!-- display the items assigned to "default" placeholder -->
  <opt:section name="default">
     <opt:component from="$default.component">
       <!-- a single field look -->
       <com:div>
         <p>{$system.component.title}</p>
         <opt:display />
         <opt:onEvent name="error">
            <p class="error">{$system.component.error}</p>
         </opt:onEvent>
       </com:div>
     </opt:component>
  </opt:section>
 
  <input type="submit" value="Submit" />
</opf:form>

Otoczenie pola formularza zdefiniowane zostało prosto, łatwo i przyjemnie, bez żonglowania dziesiątkami plików i klas, często łamiących ideę separacji logiki od prezentacji. Jeśli widok nam się spodoba tak bardzo, że zechcemy go wykorzystać w innych formularzach, pakujemy go w snippet i ładujemy wszędzie, gdzie tego pragniemy:

<opt:snippet name="widget">
       <!-- a single field look -->
       <com:div>
         <p>{$system.component.title}</p>
         <opt:display />
         <opt:onEvent name="error">
            <p class="error">{$system.component.error}</p>
         </opt:onEvent>
       </com:div>
</opt:snippet>
 
<opf:form name="form1">
  <p opt:if="not $system.form.valid">The form is invalid.</p>
 
  <!-- display the items assigned to "default" placeholder -->
  <opt:section name="default">
     <opt:component from="$default.component" template="widget">
     </opt:component>
  </opt:section>
 
  <input type="submit" value="Submit" />
</opf:form>

Poniżej przedstawiony jest z kolei szablon formularza, gdzie zdecydowaliśmy się wszystko robić ręcznie (z tym samym snippetem):

<opf:form name="form1">
  <p opt:if="not $system.form.valid">The form is invalid.</p>
 
  <!-- display the items assigned to "default" placeholder -->
  <opf:input name="title" template="widget">
  </opf:input>
 
  <opf:select name="countries" datasource="$countries" template="widget">
  </opf:select>
 
  <input type="submit" value="Submit" />
</opf:form>

W najbliższych dniach będę dalej implementować minimalną funkcjonalność, tj. wypełnianie pól domyślnymi danymi, przepisywanie do nich wartości w przypadku błędnego wypełnienia, obsługę błędów czy wsparcie dla CSS-a. Jednak przed biblioteką jeszcze długa droga. Planowo, narzędzie ma umożliwić tworzenie i bezproblemowe działanie nawet najbardziej skomplikowanym układom. Krok w tym kierunku już został poczyniony - klasa Opf_Form jest jednym z potomków Opf_Item reprezentującego element, co umożliwi tworzenie dużego formularza poprzez połączenie ze sobą kilku mniejszych. Niebawem zacznę także projektować obsługę zdarzeń zapewniających formularzom podobną funkcjonalność, jak zachowania (behaviours) w Doctrine, dla którego nawiasem mówiąc też jakieś wsparcie planuję. Będzie bardzo miło, jeśli formularz OPF będzie potrafił zintegrować się z odpowiednim modelem i automatycznie przepisywać do niego przychodzące dane...

Zakończenie

Nieodłączne pytanie brzmi: kiedy? Ciężko mi na nie odpowiedzieć w tej chwili. Podstawy stworzyć jest prosto, jednak później zostaje cała masa dłubaniny związanej z napisaniem n sprawdzarek, m filtrów i p widgetów. W tym ostatnim zaofiarował się trochę z pomocą eXtreme, więc można mieć nadzieję, że OPF będzie posiadać porządny zbiór gotowych do wykorzystania kontrolek.

Zarówno opisywany kod, jak i biblioteka, znajdują się już w repozytorium SVN Invenzzii i tam też można je sobie dokładniej obejrzeć.

Powrót

Przypisy:

Komentarze

Napisał AdvMDev w wtorek, 1 września 2009 o 14:45

Nice one, dude. But I'm worried about one thing. We have (thanks to you) OPL Exception, OPF Exception and OPT Exception. Is it possible to make a parrent class for them, and then access error handler as an object's method (ex. $Exception -> HandleException();)? It would save a lot of code. ;)

Napisał Zyx w wtorek, 1 września 2009 o 17:44

Yeu, æversêt an thæ net parrâdêt sěn, gein Opl_Exception craŝ sas sæ pater eppăt id Opt_Exception el Opf_Exception. Flanes faŝirk quantem try... catch mellane, jem erkommst kontæ infu id ŝâvalěr. Sesai ann sesai.

... i to by było na tyle w temacie "ja znam angielski i muszę się tym pochwalić na Zyxist.com" :). Miłego tłumaczenia z ferrinckiego.

Napisał Nowaker w wtorek, 1 września 2009 o 18:57

Trafne podsumowanie, Zyx. Nie ma tego języka na Google Translate, więc raczej niełatwo będzie mu to przetłumaczyć. Będzie musiał, co najmniej, przeszukać Googla w poszukiwaniu jakieś translatora ;-)

Napisał megawebmaster w wtorek, 1 września 2009 o 21:24

I tak spore ułatwienie, bo przynajmniej Zyx podał jaki to język ;)

Pamiętaj, dbaj o kulturę wypowiedzi oraz dyskusji w sieci.

Skomentuj

NickInformacja
E-mailNa wypadek potrzeby kontaktu z autorem (niepublikowany)
BlogNie zapomnij o http://
LayoutNapisz tu, czy widzisz dzienny czy nocny layout.
WpisFormatowanie wikiKomentarze są moderowane - przeczytaj zasady!

Na Zyxist.com panuje swoboda wyrażania opinii oraz krytyki pod dowolnym adresem. Jedyny warunek: musi być ona kulturalna i rzeczowa. Na chamstwo, prostactwo lub jawne obrażanie kogokolwiek nie ma tu miejsca i takie komentarze są bardzo szybko usuwane. Jeśli zamierzasz polemizować z treścią wpisu, wpierw uważnie ją przeczytaj.

© Tomasz "Zyx" Jędrzejewski 2005 - 2010 | Wykonanych zapytań: 2 | Serwer wirtualny zapewnia