Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Faza projektowania - Generator Formularzy
Forum PHP.pl > Forum > PHP > Object-oriented programming
kaniagandzowski
Witam chcę poprosić was o rady co do budowy aplikacji i sugestie w jaki sposób mają być generowane elementy formularza z godnie z programowaniem obiektowym. W tym konkretnym przypadku.

W skrócie napisze co ma robić aplikacja i z jakich bibliotek będę korzystać.

Chcę stworzyć generator, który by pozwalał na proste tworzenie formularza z wykorzystaniem AJAX.
Zasada działania polegała by na wprowadzeniu wymaganych pól formularza, nadawanie reguł na dane pola, filtry i wybranie akcji.

Reguły nakładane na pola formularza:
- pole wymagane
- wartość typu liczby
- własne reguły
- upload plików itd.
Akcje:
- Zapisanie do bazy danych
- Wygenerowanie PDF
- wysłanie Emaila
- wykonanie dowolnego skryptu itd.
Filtry
- czyszczenie wartości ze spacji na początku i końcu wartości (polecenie trim) itd.

Wygląd formularza był by tworzony przy pomocy dowolnego programu typu Dreamware lub FrontPage
, który powstały szablon by zwierał tylko odwołania do zmiennych ( $labelImie, $imie ). Zmienne zawarte w szablonie zostaną zamienione na kod html dla danego pola np.
  1. <input id="ddd" name="ddd">
.

Do realizacji mojego zadania chcę maksymalnie wykorzystać gotowe rozwiązania aby nie odkrywać na nowo już dawno znanych wzorców projektowych i bibliotek. Tak więc będę wykorzystywać :
- FrameWork Code Igniter
- Xajax (biblioteka AJAX)
- PEAR – HTML_QuickForm

Do tematu podszedłem w ten sposób że każde dowolne rodzaj pola jaki może wystąpić w formularzu jest osobną klasą (np. pola typu input generowane są przez klase input).

Reguły są nakładane w taki sam sposób jak to robione jest w HTML_QuickForm elementy w sumie też podobnie ale tworze klasy ponieważ każdy element czy to reguła wymaga jakiś specyficznych parametrów zanim zostanie wygenerowany element czy nałożona rugała.

Np. tworząc własną regułę pozwalająca na wypełnienie formularza w danym czasie. Do takiej klasy należy przekazać kilka parametrów takich jak data rozpoczęcia i zakończenia publikacji formularza. A chce też w późniejszym czasie dodawać proste klasy dodające nowe funkcje. A że program ma generować formularze, musi wiedzieć o jakie parametry należy poprosić użytkownika.

Po niżej przedstawiam diagram klass.
Diagram przedstawia tylko klasy odpowiedzialne za tworzenie formularza, która sama się nie złoży i należy jeszcze dodać klasy odpowiedzialne za składanie obiektów w całość (klasy odpowiedzialne za wyświetlanie strony w której graficznie dodawało się pola do formularza - dla mnie to będzie najgorsze, ponieważ zawsze mam problemy z złożeniem w całość. Często kod staje się mniej czytelny i sam się w nim gubię sad.gif ).

Proszę też uwagi co do samego diagramu. Na pewno strzałki pokazujące relacje pomiędzy klasami nie są poprawne ponieważ nie do końca poznałem, sens praktycznego stosowania danych relacji jak złożenia, skojarzenia, generalizacja itd.

Co do diagramu. Mam problemy z dodawaniem polskich znaków tak więc, w diagramie, gdzie nie gdzie są polskie znaki. Wynika to iż program ma przypisane skróty klawiszowe, np ą maże całą zawartość pola.


Narzędzia jakie stosuje to:
- Zend Studio
- StarUML
- DBDesigner
NuLL
Pomijam rozmach biggrin.gif ale PHP4 to przezytek i ja chcesz z niego korzystasz to proponuje dac sobie na luz i zajac sie czyms innym smile.gif

