locknet.ro

archive

Validarea obiectelor ActiveRecord

Acesta este un articol destul de lung si poate plictisitor.
Se poate inscrie intr-o serie de articole cu numele Ce nu imi place la Medick?

Validare in Medick a obiectelor ActiveRecord inainte de salvarea in baza de date se face cu ajutorul filtrelor before_*.
Am tinut foarte mult sa am o sintaxa citibila si asemanatoare cu cea din rails.
Astfel, pentru un obiect ActiveRecord – denumit pentru exemplificare Person avand obligativitatea prezentei numelul si a adresei dar si unicitatea numelul – cineva poate scrie:

class Person extends ActiveRecord {
  protected function before_save() {
    $this->validates()->presence_of('name', 'address');
    $this->validates()->uniqueness_of('name');
    return TRUE;
  }
[...]
}

In primul rand, php-ul are nevoie de linia return TRUE, fara aceasta – in momentul in care se incearca salvarea obiectului cu $person->save() – vom constata ca valoarea returnata in mod implicit este FALSE iar metoda save isi va termina (neanuntat!) executia.

Trecand peste aceasta, sintaxa pare ok. Functionalitatea este ok, chiar merge, in save rulez un “colector de errori” ce va intercepta validarile facute.

La o privire mai atenta, metoda validates returneaza un obiect de tip Validator. Acesta, abuzand de “magia” php, foloseste metoda __call pentru a intecepta apelurile catre metode nedefinite.
In __call, am adaugat conditii astfel incat in functie de numele metodei apelate se va incerca trimiterea apeluli mai departe catre metode cunoscute ale obiectului Validator.
Spre exemplificare, presence_of este redistribuita catre isEmpty cu parametru campul pe care vrem sa il validam, in cazul nostru isEmpty(camp name) si isEmpty(camp address). Daca valoare campului dat spre validare este egala cu ‘’ se adauga automat campului un mesaj de eroare (‘is empty’) si se returneaza TRUE(da, campul este gol).

Validator-ul meu stie pentru moment doar doua tipuri de verificari: uniqueness_of si presence_of.
Pentru a adauga un nou tip de validare (de exempul verificare lungimii unui camp: length_of) trebuiesc facute modificari directe in framework in metoda __call a obiectului Validator.
Adaugarea unui mesaj personalizat de eroare este o alta piedica, practic cineva ar trebui sa modifice manual mesajele trimise de Validator.

Revenind la filtrele before_*, am lasat pentru moment o portita de validare, utilizatorul putand adauga validari personalizate folosind de exemplu:

if (strlen($this->name) <= 3) {
  $this->row->getFieldByName('name')->addError('should have at least 4 chars.');
}

pentru a valida dimensiunea campului name.

Intreaga procedura de validare a obiectelor ActiveRecord in Medick este practic inutilizabila in cadrul proiectelor de tip mediu.
Validatorul meu, chiar daca functioneaza si arata bine, este practic inchis. Nu se pot schimba mesajele de eroare si nu se pot adauga alte tipuri de validari.

Vestea buna insa: sunt in cautarea unui nou design al API-uli ce se ocupa de validari. Am cateva ideii, momentan asezate doar pe hartie:
Voi adauga in clasa ActiveRecord metode directe de validare ce vor returna un descendent al obiectului Validator (transformat in obiect abstract):

[....]
protected function validates_presence_of() {
  return new PresenceOfValidator($this, func_get_args());
}
[...]
class PresenceOfValidator extends Validator {
[...]
}

Noul Validator va avea metode prin care se vor putea seta parametrii aditionali, de exemplu pentru verificarea dimensiunii unui camp se va putea scrie:
$this->validates_length_of('name')->within(1,12);
sau pentru a adauga un mesaj de eroare personalizat:

$this->validates_presence_of('name')->message('%s trebuie completat!'); // => Name trebuie completat

Nu pot spune acum cand o sa modific API-ul de validare sau cum va arata el, sunt inca in cautare de solutii (doar la asta ma gandesc pe 41).
In mod sigur insa, Medick are nevoie de un release cat mai curand.