Validation by Rules: adatellenőrzés más megközelí­tésben

Mindenki használta már a cake mezőellerőrző rendszerét. Ez elkerülhetetlen, hiszen annyira jó🙂 . Az 1.1-ben még viszonylag egyszerű rendszerről volt szó, hiszen egy mezőhöz csak egy szabályt lehetett felállitani és az is csak egy regex lehetett. A most készülő 1.2 alatt több, előre beépitett ellenőrzési szabályt is fel lehet állitani, sokkal jobb paraméterezéssel.

Mindez nagyon hasznos, viszont nagyon gyorsan belebotlottam ennek hátrányaiba:

  • a beépitett ellenőrző eljárások a Validation osztály beépitett metódusai, elég nehezen bővithetők
  • szerettem volna egy mezőről eldönteni, hogy annak értéke egyedi-e (unique); ez sajnos most nem támogatott

Mindez arra ösztönzött, hogy kidolgozzak egy, a mostanival teljes mértékben kompatibilis (csak többet tudhat) rendszert, ami szabadon bővithető, és szabadon operálhatok benne a model adta lehetőségekkel is.

Az ötlet nagyon egyszerű: egy beépitett osztály metódusai helyett minden szabályt kezeljük külön objektumként (Rule). Hogy miért jó ez? Mert igy bárki szabadon elkészitheti a saját szabályait és azt a továbbiakban bármikor szabadon használhatja.

Ehhez átvettem a cake Model osztály invalidateFields() metódus logikájának egy részét (leginkább a paraméterek kezelése és értelmezése), nagy részét pedig lecseréltem a saját szabálykezelővel. A szabályok egy része (szinte) teljesen megegyezik a Validate osztály megfelelő metódusával (nem volt célom újra feltalálni a kereket, de nagyon közel akartam maradni az eredeti működéshez).

Vágjuk a közepébe. A használata nagyon egyszerű:

  1. a zip filet le kell menteni és kitömöriteni a cake projekt könyvtárában
  2. a modelben létre kell hozni egy a $validat változóhoz hasonló $rules tömböt. A szabályok megadása teljesen hasonló a beépitett $validate változóhoz.
  3. a controllerben a $this->Model->validates() helyett a $this->Model->validateByRules() metódust kell használni. Létrehoztam egy saveByRules() metódust is adatok mentéséhez, mely a validateByRules()-t használja adatellenőrzésre.

Maga a kód 3 fontos részből áll:

  • /app/rule.php: ez tartalmazza a szabály osztályok ősét, minden szabálynak ebből az osztályból kell származni.
  • /app/app_model.php: a model működését terjeszti ki a fenti két metódussal. Ha valaki saját app_model.php-t használ, annak a bővitéseket kell bemásolnia a saját kódja mellé.
  • /app/models/rules: ebben a könyvtárban találhatók a felhasználható szabályok, illetve itt kell létrehozni új szabályt.

Használata

Mint irtam, a $rules felépitése teljes mértékben azonos az eredeti $validate-ével. Minden paraméter (rule, message, allowEmpty, on, required) ami használható az eredetiben az itt is pontosan ugyanazt a funkciót látja el.

Egy példán keresztül szeretném bemutatni a működést. Vegyünk egy felhasználó kezelőt, ahol meg lehet adni a username, company, email mezőket.

class User extends AppModel {
  var $name = 'User';

  // Egyszerű szabályok hozzárendelése a mezőkhöz
  var $rules = array(
    'username' => VALID_NOT_EMPTY,
    'company'  => VALID_NOT_EMPTY,
    'email'    => VALID_EMAIL
  );
}

A fenti példakód működik 1.1 alatt is, itt is, célunk viszont ennél kissé bonyolultabb szabályok megadása. Az 1.2 alatti $validate – $this->Model->validates() működését nem részletezném, viszont mindenkinek ajánlom a bakery-n található erre vonatkozó cikk átolvasást és kipróbálását.

Szóval az elvárások a mezőkkel kapcsolatban:

  • username:
    • ne lehessen rövidebb, mint 6 karakter
    • ne lehessen hosszabb, mint 20 karakter
    • csak alfanumerikus lehet
  • company: mint a username, csak nem korlátozzuk a hosszt, pusztán az az elvárás, hogy ne legyen üres, viszont ezt az alfanumerikus szabály megteszi nekünk.
  • email: email formátumú legyen

A legszigorúbb jelölést alkalmazva:

class User extends AppModel {
  var $name = 'User';

  var $rules = array(
    'username' => array(
      array(
        'rule' => array('alphanumeric'),
        'message' => 'Szövegesnek kell lennie!'
      ),
      array(
        'rule' => array('min_length', 6),
        'message' => 'Legalább 6 karaketer'
      ),
      array(
        'rule' => array('max_length', 20),
        'message' => 'Legfeljebb 20 karaketer'
      )
    ),
    'company' => array(
      array(
        'rule' => array('alphanumeric'),
        'message' => 'Szövegesnek kell lennie!'
      ),
    ),
    'email'    => array(
      array(
        'rule' => array(VALID_EMAIL),
        'message' => 'Email formátumu kell legyen!'
      )
    )

  );
}

Persze ez ebben a formában kicsit erőltetett, törekedjünk az egyszerűségre. Ha a szabályok között adjuk meg a hibaüzenetet, akkor kevésbé lehet egyszerű:

class User extends AppModel {
  var $name = 'User';

