Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

2 Stron V   1 2 >  
Reply to this topicStart new topic
> Klasa generująca formularz - dobre praktyki
Turson
post 24.08.2014, 11:22:41
Post #1





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Hej,
mam małą zagwozdkę logiczną w tworzeniu klasy generującej formularz. Teoretycznie takową klasę napisałem, ale mam kilka wątpliwości.
Od razu dodam, że aplikacja oparta jest o MVC.
Skrócony kod klasy:
  1. <?php
  2. class MyForm {
  3.  
  4. protected $_action;
  5. protected $_method = 'GET';
  6. protected $_html;
  7.  
  8. public function setAction($value) {
  9. $this->_action = $value;
  10. }
  11.  
  12. public function setMethod($value) {
  13. $this->_method = $value;
  14. }
  15.  
  16. public function startForm(array $options = array()) {
  17. $optionsHtml = '';
  18. if (count($options)) {
  19. foreach ($options as $key => $value) {
  20. $optionsHtml .= ' ' . $key . '="' . $value . '"';
  21. }
  22. }
  23. $this->_html = '<form action="' . $this->_action . '" method="' . $this->_method . '"' . $optionsHtml . '>';
  24. }
  25.  
  26. public function endForm() {
  27. $this->_html .= '</form>';
  28. }
  29.  
  30. public function createElement($type, $name, $label = null, $value = null, array $values = array(), array $options = array()) {
  31. // wygenerowanie kodu HTML input/textarea/select itd.
  32. $this->_html .= $html;
  33. }
  34.  
  35. public function render() {
  36. return $this->_html;
  37. }
  38.  
  39. }


Formularz generuję w widoku, np.
  1. <?php
  2. $form = new MyForm();
  3. $form->setMethod('POST');
  4. $form->startForm();
  5. $form->createElement('text', 'login', 'Nickname');
  6. $form->createElement('password', 'password', 'Password');
  7. $form->createElement('submit', 'submit', null, 'Register new account');
  8. $form->endForm();
  9. echo $form->render();
  10. ?>
  11. Jakiś dalszy tekst


Tutaj mam taką rozkminę, czy formularz lepiej generować w widoku jak powyżej, czy w osobnej klasie, np. application/forms/RegisterForm.php
Pierwsze rozwiązanie widziałem w Yii, a drugie w Zendzie.

A teraz sprawa najważniejsza. Posiadam osobną klasę walidującą pola formularza wg. zasad ustalonych w modelu. Tego nie będę pokazywał, bo nie ma związku. Chcę mieć dostęp do informacji o wszystkich walidujących polach w klasie walidacyjnej.
Kontroler:
Jeżeli naciśnięto przycisk, zbieram dane z formularza, pobieram reguły walidacji i przekazuję to do klasy walidującej np. $this->validate($data, $rules)
$data zawiera tablicę $_POST. W klasie walidacyjnej następuje sprawdzenie poprawności przesłanych danych wg. $rules. W tejże klasie muszę mieć dostęp do tego jaki dane pola ma $label - i to jest cały problem. Nie mam za bardzo pomysłu jak się do tego dostać. Podejrzewam, że w klasie MyForm i metodzie createElement() muszę zapisywać dane każdego tworzonego pola i zapisywać np. w statycznej własności klasy $formData. To nie jest problem. Tylko odwołanie się do np. MyForm::$formData z klasy walidacyjnej przecież nie pobierze tych danych, bo już ich tam nie "będzie".
A po co mi wiedzieć jaki label ma dane pole? Ano po to, że jak nie przejdzie walidacji to do tablicy KlasaWalidacyjna->errors mogę dodać $label.' can not be empty'

