26
Luty
czwartek, 26 Luty 2009

Jeszcze lepsza walidacja formularzy

Kilka miesięcy temu podjąłem się napisania mechanizmu tworzącego walidację JavaScript formularzy na podstawie tablicy walidacji tworzonej w modelu. Efekty opisałem w artykule “Automatyczna walidacja JavaScript w CakePHP“. Mechanizm ten wykorzystałem w kilku projektach i znakomicie się sprawdził. Przez ten czas Cake ewoluował do wersji 1.2.1 co przez co musiałem przygotować kolejną przeróbkę hepera form. Postanowiłem też przepisać kod JavaScript oraz nieco ulepszyć cały mechanizm.

Na początku wprowadziłem zmiany w mechanizmie walidacji Cake’a. W niektórych formularzach istniała potrzeba sprawdzenia np. kodu pocztowego, ale był on opcjonalny. Zmieniłem mechanizm walidacji tak, by sprawdzał pola jedynie gdy zostały wypełnione. W ten sposób można nadać polu np. maksymalną i minimalną długość, wymusić wpisywanie samych liczb i jednocześnie pole może być opcjonalne.  Można to wykorzystać np. przy wpisywaniu numeru telefonu. Jeżeli pole maspełniać wszystkie powyższe reguły i być wymagane wystarczy dodać metodę required.

Dodałem także kilka nowych reguł:

  • uniqueLogin – sprawdzenie czy jest unikalnym loginem
  • uniqueEmail – podobnie jak wyżej
  • confirmPassword – wymagane potwierdzenie hasła
  • tags – takgi składające się z liter oddzielone przecinkami
  • datatime – data i czas
  • int – tylko liczby
  • float – liczba zmiennoprzecinkowa
  • postal – kodpocztowy
  • phone – telefon 11to cyfrowy

Pierwsze dwie metody działają nieco inaczej od pozostałych, ale opiszę to później.

Przykład użycia w modelu:

class User extends AppModel {

	var $name = 'User';
	var $displayField = 'login';

	var $validate = array(
		'login' => array
		(
			'required' => array(
				'rule' => array('required'),
				'on'=>'create'
			),
			'between' => array(
				'rule' => array('between', 6, 16),
				'on'=>'create'
			),
			'login' => array(
				'rule' => array('login'),
				'on'=>'create'
			),
			'uniqueLogin' => array(
				'rule' => array('uniqueLogin'),
				'on'=>'create'
			),
		),
		'email' => array
		(
			'required' => array(
				'rule' => array('required'),
				'on'=>'create'
			),
			'email' => array(
				'rule' => array('email'),
				'on'=>'create'
			),
			'uniqueEmail' => array(
				'rule' => array('uniqueEmail'),
				'on'=>'create'
			),
		),
		'password' => array
		(
			'required' => array(
				'rule' => array('required'),
				'on'=>'create'
			),
			'minLength' => array(
				'rule' => array('minLength', 6),
			),
		),
		'confirm_password' => array
		(
			'required' => array(
				'rule' => array('required'),
				'on'=>'create'
			),
			'minLength' => array(
				'rule' => array('minLength', 6),
			),
			'confirmPassword' => array(
				'rule' => array('confirmPassword'),
			),
		),
	);
}

Następnie wprowadziłem wprowadziłem zmiany w helperze form. Podobnie jak poprzednia wersja, w metodzie end dodaje on kod JavaScript dodający do formularza metody jego walidacji. Istotną zmianą jest to, że bierze on pod uwagę atrybut “on” danej metody walidacji. atrybut ten został niedawno wprowadzony i może przyjmować wartości create lub update. Więcej o tym można przeczytać w dokumentacji: http://book.cakephp.org/view/127/One-Rule-Per-Field.  Tak więc helper form bierze pod uwagę czy dana metoda ma być brana pod uwagę w danym formularzu.

Największe zmiany wprowadziłem w JavaScripcie. Do jego użycia wymagane jest dołączenie biblioteki MooTools lub jQuery. Skrypt zawiera obiekt Validation. Metoda add dodaje reguły do poszczególnych pól. Wyświetlaniem i ukrywaniem komunikatów zajmują się metody showMessage i hideMessage. Można je prosto zmodyfikować by wyświetlać komunikaty w inny sposób, np. z efektem slideOut.

Opisując ten obiekt wrócę do metod walidacji uniqueLogin i uniquEmail. W tracie wypełniania formularza sprawdzają one czy w bazie danych już jest użytkownik o podanym loginie czy adresie email. Odbywa się to za pomocą AJAX’a. Do tego wymagane są metody w kontrolerze. Wygląda ją one następująco:

	function check_login($data)
	{
		$this->layout='ajax';
    	if ($this->User->findByLogin($data))
			$this->set('response', 'err');
		else
			$this->set('response', 'ok');
	}

	function check_email($data)
	{
		$this->layout='ajax';
		if ($this->User->findByEmail($data))
			$this->set('response', 'err');
		else
			$this->set('response', 'ok');
	}

