Mamy stworzyć aplikację, która pozwala zawrzeć przez internet umowę ubezpieczenia. Musimy spytać o przystępujące do umowy strony (ubezpieczający, ubezpieczony, ubezpieczyciel, uposażony, cesjonariusze itd.), każda ze stron może być osobą lub podmiotem, może być właścicielem ubezpieczanego mienia, współwłaścicielem, użytkownikiem itd. Firmy mają czasem po kilka adresów i telefonów. Potem mamy przedmiot ubezpieczenia - jeden bądź więcej. Warunki ogólne (okres, sumy, limity, dotychczasowe umowy itd.). Pewne elementy wynikają z siebie, inne nie. Generalnie saigon znczy się przejebane jak w ruskim czołgu.
No i teraz mamy napisać do tego nawigację, taką, żeby można sobie swobodnie dodawać odpowiednie elementy, nieraz wielokrotnie, musi działać kontrola danych oraz możliwość cofnięcia się i poprawienia dowolnego elementu, a potem powrót do normalnego trybu wypełniania.
8O ...

Jak ktoś ma gotowe rozwiąznie to niech mi przyśle

Na razie wykombinowałem kilka ważnych elementów. Mianowicie: nawigacja musi działać w pętli. Przecież nie zawsze przeładowywujemy stronę przed wyświetleniem nowego formularza. Po wypełnieniu post'ujemy zmienne do siebie (dlaczego, chyba nie muszę tłumaczyć) i sprawdzamy poprawność. Jeśli są błędy to wyświetlamy ten sam formularz ale informujemy o błędach, a jeśli wszystko ok? No to musimy wyświetlić kolejny formularz ale strony nie przeładowujemy - dlatego przydaje się pętla. Moduł nawigacji może wyglądać np. tak (nazwijmy go powiedzmy "nawigacja.php"):
Kod
<?php
session_start();
if (empty($_POST['form_name']))
{
$_SESSION['dane']=$_SESSION['nawigacja']=array();
$_SESSION['nawigacja'][]='strona_2';
$_SESSION['nawigacja'][]='strona_1';
$form_name=array_pop($_SESSION['nawigacja']);
};
//nadpisujemy 'form_name' i 'post_name' jeśli przyszły post'em
if (!empty($_POST['form_name'])) $form_name=$_POST['form_name'];
if (!empty($_POST['post_name'])) $post_name=$_POST['post_name'];
//definiujemy cechy pliku
$file_root='';
$file_extension='.inc';
while ($form_name)
{
//przenosimy nazwę formularza na inną zmienną
$temp_form_name=$form_name;
//blokujemy pętle
$form_name='';
//kontrolujemy czy to pierwsze wyświetlenie formularza
$first_time=((!($temp_form_name==$post_name))||$once_again);
//czyścimy zmienne
$errors=$values=array();
$once_again=false;
//definiujemy plik formularza
$file=$file_root.$temp_form_name.$file_extension;
//ładujemy odpowiedni formularz
include($file);
}
function cut_blank($value) {return (trim($value)=='') ? false : true;}
function set_values(&$values)
{
//tworzymy tablice wartości pól
//jeśli ktoś ma ponazywane przyciski
//submit to tutaj można wyciąć
//ich wartość z tablicy
$values=$_POST;
unset($values['form_name']);
unset($values['post_name']);
unset($values['post_once_again']);
}
function set_variable($name,$value)
{
print("<INPUT TYPE="HIDDEN" NAME="$name" VALUE="$value">n");
}
?>
session_start();
if (empty($_POST['form_name']))
{
$_SESSION['dane']=$_SESSION['nawigacja']=array();
$_SESSION['nawigacja'][]='strona_2';
$_SESSION['nawigacja'][]='strona_1';
$form_name=array_pop($_SESSION['nawigacja']);
};
//nadpisujemy 'form_name' i 'post_name' jeśli przyszły post'em
if (!empty($_POST['form_name'])) $form_name=$_POST['form_name'];
if (!empty($_POST['post_name'])) $post_name=$_POST['post_name'];
//definiujemy cechy pliku
$file_root='';
$file_extension='.inc';
while ($form_name)
{
//przenosimy nazwę formularza na inną zmienną
$temp_form_name=$form_name;
//blokujemy pętle
$form_name='';
//kontrolujemy czy to pierwsze wyświetlenie formularza
$first_time=((!($temp_form_name==$post_name))||$once_again);
//czyścimy zmienne
$errors=$values=array();
$once_again=false;
//definiujemy plik formularza
$file=$file_root.$temp_form_name.$file_extension;
//ładujemy odpowiedni formularz
include($file);
}
function cut_blank($value) {return (trim($value)=='') ? false : true;}
function set_values(&$values)
{
//tworzymy tablice wartości pól
//jeśli ktoś ma ponazywane przyciski
//submit to tutaj można wyciąć
//ich wartość z tablicy
$values=$_POST;
unset($values['form_name']);
unset($values['post_name']);
unset($values['post_once_again']);
}
function set_variable($name,$value)
{
print("<INPUT TYPE="HIDDEN" NAME="$name" VALUE="$value">n");
}
?>
W kodzie tym jest jeszcze jeden ciekawy element - zmienna $_SESSION['nawigacja']. Jest to stos sterujący przebiegiem wypełniania formularzy. Na początku wypełniamy go listą kolejnych formularzy i zdejmujemy ze stosu pierwszy element. Zostaje on uruchomiony w pętli. Każdy kolejny formularz wykonuje się i zdejmuje ze stosu następny element (czyli wskazuje kto ma być po nim). No chyba, że ktoś źle wypełnił formularz. Wówczas nie wskazujemy następcy lecz wykonujemy formularz jeszcze raz i pozwalamy userowi dokonać poprawek. Możliwa jest też sytuacja, że chcemy ponownie wyświetlić formularz, pomimo że ten wypełniono poprawnie. Nie ma problemu. Formularz dodaje siebie samego na stos i po kłopocie.
Pliki następnych dwóch formularzy (mamy je na stosie zgodnie z pierwszym skryptem) mogą wyglądać tak:
plik "strona1.inc":
Kod
<?php
//sprawdzamy czy to pierwsze wyświetlenie formularza
if ($first_time)
{
//jeśli to pierwsze wyświetlenie
//to tu możesz wykonać czynności wstępne
//np. ustawić domyślne wartości pól formularza
} else {
//to nie pierwsze wyświetlenie (wypełniono formularz)
//pobieramy zmienne które przyszły post-em
set_values($values);
//tu możemy dokonać kontroli poprawności
if(empty($_POST['imię'])) $errors['imię']='Proszę podać imię.';
if(empty($_POST['nazwisko'])) $errors['nazwisko']='Proszę podać nazwisko.';
};
//jeśli już wypełniono formularz i sprawdziliśmy
//że nie ma w nim błędów to zapisujemy dane w sesji
if ((!$first_time)&&(empty($errors))) {
//jeśli formularz ma nam posłużyć jeszcze raz
//to każemy mu się wyświetlić ponownie
if ($_POST['post_once_again']=="true")
{
$_SESSION['nawigacja'][]=$temp_form_name;
$once_again=true;
};
//dopisujemy komplet danych do sesji
//array_filter usuwa puste elementy tablicy
$_SESSION['dane'][]=array_filter($values,"cut_blank");
//bierzemy w obroty następny formularz
$form_name=array_pop($_SESSION['nawigacja']);
} else {
//wyświetlenie formularza
?>
<HTML><BODY>
<FORM NAME="Form1" ACTION="<?= $_SERVER['PHP_SELF'] ?>" METHOD="POST" ACCEPT-CHARSET="iso-8859-2">
<?php
//ustawianie zmiennych które trzeba wysłać
set_variable('form_name',$temp_form_name);
set_variable('post_name',$temp_form_name);
set_variable('post_once_again','false');
?>
<TABLE ALIGN="CENTER" CELLSPACING="0" CELLPADDING="0" BORDER="1" STYLE="width: 400px">
<TR><TH>Formularz 1</TH></TR><TR><TD>
<TABLE CELLSPACING="1" WIDTH="100%">
<TR>
<TD STYLE="width: 100px">
Imię:
</TD>
<TD>
<INPUT TYPE="TEXT" NAME="imię" STYLE="width: 200px" VALUE="<?= $values['imię'] ?>">
<?php if(!empty($errors['imię'])) print("<BR>".$errors['imię']); ?>
</TD>
</TR>
<TR>
<TR>
<TD STYLE="width: 100px">
Nazwisko:
</TD>
<TD>
<INPUT TYPE="TEXT" NAME="nazwisko" STYLE="width: 200px" VALUE="<?= $values['nazwisko'] ?>">
<?php if(!empty($errors['nazwisko'])) print("<BR>".$errors['nazwisko']); ?>
</TD>
</TR>
<TR>
<TR>
<TD STYLE="width: 100px">
Pole opcjonalne:
</TD>
<TD>
<INPUT TYPE="TEXT" NAME="opcjonalne" STYLE="width: 200px" VALUE="<?= $values['opcjonalne'] ?>">
</TD>
</TR>
<TR>
</TABLE>
</TD></TR><TR><TD><BR><UL>
<LI><A HREF="javaScript: document.Form1.submit();">Zapisz dane i przejdź dalej.</A>
<LI><A HREF="javaScript: document.Form1.post_once_again.value='true'; document.Form1.submit();">Zapisz dane i pozwól mi dodać kolejną osobę.</A>
</UL>
</TD>
</TR>
</TABLE><BR>
<?php
};
?>
//sprawdzamy czy to pierwsze wyświetlenie formularza
if ($first_time)
{
//jeśli to pierwsze wyświetlenie
//to tu możesz wykonać czynności wstępne
//np. ustawić domyślne wartości pól formularza
} else {
//to nie pierwsze wyświetlenie (wypełniono formularz)
//pobieramy zmienne które przyszły post-em
set_values($values);
//tu możemy dokonać kontroli poprawności
if(empty($_POST['imię'])) $errors['imię']='Proszę podać imię.';
if(empty($_POST['nazwisko'])) $errors['nazwisko']='Proszę podać nazwisko.';
};
//jeśli już wypełniono formularz i sprawdziliśmy
//że nie ma w nim błędów to zapisujemy dane w sesji
if ((!$first_time)&&(empty($errors))) {
//jeśli formularz ma nam posłużyć jeszcze raz
//to każemy mu się wyświetlić ponownie
if ($_POST['post_once_again']=="true")
{
$_SESSION['nawigacja'][]=$temp_form_name;
$once_again=true;
};
//dopisujemy komplet danych do sesji
//array_filter usuwa puste elementy tablicy
$_SESSION['dane'][]=array_filter($values,"cut_blank");
//bierzemy w obroty następny formularz
$form_name=array_pop($_SESSION['nawigacja']);
} else {
//wyświetlenie formularza
?>
<HTML><BODY>
<FORM NAME="Form1" ACTION="<?= $_SERVER['PHP_SELF'] ?>" METHOD="POST" ACCEPT-CHARSET="iso-8859-2">
<?php
//ustawianie zmiennych które trzeba wysłać
set_variable('form_name',$temp_form_name);
set_variable('post_name',$temp_form_name);
set_variable('post_once_again','false');
?>
<TABLE ALIGN="CENTER" CELLSPACING="0" CELLPADDING="0" BORDER="1" STYLE="width: 400px">
<TR><TH>Formularz 1</TH></TR><TR><TD>
<TABLE CELLSPACING="1" WIDTH="100%">
<TR>
<TD STYLE="width: 100px">
Imię:
</TD>
<TD>
<INPUT TYPE="TEXT" NAME="imię" STYLE="width: 200px" VALUE="<?= $values['imię'] ?>">
<?php if(!empty($errors['imię'])) print("<BR>".$errors['imię']); ?>
</TD>
</TR>
<TR>
<TR>
<TD STYLE="width: 100px">
Nazwisko:
</TD>
<TD>
<INPUT TYPE="TEXT" NAME="nazwisko" STYLE="width: 200px" VALUE="<?= $values['nazwisko'] ?>">
<?php if(!empty($errors['nazwisko'])) print("<BR>".$errors['nazwisko']); ?>
</TD>
</TR>
<TR>
<TR>
<TD STYLE="width: 100px">
Pole opcjonalne:
</TD>
<TD>
<INPUT TYPE="TEXT" NAME="opcjonalne" STYLE="width: 200px" VALUE="<?= $values['opcjonalne'] ?>">
</TD>
</TR>
<TR>
</TABLE>
</TD></TR><TR><TD><BR><UL>
<LI><A HREF="javaScript: document.Form1.submit();">Zapisz dane i przejdź dalej.</A>
<LI><A HREF="javaScript: document.Form1.post_once_again.value='true'; document.Form1.submit();">Zapisz dane i pozwól mi dodać kolejną osobę.</A>
</UL>
</TD>
</TR>
</TABLE><BR>
<?php
};
?>
Plik "strona2.inc":
Kod
<?php
print('<PRE>');
print_r($_SESSION['dane']);
print('</PRE>');
session_destroy();
?>
print('<PRE>');
print_r($_SESSION['dane']);
print('</PRE>');
session_destroy();
?>
Dane gromadzone są w tablicy $_SESSION['dane']. Podsumowanie wyświetlamy sobie na "strona2.inc". Stwórzcie sobie te 3 pliki i odpalcie, powinny działać. Kod nie jest piękny (o tym za chwilę) ale spełnia swoją funkcję.
To jednak dopiero pierwszy krok. Dalej wizja jest taka: trzeba stworzyć drugi stos, na który odkładalibyśmy to, co już zostało poprawnie wypełnione. Dzięki temu w każdym momencie mielibyśmy pełny raport o tym gdzie jesteśmy, co już za nami, a co przed nami. Wyrzucamy sobie tą informację u góry okna (albo z boku - coś jak w setupie od Mandrake'a) i pozwalamy userowi łazić po formularzach w te i we fte ;-)
W międzyczasie zauważamy kolejną trudność. W przypadku błędu musimy wyświetlić formularz ponownie, ale dane które już są wypełnione, muszą takimi pozostać. Trzeba mieć więc możliwość nadania elementom formularza wartości domyślnych. Kiedy posługujemy się tylko polami tekstowymi w formularzu, to nie ma problemu - działa to jak w powyższym przykładzie. Ale co wtedy, gdy mamy pola typu checkbox, radio, select, multi-select czy też daty albo jeszcze czegoś dziwniejszego? Musimy stworzyć sobie bibliotekę komponentów które będzie można odpalić jednym poleceniem i nadać im przy tym wartość. Ja taką bibliotekę mam już częściowo napisaną, mogę ją opublikować. Niestety tworzyłem ją wtedy, gdy o obiektach w php wiedziałem jeszcze niewiele. Zadanie jest więc spore - trzeba przełożyć wszystkie komponenty formularza na system klas, a sam formularz przerobić na wersję obiektową.
Jest jeszcze problem rozgałęzień. Czasami trzeba w konkretnym formularzu zejść ze ścieżki wytyczonej przez stos, dodać na wierzch kilka pośrednich elementów (np. formularze do dodawania adresów i telefonów do osóB), wypełnić je, a potem kierująć się stosem wrócić do normalnego biegu wydarzeń. Da się to oczywiście zrobić, ale jeśli miałoby to działać na obiektach to musiałoby być bardzo spójnie napisane.
Kontrola poprawności pól powinna być w budowana w klasę danego komponentu formularza, tak by odpowiednia metoda sprawdzała poprawność danych.
Co sądzicie o takiej formie magazynowania danych jak użyta powyżej tablica 'dane'? Może lepszy byłby XML? Za chwilę trzeba będzie przecież wrzucić wyniki formularza do bazy danych i problem zyska na wadze. Poza tym czasami musimy wypełnić formularz domyślnymi danymi z bazy. Przykład: ktoś wklepuje pesel, powinien wklepać resztę danych osoby, ale okazuje się, że nie musi - mamy tą osobę już w bazie. Trzeba mu znalezione dane wyświetlić i pozwolić skorygować. I tu wychodzi na wierzch problem uspójnienia operacji wejścia/wyjścia, tzn. ujednolicenia formatu źródeł danych (formularz, baza, informacje z zewnątrz) i wyjścia danych (strona html - raport, formularz html - dane do skorygowania, plik pdf, zapis do bazy, wysyłka na inny serwer itd.). XML przypomina o sobie z nową siłą.
I jeszcze drobnostka: ma ktoś pomysł na wysłanie odpowiednich zmiennych w zależności od tego co user kliknie na dole formularza? W powyższym skrypcie robi to JavaScript ale metoda jakoś mi się nie podoba. Ma ktoś inny pomysł?
Formularze to chyba temat dla każdego aktualny. Mam więc nadzieję, że niektórych sprawa zainteresuje. Ciekawe linki na ten temat są przy podpunkcie na liście proponowanych przeze mnie tematów.