Ten post edytował Turson 24.08.2014, 11:23:12
Go to the top of the page
+Quote Post
Pyton_000
post 24.08.2014, 19:01:59
Post #2





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Wypluwasz errory w tablicy z kluczami z name pola i w widoku robisz coś na zasadzie
  1. if($errors->has('email') {
  2. echo $errors->get('email)->first();
  3. }


IHMO startForm powinno zbierać wszystkie opcje włącznie z method i action, skoro już robisz przypisanie po tablicy.
Do tego robisz małe sprawdzanie: jeżeli brak to nic nie ustawiasz w action, a method domyślnie POST
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 07:59:42
Post #3





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Z walidacją i wyświetleniem komunikatów nie mam problemu, tylko klasa walidacyjna otrzymuje tablicę $_POST nazwa_pola=>wartość, a potrzebuję dodatkowo na podstawie nazwa_pola pobrać jego label.
Czyli np. w MyFrom->createElement()
  1. $labels[$name] = $label;

gdzieś to przechować, żebym mógł z poziomu klasy walidacyjnej się do tego odwołać <- i to nie wiem jak.
Go to the top of the page
+Quote Post
Pyton_000
post 25.08.2014, 08:10:40
Post #4





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


A na co Ci to? Do reguł validujących dodajesz sobie tekst w przypadku błędu i po problemie.
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 08:14:59
Post #5





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


To jest jakieś rozwiązanie, ale chciałbym, żeby w aplikacji były defaultowe komunikaty, a jak się doda w regułach customowy komunikat to wtedy on się wyświetli.
Go to the top of the page
+Quote Post
Pyton_000
post 25.08.2014, 08:27:21
Post #6





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


No to dodajesz sobie w klasie Validującej odwołanie do jakiejś tablicy z tekstami (klucz nazwa pola) i sprawdzasz czy jest customowy tekst w rules a jak nie to z tłumaczeń.
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 08:36:06
Post #7





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Tablicę rules mam zbudowaną w sposób:
  1. $rules = array(
  2. 'register' => array(
  3. 'NAME POLA' => array(
  4. 'required' => true
  5. ),
  6. 'NAME POLA' => array(
  7. 'min_length' => 3
  8. ),
  9. )
  10. );

czyli namespace formularza (tak sobie wszystko definiuję takim słowem kluczem, ale mniejsza o to) -> name pola -> reguły
Tutaj nie mam żadnego problemu jak już wspomniałem. Chcę gdzieś zapisać do każdego nowego elementu tablicę $tablica[name_pola]=>[label_pola]
I nie wiem gdzie to zapisać, żeby potem z klasy walidacyjnej odwołać się do tablicy, jeżeli mam regułę z name_pola w paremetrze, to odwołam się do $tablica[name_pola] i mam jego label. Chodzi tylko i wyłącznie o to.
Go to the top of the page
+Quote Post
Pyton_000
post 25.08.2014, 08:42:54
Post #8





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


No to jedno rozwiązanie z ... :

- Robisz sobie Interface Input
- Implementujsz każdy z inputów nadając im atrybuty publiczne

Klasa Form tworzy nowe obiekty typu Input i wrzuca do jakiejś kolekcji.

W klasie validującej robisz coś na zasadzie:

$label = Form::get('email')->label;
lub coś w ten deseń.

Ten post edytował Pyton_000 25.08.2014, 08:43:31
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 08:58:03
Post #9





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Dobrze rozumiem?
  1. <?php
  2. class Form{
  3.  
  4. protected static $_data = array();
  5.  
  6. function createElement($label,$name, $value = null){
  7. // something
  8. $data = new stdClass();
  9. $data->label = $label;
  10. $data->value = $value;
  11. self::_putData($name,$data);
  12. }
  13.  
  14. protected static function _putData($name, $data){
  15. self::$_data[$name] = $data;
  16. }
  17.  
  18. public static function get($name){
  19. $data = self::$_data;
  20. return isset($data[$name]) ? $data[$name] : false;
  21. }
  22.  
  23. }

  1. <?php
  2. $form = new Form();
  3. $form->createElement('Nickname', 'login');

  1. <?php
  2. class Validate{
  3.  
  4. function doSth(){
  5. $input = Form::get('login');
  6. var_dump($input);
  7. }
  8.  
  9. }

  1. <?php
  2. $validate = new Validate();
  3. $validate->doSth();
Go to the top of the page
+Quote Post
Pyton_000
post 25.08.2014, 09:08:59
Post #10





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Cos w ten deseń wink.gif Tylko zamiast false zwracaj albo null albo array() jeżeli oczekujesz tablicy.
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 09:12:43
Post #11





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Już próbowałem już takiego rozwiązania i jak z klasy Validation odwoływałem się do Form::get(), to zwracało mi zawsze pustą tablicę. Dlaczego?
Wydaje mi się, że przy odwołaniu statycznym tablica $data klasy Form się czyściła. Ba, kombinowałem z Singletonem, bo myślałem że to kwestia instancji.

Ten post edytował Turson 25.08.2014, 09:14:49
Go to the top of the page
+Quote Post
Pyton_000
post 25.08.2014, 09:58:55
Post #12





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Czysta bo to kolejny request a więc dane nie są znane.
Przy odbieraniu danych z $_POST wrzuć to w Form, wygeneruj formularz na nowo ale wstawiając value dla inputów i zadziała.

Chociaż i tak uważam że to już kombinowania. Najprościej jak mówiłem, olej label i podziałaj z tekstami validacji
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 10:09:44
Post #13





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Pisanie własnego komunikatu o błędzie dla każdej reguły jest jak dla mnie średnim pomysłem, dlatego wolałbym sobie ułatwić życie i wprowadzić automatyczne teksty, jeżeli nie podano własnego. Jak będę miał czas to wrócę do tego kodu i napiszę w tym temacie, gdyby jeszcze coś było smile.gif
Go to the top of the page
+Quote Post
memory
post 25.08.2014, 11:10:01
Post #14





Grupa: Zarejestrowani
Postów: 616
Pomógł: 84
Dołączył: 29.11.2006
Skąd: bełchatów

Ostrzeżenie: (0%)
-----


Może coś w ten deseń

  1. $rules = array(
  2. 'name' => array( 'required' => true,'min' =>3),
  3. 'link' => 'active_url',
  4. );
  5.  
  6. //Tworzysz sobie np tablice domyślnych nazw:
  7.  
  8. $validation =
  9. array("required" => "The {label} must be required.",
  10. "active_url" => "The {label} is not a valid URL.",);
  11.  
  12. W klasie walidującej
  13.  
  14. //tablica validation
  15. protected $validation = array();
  16. protected $messages = array();
  17.  
  18. function validate($attribute,$rule){
  19.  
  20. //sprawdzanie czy poprawnie itd ($attribute = name, $rule = 'required ')
  21. $this->addError($attribute, $rule);
  22.  
  23. }
  24.  
  25. function addError($attribute, $rule)
  26. $this->message[$attribute] = $this->getMessage($attribute, $rule,$this->validation);
  27. }
  28.  
  29. function getMessage($attribute, $rule, $source){
  30.  
  31. if (isset($source[$rule])) return $source[$rule];
  32.  
  33. }


Ten post edytował memory 25.08.2014, 11:21:46
Go to the top of the page
+Quote Post
Turson
post 25.08.2014, 13:56:49
Post #15





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


@memory, ale co z "{label}" ? Nie stosujesz tutaj żadnej podmiany {label}=>$label
Go to the top of the page
+Quote Post
RiE
post 25.08.2014, 14:24:24
Post #16





Grupa: Zarejestrowani
Postów: 97
Pomógł: 45
Dołączył: 5.05.2010

Ostrzeżenie: (0%)
-----


Może zamiast metody z 6 argumentami lepiej będzie przekazać do niej 1 argument w postaci tablicy w której będą się znajdowały wszystkie niezbędne informacje. Bo co w przypadku jeśli chce dodać klasę albo ID do pola tekstowego? Który parametr za to odpowiada? Ostatni? A jak będę chciał dodać klasę do label to też ostatni?
  1. public function createElement($type, $name, $label = null, $value = null, array $values = array(), array $options = array()) {
  2. // wygenerowanie kodu HTML input/textarea/select itd.
  3. $this->_html .= $html;
  4. }


Lepszym wyjściem moim zdaniem byłoby:

  1. protected $inputs = array();
  2.  
  3. public function createElement($input)
  4. {
  5. $this->inputs[$input['input']['name']] = $input;
  6. }
  7.  
  8. public function getElement($name)
  9. {
  10. return $this->inputs[$name];
  11. }
  12.  


  1. $form = new MyForm();
  2. $form->createElement(array(
  3. 'input' => array(
  4. 'name' => 'pole_1',
  5. 'type' => 'text'
  6. 'options' => array(
  7. 'value' => 'Domyślna wartość',
  8. ...
  9. ),
  10. 'attributes' => array(
  11. 'id' => 'pole_1'
  12. 'class' => 'form-input',
  13. 'placeholder' => 'Tekst...',
  14. ...
  15. )
  16. ),
  17. 'label' => array(),
  18. //'errors' => array()
  19. ));


Następnie przekazujesz ten formularz do widoku i w widoku Dekorator odpowiada za poprawne wyświetlenie.

  1. <div class="my-form">
  2. <?php echo $this->renderLabel($form->getElement('pole_1)); ?>
  3. <?php echo $this->renderInput($form->getElement('pole_1)); ?>
  4. <?php echo $this->renderErrors($form->getElement('pole_1)); ?>
  5. </div>


W renderLabel() dajesz input i tworzysz label na podstawie klucza label. W renderInput() tworzysz input na podstawie klucza input. A podczas walidacji dodajesz klucz error i jeżeli nie jest pusty to wyświetlasz błedy pod polem w którym się pojawiły.

W $rules przydałoby się jeszcze przekazać informację z jakiego validatora chcesz skorzystać bo może się zdarzyć tak że dwa validatory będą miały dwa takie same klucze np. min_length albo pattern w którym będzie wyrażenie Regexp i co wtedy? Skąd wiadomo z jakiego validatora skorzystać?
Go to the top of the page
+Quote Post
memory
post 25.08.2014, 14:52:52
Post #17





Grupa: Zarejestrowani
Postów: 616
Pomógł: 84
Dołączył: 29.11.2006
Skąd: bełchatów

Ostrzeżenie: (0%)
-----


  1. miało być {attribute}; :).
  2.  
  3. function addError($attribute, $rule){
  4. $message = $this->getMessage($attribute, $rule,$this->validation);
  5. $message = str_replace('{attribute}', $this->getAttribute($attribute), $message);
  6. $this->message[$attribute] = $message;
  7. }
  8.  
  9. function getAttribute($attribute){
  10.  
  11. // tutaj porownujesz z danymi z Post czy atrybut posiada label
  12.  
  13. }
  14.  
  15.  

Go to the top of the page
+Quote Post
kayman
post 25.08.2014, 15:27:59
Post #18





Grupa: Zarejestrowani
Postów: 556
Pomógł: 40
Dołączył: 20.07.2012
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


imo klasa generująca formularz to w ogóle zła praktyka, od tego są widoki w Twigu, Smarty etc. ale ..... oto mój burdelik smile.gif

  1.  
  2. // formularz kontaktowy
  3.  
  4. if ($error = Main::getSessionValue('error')) {
  5. Main::unsetSessionValue('error');
  6. }
  7.  
  8. $form = new Form();
  9. $form->addFile(array('type' => 'text', 'name' => 'name', 'value' => $error['name'], 'id' => 'name', 'description' => 'Imię i nazwisko lub nazwa firmy:', 'validation' => 'vname'));
  10. $form->addFile(array('type' => 'text', 'name' => 'mail', 'value' => $error['mail'], 'id' => 'mail', 'description' => 'Adres email:', 'validation' => 'vmail'));
  11. $form->addFile(array('type' => 'textarea', 'name' => 'message', 'value' => $error['message'], 'id' => 'message', 'description' => 'Treść wiadomości:', 'validation' => 'vmesage'));
  12. $form->addFile(array('type' => 'text', 'name' => 'captcha', 'id' => 'captcha', 'description' => $captcha->getCaptcha(), 'validation' => 'vcaptcha'));
  13. $form->addFile(array('type' => 'button', 'class' => 'art-button', 'value' => 'Wyślij'));
  14. $form->addFile(array('type' => 'hidden', 'name' => 'action', 'value' => 'email'));
  15. $this->content = $form->getForm();
  16.  


jak widać dobra praktyka to raczej unikać takich leniwców biggrin.gif

Ten post edytował kayman 25.08.2014, 15:34:51
Go to the top of the page
+Quote Post
Pyton_000
post 25.08.2014, 15:39:23
Post #19





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


A wystarczyłoby
  1. new InputText('name', val|null, array('params'=>'sss'));
Go to the top of the page
+Quote Post
Turson
post 8.09.2014, 14:21:52
Post #20





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Co myślicie o rozwiązaniu: w $form->createElement() w argumencie przekazuję tablicę $rules = array('required' => true), w klasie Form następuje zapisanie w sesji reguł walidujących przy konkretnych polach. Z sesji odczytam, sprawdzę i czyszczę sesję.
Zapiszę w sesji: reguły walidujące i dane pola (label, name, value itd)

Ten post edytował Turson 8.09.2014, 14:25:43
Go to the top of the page
+Quote Post
aniolekx
post 9.09.2014, 11:37:35
Post #21





Grupa: Zarejestrowani
Postów: 340
Pomógł: 46
Dołączył: 31.07.2009
Skąd: A

Ostrzeżenie: (0%)
-----


moim zdaniem możesz sobie tworzyć formularze dynamicznie w widoku, ale jeżeli chcesz do nich bindować dane to już nie tedy droga.
Go to the top of the page
+Quote Post
Turson
post 9.09.2014, 11:42:13
Post #22





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


To jakie rozwiązanie, żeby budować formularz obiektem mniej więcej jak podałem, a dodatkowo po wysłaniu formularza mieć dostęp m.in. do labela podając np. jego 'name' ?
Go to the top of the page
+Quote Post

2 Stron V   1 2 >
Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 29.06.2025 - 11:32