http://gophp5.org
kaniagandzowski
Ja nie zamierzam pisać w php4. Wiem, że Code Igniter jest w php4 i HTML_QuickForm. Ale korzystam z nich ponieważ najłatwiej zacząć od tego (dla osób początkujących. Jeszcze nigdy nie programowałem tak poważnie - w sensie spróbowania napisania coś porządnie niż pisania kodu proceduralnie. Zawsze było to krótko trwałe zainteresowanie, nie próbując poznać tematu dogłębniej).

Tak pozatym nie muszę pisać swoje klasy w frameworku w php4. Mogę spokojnie pisać w PHP5 i te klasy będą działać z tymi bibliotekami napisanymi w php4 (mam namyśli Framework oraz PEAR QuickForm). Pod warunkiem że idea pracy programu zostanie szczegółowo zaplanowana. Bo jeśli tego nie zrobię to podczas pisania stanie się kod skomplikowany, w którym będę stosować tymczasowe rozwiązania dla danego problemu.

Sprawdziłem też czy AJAX będzie mógł spokojnie działać z HTML_QuickForm. Nie było żadnych problemów tylko zastosowałem małą sztuczkę polegającą na przekazaniu wartości wywołania funkcji
  1. xajax.getFormValues(nazwaFormularza)
po wysłaniu formularza przez AJAX do do jakieś metody z wartościami formularza, które to wartości są przekazane do $_POST.

Wiem że już powstaje QuickForm w wersji PHP5 ale jest to wersja, która dopiero powstaje. Próbowałem zainstalować lecz nie powiodło. Wolałem pozostać przy starej wersji do której jest dużo dokumentacji.

Oczywiście to co napisałem mogę się mylić, nie mieć świadomości na jakie problemy na potkam podczas pisania lub jakieś bzdury pisać. I dlatego proszę o skomentowanie czy mój pomysł jest najprostszy w sensie utrzymania kodu i możliwości rozbudowania oraz czy poprawnie narysowałem Diagram z godnie UML 2.0

Wiem że są wzorce projektowe poznałem kilka ale to wszystko teorię poznałem. Martwię się czy nie są klasy za mocno powiązane pomiędzy sobą.
Do przekazywania jakie pola mają powstać oraz jakie reguły dana ma mieć element (pole formularza), służyć będzie do tego klasa AtributesForm.

Obiekt z klasy AtributesForm będzie zawierać dane do stworzenia formularza takie jak:
- nazwa pola, typ pola i inne parametry potrzebne do wygenerowania danego elementu
- reguły walidacji dla danego pola
- nazwa formularza, komentarz dla formularza (aby ułatwić opisywanie formularzy)

Taki obiekt AtributesForm będzie przekazywany do metody createElement obiektu pochodzenia klasy Formularz (nie wiem jak fachowo to nazwać
  1. <?php
  2. $jakiśObiekt = new Formularz;
  3. ?>
).

Ta metoda będzie zawierać jakieś funkcje ładujące określone klasy na podstawie danych przekazanych tej metodzie. I wywoływać będzie po kolei każdą klasę Element dla danego typu np. input, data czy upload i do niej przekazywał potrzebne parametry a ta zwracać będzie obiekt typu HTML_QuickForm. Który obiekt będzie przechowywany w obiekcie Formularz w zmiennej arrElement (zmienna będzie zawierać wszystkie elementy).
W sumie nie chcę dużo zmieniać działania samego QuickForm.

Reguły i w sumie elementy zawierać się będą w zmiennej $objForm w klasie Formularz tak jak się robi formularze przy pomocy QuickForm. Poniżej prosty przykład tworzenia formularza w HTML_QuickForm
  1. <?php
  2. require_once "HTML/QuickForm.php";
  3.  
  4. $form = new HTML_QuickForm('frmTest', 'get');
  5. $form->addElement('header', 'MyHeader', 'Wypelnij dane:');
  6. $form->addElement('text', 'MyTextBox', 'Twoje imie:');
  7. $form->addElement('reset', 'btnClear', 'Wyczysc');
  8. $form->addElement('submit', 'btnSubmit', 'OK');
  9.  
  10. $form->addRule('MyTextBox', 'Twoje imie jest wymagane!', 'required');
  11.  
  12. if ($form->validate()) {
  13. # Jezeli poprawnie wypelniono formularz -> FREEZE
  14. $form->freeze();
  15. }
  16. $form->display();
  17. ?>


