Nie raz spotkałem się z problemem implementacji wielostronicowych formularzy w projektach w których uczestniczyłem, zazwyczaj były to formularze rejestracji, które składały się z 2-4 kroków. Podstawowe problemy które należy rozwiązać przy wykonywaniu formularza tego typu:
- możliwie jak najprostszy, spójny i elastyczny sposób przetwarzania formularza, aby ewentualne dodanie nowego pola lub całego formularza kosztowało jak najmniej nakładu pracy
- napisanie kodu, który będzie można również wykorzystać w przyszłości w innym projekcie
Istnieją dwa główne mechanizmy przechowywania danych z formularzy z poprzednich stron / kroków. Na pierwszy rzut oka zaprzęgnięcie sesji w tym celu wydaje się dobrym rozwiązaniem, jednakże czy tak w rzeczywistości jest? Sesja może wygasnąć podczas wypełniania długiego formularza lub też formularz może nie zostać w pełni wypełniony, co skutkuje przechowywaniem śmieci w sesji (rzutuje to również na wydajność). Oczywiście, można napisać coś w rodzaju garbage collection aby rozwiązać ten drugi problem, ale jest też inne rozwiązanie - przekazywanie danych z poprzednich kroków w ukrytych polach formularza. To również nie jest doskonałe, ale w mojej ocenie przysparza mniej problemów niż sesja.
Jeśli wiemy w jaki sposób przekazywać dane między żądaniami, zobaczmy jak to by wyglądało w praktyce.
(pseudokod)
[PHP]- $forms = array(/* tablica obiektów formularzy dla poszczególnych stron*/);
-
- if(/*wysłano formularz*/)
- {
- $page = /*strona obecnego formularza, załóżmy że numerowanie zaczyna się od 0 */;
- $values = /*dane wysłane postem*/;
- $fail = null;//pierwszy formularz, który nie przeszedł walidacji
-
- //waliduj wszystkie formularze do ostatnio wysłanego
- for($i=0; $i<$page; $i++)
- {
- if(!$forms[$i]->isValid($values))
- {
- $fail = $forms[$i];
- break;
- }
- }
-
- //wystąpił błąd
- if($fail)
- {
- //wyświetl $i-ty formularz z komunikatami błędów
- //oraz poprawnie zweryfikowane formularze jako ukryte pola
- }
- else
- {
- if(/* wysłano i zweryfikowano formularze ze wszystkich stron */)
- {
- //zapisanie danych do bazy danych i przekierowanie
- }
- }
- }
-
- if(!$fail)
- {
- //wyświetl obecny formularz
- echo $forms[$page];
- for($i=0; $i<$page; $i++)
- {
- //wyświetl poprzednie formularz jako ukryte pola formularzy
- }
- }
W powyższym rozwiązaniu cała logika obsługi wielostronicowego formularza jest skumulowana w jednym miejscu. To znacznie lepsze rozwiązanie niż obróbka formularzy z kolejnych kroków w innej akcji kontrolera. Dzięki powyższemu rozwiązaniu dodawanie kolejnych formularzy nie stanowi problemu, należy jedynie do tablicy $forms dodać kolejny obiekt formularza (lub tablicę reprezentującą formularz?) na odpowiednim miejscu oraz ewentualnie zmienić kod wykonywany po weryfikacji wszystkich formularzy.
Pierwszy punkt z listy przedstawionej na początku artykułu został spełniony: sposób przetwarzania jest dosyć prosty, spójny i elastyczny. Pozostaje problem przenośności kodu.
Plugin do obsługi wielostronicowych formularzy dla symfony
Swego czasu napisałem plugin do obsługi wielostronicowych formularzy dla frameworku symfony. Jego główne zadania to rozdzielanie wartości parametrów, walidacja poszczególnych formularzy oraz ukrycie przed programistą problemu przenoszenia danych między żądaniami.
Tworzenie własnego wielostronicowego formularza.
[PHP]- class Form extends psPageableForm
- {
- public function setup()
- {
- $this->addForm(new Form1());
- $this->addForm(new Form2());
- $this->addForm(new Form3());
-
- $this->setNameFormat('form[%s]');
- }
- }
Jednym ze sposobów stworzenia wielostronicowego formularza jest nadpisanie klasy psPageableForm i w metodzie setup (lub configure) dodanie kolejnych formularzy.
Przetwarzanie formularza tego typu będzie zbliżone do tego zaprezentowanego na pierwszym listingu - algorytm jest niemalże ten sam, z tą różnicą, że część zadań wykonują klasy pluginu.
[PHP]- //kontroler
- public function executeProcessForm(sfWebRequest $request)
- {
- $page = (int) $request->getParameter('step', 1);
- $form = $request->hasAttribute('form') ? $request->getAttribute('form') : new Form();
- $form->setPage($page);
-
- if($request->isMethod('post'))
- {
- //jeśli ta metoda została wywołana po błędzie walidacji poprzedniego formularza
- //nie przeprowadzaj walidacji
- if($request->getAttribute('return') != 1){
- $form->bind($request->getParameter('form'));
- if(!$form->isValid()){
- //numer strony to numer pierwszego niepoprawnego formularza
- $page = $form->getFirstInvalidForm()->getOption('page');
- $request->setParameter('step', $page);
-
- //utawić atrybuty żądania, aby przekazać zwalidowany formularz
- $request->setAttribute('return', 1);
- $request->setAttribute('form', $form);
-
- //wywołanie rekurencyjne akcji aby wyświetlić formularz
- //który nie przeszedł walidacji
- return $this->executeProcessForm($request);
-
- //zweryfikowano ostatni formularz
- }elseif($page > $form->getPages()){
- //zapisanie danych do bazy
- $this->redirect(/*przekierowanie*/);
- }
- }
- }
- else
- {
- $form->setPage(1);
- }
-
- $this->form = $form;
- }
-
- //widok
- echo $form->getCurrentForm();
- //wyświetlenie ukrytych pól formularza
- echo $form->persist();
Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...
Zwiń
Czytaj na blogu autora...