  var $rules = array(
    'username' => array(
      array(
        'rule' => 'alphanumeric',
        'message' => 'Szövegesnek kell lennie!'
      ),
      array(
        'rule' => array('min_length', 6),
        'message' => 'Legalább 6 karaketer'
      ),
      array(
        'rule' => array('max_length', 20),
        'message' => 'Legfeljebb 20 karaketer'
      )
    ),
    'company'  => array(
      array(
        'rule' => 'alphanumeric',
        'message' => 'Szövegesnek kell lennie!'
      ),
    ),
    'email'    => array(
      array(
        'rule' => VALID_EMAIL,
        'message' => 'Email formátumu kell legyen!'
      )
    )

  );
}

Hát ez nem sokkal lett egyszerűbb. Ha úgy döntünk, hogy a hibaüzenetet majd a view-ban, a $form->input() alatt adjuk meg, akkor viszont megváltozik a helyzet:

class User extends AppModel {
  var $name = 'User';

  var $rules = array(
    'username' => array(
      'alphanumeric',
      array('min_length', 6),
      array('max_length', 20),
    ),
    'company'  => 'alphanumeric',
    'email'    => VALID_EMAIL
  );
}

Na ez már sokkal barátibb (a hibaüzenet viewban való megjelenitéséről a fentebb linkel bakery-s cikkben található leirás a mérvadó). A szabályok megadásakor a szabálynevek megadhatók CamelCased és underscore formában is. A fenti példában az underscore változatot használtam, de az alábbi kód a fentivel ekvivalens:

class User extends AppModel {
  var $name = 'User';

  var $rules = array(
    'username' => array(
      'Alphanumeric',
      array('MinLength', 6),
      array('MaxLength', 20),
    ),
    'company'  => 'Alphanumeric',
    'email'    => VALID_EMAIL
  );
}

Mivel a rendszer névkonvertálást végez, ezért elég a nevekben a szóhatárokat jelölni vagy nagybetű, vagy aláhúzás használatával. (az eredeti 1.2 alatt a példákban ‘maxLength’ szerepel, ez itt is használható)

Saját szabály (Rule) készitése

Nagyon egyszerű: a /app/models/rules könyvtár alatt létre kell hozni egy .php filet, mely a szabály nevével egyezik meg aláhúzásos formában. Ebben definiálni kell a szabály osztályát NagyBetűs formában (, mint a file neve), csak hozzá kell csapni a ‘Rule’ szót a végére. Az osztálynak a Rule osztályt kell kiterjesztenie.

Az osztályban egyetlen metódust kell definiálni: validate(ÉRTÉK_AMIT_ELLENŐRZíœNK, paraméterek,…)

Lássunk a max_length szabály felépitését (/app/models/rules/max_length.php):

class MaxLengthRule extends Rule {
  function validate($value, $length) {
    if (!is_numeric($length)) {
      return false;
    }

    $return = (strlen($value) < = $length);
    return $return;
  }
}

A validate() metódus első paramétere mindig az az érték, amelyet ellenőrzünk, a továbbiak pedig, amit a szabály leirásánál megadtunk.

A fenti példánál maradva: mikor a rendszer a max_length szabályt ellenőrzi, akkor a $value az mindig az átadott mező értéke (jelenesetben a username), a $length pedig most 20. Ha további paramétereket adtunk volna meg, akkor azok teljes mértékben figyelmen kivül maradnak.

A Rule osztály két változóval érkezik, ami rendkivül hasznos lehet:

  • $this->model: ezen keresztül lehet elérni az aktuális modelt.
  • $this->fieldName: annak a mezőnek a neve, amelyen éppen az ellenőrzést végezzük.

Hogy ezek miért jók, arra a unique szabály hoznám fel példának minden további magyarázat nélkül (a unique szabály ellenőrzi, hogy a mező értéke szerepel-e az adattáblában. ajánlott az ‘on’=>’create’ paraméterrel együtt használni.)

class UniqueRule extends Rule {
  function validate($value) {
    $return = $this->model->isUnique(array($this->fieldName => $value));
    return $return;
  }
}

További hasznos tudnivalók

A rendszert nevezzük 0.1-es verziónak, mindenki csak saját felelősségre használja. Ezen kivül a szabályok listája még koránt sem teljes.

Röviden ennyi mára.

Kategória: cakephp, php
Címke: ,
Közvetlen link a könyvjelzőhöz.

2 hozzászólás a(z) Validation by Rules: adatellenőrzés más megközelí­tésben bejegyzéshez

  1. jf szerint:

    í–öö… Validálási szabálynak meg lehet adni saját, mondjuk az app_model-ben definiált függvényt is. Vagy ez nem erre való?

  2. Sulik Szabolcs szerint:

    De, a userDefined pont erre való; vagy egyszerűen csak megadod szabálynak a model (akár AppModel) metódusát (először azt keresi a rendszer és csak utána a Validation osztály megfelelő nevű metódusát).

    Viszont nem gondolom, hogy ez túl praktikus lenne. Minden projektben elkezdheted összevadászgatni a megfelelő app_model (vagy egyedi model) ellenőrző metódusokat.

    A megoldásom közelebb van a megfelelőhöz: gondold el, hogy nincsenek komponenseid, hanem van egy nagy Component osztály néhány metódussal, de nem tudod bőví­teni, csak ha a controllert bőví­ted. Ezzel szemben pontosan azért vannak komponensek, hogy az ilyen újrafelhasználható kódrészek teljesen különváljanak a controller-ektől. Ezt valósí­tom meg a szabályokkal. (kicsi eröltetett lehet a példa, de szemléletes)

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s