Widać, że obiekt $form zawiera wszystkie elementy i reguły tylko do niego są rzucane to co nam jest potrzebne.
Działanie programu chce podobnie zrobić. Zapamiętanie tych wszystkich elementów i reguł będą zawarte w obiekcie Formularz, który zostanie serializowany do bazy danych.
Starałem się napisać w miarę prosto jak tylko potrafię.

Na pewno to co piszę może się zdarzyć jakiś błąd, odnośnie wiedzy jaką posiadam o danych bibliotekach i Frameworku tzn. w jaki są zbudowane itd. ogólnie Informacje techniczne.
Sh4dow
No ja w wirze pisanie 1000 i jednego narzedzia zrobiłem generator takich formularzy opisanych oczywiscie za pomoca tablic (w 99% pochodzacych z bazy danych). W moim przypadku kozystam jedynie z wlasnego rozwiazania gdzie pojedyncze pole formularza jest osobnym obiektem. Na chwile obecna nie mam pol do uploadu plikow, ale poprostu nie uzywalem jeszcze tego do takich celow.
Ja mam troche bardziej rozbudowany sam formularz i walidacje, ale nie mam takich rzeczy jak generowanie pdf'a czy wysylanie maila bo uwazam to za niepotrzebne.
Ja za to poza nazwa i rodzajem pola mam sposob walidacji. powiedzmy pole text moze byc inputem lub textarea, pole bool moze byc radio albo checkbox. do tego dochodza zaleznosci miedzy polami, wymagania pola, dodatkowe opcje jak max ilosci znakow, maxymalna wartosc i wiele wiele innych.