Sprawdzają jedynie czy w bazie istnieją już podane wpisy i zwracają OK lub ERR.

Obiekt walidacji został wzbogacony również o możliwość sprawdzania nie tylko inputów typu text czy textarea ale również pozostałych typów oraz selectów. Potrafi także sprawdzić pole daty składające się z 3 lub 5ciu selectów oraz grupę radio buttonów.

Kody źródłowe można pobrać stąd:

http://www.reboo.pl/files/download/validation-v2.zip wersja wykorzystująca MooTools

http://www.reboo.pl/files/download/validation-v2j.zip wersja wykorzystująca jQuery

a demo można zobaczyć pod adresem:

http://www.reboo.pl/files/demos/cake/www/users/add

Proponuję jako login wpisać np. reboo13, ponieważ taki już istnieje.

czwartek, 26 Luty 2009 
Kategoria: AJAX, CakePHP, JavaScript
Możesz śledzić ten wpis przez RSS 2.0 lub dodać komentarz.

Liczba komentarzy: 3

  1. Fajnie działa ta walidacja. Ale jest jeden denerwujący bug. Jeśli na stronie pojawią się dwa formularze to walidacja ich nie odróżnia.
    Np.
    1) Strona z logowaniem i belka logowania. Klikamy submit na stronie a sprawdza nam dane z belki.
    2) Teraz też obserwuje problem z modalboxem, jeśli mam okienko z logowaniem w modalboxie, a pod spodem jest strona z rejestracją. To po klinięciu w submit z modalboxa, wyświetla błędy i w modalboxie i na stronie tej pod spodem z rejestracją.

  2. Wiem, że niestety tak to działa, bo nie miałem też takiej potrzeby, żeby umieszczać i sprawdzać kilka formularzy na stronie. Jeśli jest na niej formularz logowania, używam do niego prostego skryptu, który tylko sprawdza tylko, czy oba pola są wypełnione.
    Postaram się to uwzględnić w następnej wersji.

  3. Rozwiązanie problemu kilku formularzy:

    validation.js

    for (it in Validation.formFields)
    {
    if (Validation.formFields[it][0])
    {
    if (Validation.formFields[it][0]["tag"])
    {
    // efik begin
    validationAction = Validation.formFields[it][0]["params"].param1;
    commonId = formId.slice(0,-4);

    if(validationAction == commonId)
    {
    // efik end
    for(var i=0; i array
    (
    ‘required’ => array( ‘rule’ => array(‘required’) ),
    ‘between’ => array( ‘rule’ => array(‘between’, 2, 20) ),
    ‘username’ => array( ‘rule’ => array(‘username’) ),
    ‘uniqueUsername’ => array( ‘rule’ => array(‘uniqueUsername’) ),
    ),

    ‘email’ => array
    (
    ‘required’ => array( ‘rule’ => array(‘required’) ),
    ‘maxLength’ => array( ‘rule’ => array(‘maxLength’, 60) ),
    ‘email’ => array( ‘rule’ => array(‘email’) ),
    ‘uniqueEmail’ => array( ‘rule’ => array(‘uniqueEmail’) ),
    ),

    app_model.php

    function setValidateRules($validate_fields = null, $controller = null, $action = null)
    {
    if($validate_fields)
    foreach ($validate_fields as $field)
    if (isset($this->validateRules[$field]))
    {
    $model = substr($controller, 0, strlen($controller) – 1);
    $model_action = Inflector::camelize($model . ‘_’ . $action);
    $this->validate[$field]['action'] = array( ‘rule’ => array(‘action’, $model_action) );
    foreach($this->validateRules[$field] as $k=>$v)
    $this->validate[$field][$k] = $this->validateRules[$field][$k];
    }
    }

    app_controller.php

    function setValidateRules($validate_fields = null)
    {
    $this->User->setValidateRules($validate_fields, $this->params['controller'], $this->params['action']);
    }

    Użycie users_controller.php

    function register($referrer = null)
    {
    // ustawiamy które pola idą do tablicy walidacji
    $this->setValidateRules(array(‘username’, ‘email’))

Zostaw komentarz

Home Home O mnie O mnie Efekty
JavaScript, jQuery, AJAX
Efekty
Inspiracje
Ciekawe strony WWW
Inspiracje
Programowanie
CakePHP, PHP, MySQL
Programowanie
SEO
Wyszukiwarki
SEO
Web Building
HTML i CSS
Web Building
Projekty Linki Kontakt Kontakt