Autor wpisu: batman, dodany: 27.10.2010 08:00, tagi: zend_framework
Jedną z najgorszych czynności związanych z Zend_Form, jest pisanie w kółko tego samego fragmentu kodu – obsługi formularza. W większości przypadków obsługa ta sprowadza się do czterech kroków:
- Sprawdzenie, czy wysłano POSTa.
- Sprawdzenie, czy dane są poprawne.
- Przetworzenie danych.
- Przekierowanie na inną stronę.
Zgodnie z zasadą DRY, wypadałoby czynności te wydzielić do osobnego bloku (funkcji/metody/klasy), a następnie do tego bloku się odwoływać. Niestety na przeszkodzie stoi punkt 3, czyli operacje na danych, które nie zawsze muszą się powtarzać.
Z pomocą przychodzi tutaj PHP, a dokładniej anonimowe funkcje. Wprawdzie funkcjonalność ta jest dostępna dopiero od wersji 5.3 języka, jednak nie widzę powodu, aby nie iść z duchem czasu i trzymać się przestarzałych rozwiązań.
W przykładach będę korzystał z klasy Batman_Form, której opis znajdziecie we wpisie Uniwersalne dekorowanie Zend_Form.
Automatyczna obsługa formularzy Zend_Form sprowadza się do wykorzystania action helpera, który załaduje formularz, a następnie wykona po kolei opisane wyżej czynności. Funkcja anonimowa pozwoli na dodanie do action helpera kodu, który będzie można wykonać w ściśle określonym momencie. Na koniec tego teoretycznego wywodu napiszę jeszcze drobną uwagę. Obecna implementacja anonimowych funkcji opera się o klasę Closure i magiczną metodę __invoke. Manual enigmatycznie głosi, iż nie należy polegać na tej wiedzy, dlatego też może dojść do sytuacji, w której opisany przeze mnie sposób, przestanie działać. Rozwiązanie tego problemu jest banalnie proste i sprowadza się do usunięcia sprzed argumentu funkcji typu Closure.
A teraz do rzeczy. O pisaniu własnych helperów akcji przeczytacie w dokumentacji. W skrócie, helpery akcji są nieco okrojoną wersją pluginów i w przeciwieństwie do nich, wywoływane są na żądanie.
Kod helpera prezentuje się następująco
class Batman_Controller_Action_Helper_FormHandler extends Zend_Controller_Action_Helper_Abstract { protected $_form = null; protected $_callbacks = array(); public function postDispatch() { // czy wysłano POSTa if($this->getRequest()->isPost()) { $postData = $this->getRequest()->getPost(); // czy dane są poprawne if($this->_form->isValid($postData)) { // pobranie wszystkich funkcji zwrotnych przypisanych do formularza $callbacks = $this->_form->getCallbacks(); $formData = $this->_form->getValues(); foreach($callbacks as $callback) { // kolejne wywołanie zarejestrowanych funkcji zwrotnych $callback($formData); } } } } /** * @return Zend_Form */ public function getForm($name, $options = null) { $prefix = 'Application_'; if(isset($options['prefix'])) { $prefix = $options['prefix']; unset($options['prefix']); } $formClass = $prefix . 'Form_' . $name; $this->_form = new $formClass($options); return $this->_form; } /** * @return Zend_Form */ public function direct($name, $options = null) { return $this->getForm($name, $options); } }
Założyłem w helperze, iż domyślną przestrzenią nazw dla klas jest Application. W większości przypadków takie założenie jest prawidłowe, jednak dla spokoju ducha, można w tym miejscu pobrać tą wartość z pliku konfiguracyjnego. Drugim założeniem jest wykorzystanie domyślnej struktury katalogów.
W sumie powyższy kod nie jest specjalnie skomplikowany. W momencie wywołania tego helpera, utworzony zostanie obiekt formularza, a następnie zwrócony do kontrolera.
Wywołanie wyżej opisanego helpera w kontrolerze wygląda nastęująco