Kazdy obiekt ma metody do wyswietlenia samego pola, jego nazwy, znaku wymagania (np.*), sprawdzanie czy jest jakis blad do wyswietlenie(np. 'pole wymagana', 'maksymalna wartosc to 25');
No i dodatkowo opcjonalny modul do generowania kodu JS (w przygotowaniu bo nie lubie JS'a) do wstepnej walidacji po stronie przegladarki.

W sumie jest pare niedociagniec bo project zostawilem na tyle na ile jest mi potrzebny i nie dodalem wszystkich (kiedys zakladanych) modulow jak wyjscia inne niz (x)HTML np. XUL itd.

Zycze powodzenia w pisaniu takich narzedzi bardzo czesto sie przydaja smile.gif
kaniagandzowski
Ma problem z cechą jaką posiadają obiekty.

Obiekty serializowane czy to do bazy czy do sesji ($_SESSION[‘nazwaJakiegośObiektu’] = jakiśSerializowanyObiekt) po deserializacji po jakimś czasie (uruchomieniu programu czy to inna sytuacja) wymaga posiadania definicji klasy dla danego obiektu. Czyli korzystanie z obiektu wymaga pierw załadowanie jego klasy z której powstał (szkielet klasy, z którego powstał obiekt).

Chcę uprościć sobie zadanie aby generowany formularz (obiekt) wraz elementami jaki zawiera, regułami, akcjami (i ich właściwościami) był raz generowany. Generowanie obiektu Formularz było by przy tworzeniu tego obiektu. Później jego wynik został zapisany do bazy danych.
Co nie musiał na nowo ładować definicji klas i parametrów, czyli powtarzać całej czynność jaka jest wykonywana przy tworzeniu tego obiektu.

Czy jest jakaś metoda, która by pozwalała na prosty sposób ominięcie tego problemu lub w eleganckie rozwiązanie. Czy jednak należy powtarzać tą całą operacje ładowania każdego elementu jaki został użyty do budowy formularza.

Jeszcze druga sprawa odnośnie samych obiektów.

Co obiekt posiada takiego (czy ma jakiś numer id obiektu), że przy przekazywaniu do różnych klas i wykonywaniu operacji na nim (zmiana jego właściwości) jest widoczna wszędzie, w każdej klasie, która posiada jego instancje.

Pytam się ponieważ, pomyślałem ze do moich akcji, które niestety będą do swojego działania (nie które akcje) wymagały informacji poprzednika (informacje takie jak czy akcja się powiodła).
Wyglądało by to w tę sposób, że do akcji bym raz przekazał instancje obiektu formularza (która zwiera informacje o przebiegu poszczególnych akcji) w postaci parametru dla akcji przy tworzeniu formularza.
Każda akcja np. Baza Danych wymaga informacji konfiguracyjnych (takie jak nazwa bazy danych itd., które przekazywane są przy tworzeniu formularza) a wraz parametrami przekazał instancje formularza. Później jakie kol wiek zmiany by zaszły w historii wykonywania akcji (ich przebiegu) dostępna była by od razu w wybranej akcji, która to wymaga tych danych (myślę, że będzie działać, bo będzie posiadać unikatowy nr id tego obiektu formularza jaki jest nr. Obiektu formularza, ponieważ nie był by na nowo tworzony obiekt formularza tylko odtworzony z bazy danych, za pomocą unserialize).
Czy to zadziała?


Instancja mam na myśli operacje przekazywania obiektu do metody czy do funkcji tak jak zmienną. Nie wiem czy dobrze używam tego słowa w tym znaczeniu!.
Sh4dow
Cytat
Ma problem z cechą jaką posiadają obiekty.
...

No tutaj obejść się tego nie da, żeby mieć obiekt musisz mieć definicje, serializacja nie powiada danych o metodach i wszystkich innych cudach, tylko i wyłacznie dane jakie zapisałes w obiekcie. Połączenia z baza danych tez zostają zerwane. Jak z instancjami obiektów nie wiem, ale podejrzewam że także znikną. ale o to zawsze mogą zadbać "magiczne" metody __sleep() i __wake(), jeśli niczego nie poprzekręcałem.
Ostatecznie obiekt który obsługuje obiekty, może pobierać dane z obiektów z klasy stdClass która jest zbudowana w PHP i służy jedynie jako taki magazyn danych w postaci obiektu. Do tego nie wymagana jest definicja klasy, ale jednocześnie nie posiadasz żadnych metod w obiekcie.

Cytat
...
Jeszcze druga sprawa odnośnie samych obiektów.

Co obiekt posiada takiego (czy ma jakiś numer id obiektu), że przy przekazywaniu do różnych klas i wykonywaniu operacji na nim (zmiana jego właściwości) jest widoczna wszędzie, w każdej klasie, która posiada jego instancje.
...


Pracujesz na pewno na PHP 5.x (i chwała tobie) a tam obiekty przekazywane są jako referencje, zawsze. Wiec zmiana na obiekcie będzie widziana wszędzie gdzie została stworzona referencja. Jeśli chcesz zrobić "klony" obiektu to proponuje ten link klonowanie obiektów, który może ci będzie do czegoś pomocny.

Niestety reszta twojego posta jest trochę zagmatwana i nie do końca rozumiem co miałeś na myśli.
kaniagandzowski
Znalazłem fajny loader do ładowania klas z wybranego katalogu nie padając nazwy pliku (jeśli nie chcemy).
Pozwala w prosty sposób na załadowanie wszystkich klas, nie martwiąc się ze przy dodaniu następnej klasy będę musiał w kodzie dodawać nazwę klasy do załadowania.

Tak więc bez problemu mogę w prosty sposób wywołać metodę lądującą klasy a później nie martwiąc się wywoływać obiekty i wykonywać ich metody (obiekty które były serializowane).

Kod znalazłem na forum. Kod umieścił DjKermit.
http://forum.php.pl/index.php?showtopic=26107&st=160

Tam też znajduję się opis jak uruchomić i jak działa. Nie będzie miał nikt problemów.
Tylko jest problem w Framework CodeIgniter (jak zwykle coś zawsze pójdzie nie tak).
Są problemy z załadowaniem samej klasy tego kodu.

Udało mi się uruchomić lecz w klasie ClassLoader
  1. <?php
  2. function __autoload($class_name) {
  3. $cloader = ClassLoader::getInstance();
  4. $cloader->autoLoad($class_name);
  5. }
  6. ?>

musiałem dodać linijkę sprawdzająca czy nie jest podany parametr CI_DB

  1. <?php
  2. function __autoload($class_name) {
  3. $cloader = ClassLoader::getInstance();
  4. if ($class_name == 'CI_DB') 
  5. {
  6.  return; 
  7. }
  8. $cloader->autoLoad($class_name);
  9. }
  10. ?>


Nie wiem dlaczego się tak dzieje. Bardzo dziwne bo błąd występuję przy ładowaniu klasy
  1. <?php
  2. require_once(CLASS_PATH .'core/ClassLoader.class.php');
  3. ?>

I nie wiem skąd bierze ten parametr, jak nigdzie ja nie podaje.

Druga sprawa.
Jak już pisałem chcę zrobić w klasie akcja właściwość która by posiadała instancje obiektu formularza, który był dodany przy tworzeniu formularza. (Obiekt formularza jest tworzony raz. Później jest on pobierany z bazy i odserializowany do zmiennej, a nie na nowo tworzony)
Chcę zrobić aby ta instancja formularza w akcji było tylko raz podana, przy tworzeniu formularza.
Gdy dana akcja potrzebowała informacje danych pochodzących z obiektu formularza to już by posiadał jego instancje. Więc nie trzeba było by przekazywać do metody danej akcji obiektu tego formularza.
Tak więc akcja by miała na bieżąco dane jakie zachodzą przy różnych operacjach na formularzu.

Ale jest duży problem!
Nie działa jak sobie to wyobraziłem. Zrobiłem test i zmiany jakie zachodzą później w formularzu już nie są widoczne w obiekcie klasy akcji.
  1. <?php
  2. /*powołuje formularz do życia/*
  3. $form = new Formularz(); 
  4. /* tworze akcje – jakiś tam skrypt który będzie prztewarzal dane formularza albo wyswietla formularz lub wysyłał email z danymi formularza  
  5. */
  6. $objAkacja = new akcja();
  7. $_SESSION['form'] = serialize($form);
  8. ?>


Później gdy dodaje np. element do formularza, jest bez problemowo dodany i pamięta poprzednie elementy dodane.
  1. <?php
  2. $objElement = new element; // tworze element
  3. $ objElement ->setValues($values); // nadaje obiektowi jakąś wartość
  4. $form = unserialize($_SESSION['form']); // pobiera instancje obiektu z sessji
  5. $form->addElement($op); // dodaje element do formularza 
  6. $_SESSION['form'] = serialize($form);
  7. ?>

Ale później gdy
  1. <?php
  2. $objAkcja = unserialize($_SESSION['akcja']);
  3. $objAkcja->pobranieWlasciwosciZobiektuFormularza();
  4. ?>

To on ma te właściwości które były dodane przed metodą
  1. <?php
  2. $objAkacja->setAkcja($form);
  3. ?>


Nie ma późniejszy zmian typu dodania nowych elementów do formularza. Aby mieć te zmiany musiał bym non stop przekazywać do akcji instancje tego formularza a ja chce tylko przy tworzeniu akcji przekazać instancje formularza. A później tylko operować na nim w klasie akcja.
Ale nie działa. Jest zerwane połączenie pomiędzy obiektem formularza a obiektem formularza umieszczonym w akcji (jakby już to były inne obiekty. A
używam PHP5
).
Co należy zrobić żeby obiekt formularza był taki samym obiektem w akcji.


Sh4dow napisał, że można zadbać o połączenie pomiędzy tymi instancjami poprzez metody __sleep i __wakeup.
Wiem że są one uaktywniane przy serializacji danego obiektu.
Ale nie wiem co bym miał zrobić aby połączenie pomiędzy tymi obiektami formularza a formularza w akcji wskazywało na ten sam obiekt.
Sedziwoj
Ja bym raczej stosował tego :
http://forum.php.pl/index.php?s=&showt...st&p=178508
Napisanego przez dr_bonzo, choć sam nie korzystam tylko własny mam ale jest znacznie lepszy (moim zdaniem).

co do __sleep i __wake, to robisz tak aby móc przy odtwarzaniu odzyskać stary stan obiektu. Czyli zapisujesz wszystkie właściwości (te nie zdefiniowane na stałe) oraz serializujesz obiekty agregowane, a w tych obiektach też powinieneś mieć zaimplementowaną obsługę serializacji.
kaniagandzowski
Cytat
Ja bym raczej stosował tego :
http://forum.php.pl/index.php?s=&showt...st&p=178508
Napisanego przez dr_bonzo, choć sam nie korzystam tylko własny mam ale jest znacznie lepszy (moim zdaniem).


Chciałem zobaczyć jego kod gdy wybrałem tamten loader, lecz nie mogłem ponieważ nie działa jego link jaki zamieścił z kodem. Ale loader, który mam na razie spełnia moje oczekiwania.

Co do forum to wielka szkoda, że nie ma możliwości załańczania załączników. Nie było by takich sytuacji.

Bardzo dla mnie ważna sprawa nadal nie rozwiązana jest z tymi referencjami obiektów. Czy jak to zwał uchwyty.

Cytat
co do __sleep i __wake, to robisz tak aby móc przy odtwarzaniu odzyskać stary stan obiektu.


Chodzi o właściwości? Ja nie mam problemów z właściwościami, w których są przechowywane jakieś wartości. Po serializacji i unserialize bez problemów są te wartości jakie były zadane.
Ale nie mogę zachować referencji obiektu, która była przekazana do innego obiektu. Bo gdy serilizuje obydwa obiekty i z powrotem. To obiekt tej pierwszej klasy, który był przekazany do drugiej klasy jako referencja już nie są tymi samymi obiektami. Bo już w tym momencie zmiany naniesione w obiekcie pierwszym już nie są widoczne od razu w tym drugim obiekcie, który miał jego obiekt (referencje) to taki Klon obiektu.


Coś czuję, że nie rozwiąże tego problemu bo nigdzie czegoś na forum i w książkach nie znalazłem. Więc będę musiał do metody obiektu akcji przekazać obiekt formularza. Co w tym momencie mniej się mi podoba, nie będzie kod taki elegancki i łatwy. Będzie trzeba zawsze zawracać sobie głowę o dopilnowanie obiektu formularz, był zawsze przekazany do metody akcji.
kaniagandzowski
Rozwiązałem problem dotyczący zachowania referencji obiektów po serializacji. Tak więc jeśli zamierzasz serializować obiekt, który zawiera referencje do innych obiektów, musisz serializować obiekty w tym samym czasie, żeby nie straci referencje kiedy obiekty są unserilizowane.
  1. <?php
  2. $a = new ClassA(); 
  3. $b = new ClassB($a); //$b zawiera referencje obiektu $a;
  4.  
  5. $s1=serialize($a);
  6. $s2=serialize($b);
  7.  
  8. $a=unserialize($s1);
  9. $b=unserialize($s2);
  10. ?>


Po takim zabiegu referencja obiektu $a w obiekcie $b jest stracona i obiekt staje się innym obiektem klasy A

Należy użyć to:
  1. <?php
  2. $buf[0]=$a;
  3. $buf[1]=$b;
  4. $s=serialize($buf);
  5. $buf=unserialize($s);
  6. $a=$buf[0];
  7. $b=$buf[1];
  8. ?>

Wszystkie referencje są zachowane
Sedziwoj
Ja bym na Twoim miejscu zastosował rekurencyjną serializację, powinno to działać i być ładniejsze.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.