POSITION DU CONTEXTE:

j’utilisais le système MVC proposé par Grafickart sans me poser de question jusqu'à ce que je soit confronté à un problème survenu lors de la
validation des données transmises via des formulaires. je m'explique:
-soit mon application nommé "Bonjour"
mon controller est le suivant:

class BonjoursController extends Controller
{
function methode1(){}
function methode2(){}
}

les vues liées à chacune des 2 methodes de mon controller possedent chacune 1 ou plusieurs formulaires dont les donneés une fois soumises necessitent une validation. or la methode validation "validates" de la classe modele principale ne prend en charge que la propriété "validate" declarée dans le modele de notre application:

class Bonjour extends Model
{
var $validate = array(
                        'champs1'=>array('regle1','message1'),
                        'champs2'=>array('regle2','message2'),
                        'champs3'=>array('regle3','message3')
                      );
}

si nous mettons dans ce seul et unique tableau les règles de validation des champs de tous les formulaires gérés par notre controller,le processus de validation de chaque formulaire de notre application ne sera jamais réalisé avec succès car les methodes (formulaires) étant différentes, les champs nécessitant une validation ne seront jamais tous soumises en même temps.

SOLUTION AU PROBLÈME

nous allons légèrement modifier la methode "validates" de la classe model principale tout en veillant à ce qu'elle soit totalement compatible avec les autres applications déjà existantes:
au lieu de :

public function validates($data)
{
..............................
code ici
}

nous remplaçons par ceci:

public function validates($data,$property=null)
{   
   if($property and property_exists($this,$property)) $this->validate = $this->$property;
..............................
code ici
}

QU'EST CE QUE CELA CHANGE ?

rien tant que notre application ne gère en tout et pour tout qu'un seul formulaire et que dans sa classe modele nous continuons à attribuer à notre proprieté de validation le nom "validate".
mais si ce n'est pas le cas, on aura dans le model de notre application un truc de ce genre:

class Bonjour extends Model
{
var $validate1 = array(
                        'champs1'=>array('regle1','message1'),
                        'champs2'=>array('regle2','message2'),
                        'champs3'=>array('regle3','message3')
                      );
var $validate2 = array(
                        'champsA'=>array('regleA','messageA'),
                        'champsB'=>array('regleB','messageB'),
                        'champsC'=>array('regleC','messageC')
                      );
}

puis au niveau du controller, on aura par exemple:

class BonjoursController extends Controller
{
function methode1()
{
    $this->loadModel('Bonjour');
//noter la presence du 2e parametre, indiquant quel tableau utilisé pour la validation
//(NB: seul le nom du tableau est nécessaire pas la variable elle même)
    if(!$this->Bonjour->validates($this->request->data,'validate1'))
    {
        $this->Session->setFlash($this->Bonjour->errors,'error');
        return false;
    }
..............................
code ici
}
function methode2()
{
    $this->loadModel('Bonjour');
//IDEM
//noter la presence du 2e parametre, indiquant quel tableau utilisé pour la validation
//(NB: seul le nom du tableau est nécessaire pas la variable elle même)
    if(!$this->Bonjour->validates($this->request->data,'validate2'))
    {
        $this->Session->setFlash($this->Bonjour->errors,'error');
        return false;
    }
..............................
code ici
}
}

Note: si une même vue contient plusieurs formulaires(donc gérés par la même methode), le même principe s'applique à l’intérieur de cette methode.
voilà, j’espère que cela aidera certains d'entre vous.

3 réponses


iriven
Auteur
Réponse acceptée

RECAPITULATIF:

-soit mon application nommé "Bonjour"
mon controller est le suivant:

class BonjoursController extends Controller
{
function methode1(){}
function methode2(){}
}

les vues liées à chacune des 2 methodes de mon controller possedent chacune 1 ou plusieurs formulaires dont les donneés une fois soumises necessitent une validation. or la methode validation "validates" de la classe modele principale (fichier: model.php) ne prend en charge que la propriété "validate" declarée dans le modele de notre application:

class Bonjour extends Model
{
var $validate = array(
                        'champs1'=>array('regle1','message1'),
                        'champs2'=>array('regle2','message2'),
                        'champs3'=>array('regle3','message3')
                      );
}

si nous mettons dans ce seul et unique tableau les règles de validation des champs de tous les formulaires gérés par notre controller, et sans modification aucune de la methode

validates()

de notre fichier model.php, le processus de validation de chaque formulaire de notre application ne sera jamais réalisé avec succès car les methodes (formulaires) étant différentes, les champs nécessitant une validation ne seront jamais tous soumises en même temps.

SOLUTION AU PROBLÈME

nous allons légèrement modifier la methode "validates" du ficher model.php tout en veillant à ce qu'elle soit totalement compatible avec les autres applications déjà existantes:
au lieu de :

public function validates($data)
{
if(!$this->validate)
    {
      $this->errors] = 'Aucune règle de validation n\'a été definie';
      return false;
    }
..............................
code ici
}

nous remplaçons par ceci:

public function validates($data,$key=null)
{   
    if($key) 
    $this->validate = (isset($this->validate$key]) and count($this->validate$key]))? $this->validate$key] : null;
    if(!$this->validate)
    {
      $this->errors] = 'Aucune règle de validation n\'a été definie';
      return false;
    }
..........................
code ici
}

QU'EST CE QUE CELA CHANGE ?

rien tant que notre application ne gère en tout et pour tout qu'un seul formulaire et que dans sa classe modele la propriete "validate" garde sa structure originelle.

var $validate = array(
                         'champs1'=>array('regle1','message1'),
                         'champs2'=>array('regle2','message2'),
                         'champs3'=>array('regle3','message3')
                     );

mais si ce n'est pas le cas, on aura dans le model de notre application un truc de ce genre:

class Bonjour extends Model
{

var $validate = array(
                        'validate1' => array(
                                        'champs1'=>array('regle1','message1'),
                                        'champs2'=>array('regle2','message2'),
                                        'champs3'=>array('regle3','message3')
                                      ),
                        'validate2' => array(
                                        'champsA'=>array('regleA','messageA'),
                                        'champsB'=>array('regleB','messageB'),
                                        'champsC'=>array('regleC','messageC')
                                        )
                    );
}

puis au niveau du controller, on aura par exemple:

class BonjoursController extends Controller
{
function methode1()
{
    $this->loadModel('Bonjour');
//noter la presence du 2e parametre, indiquant quel sous tableau de la propieté $this->validate utilisé pour la validation
    if(!$this->Bonjour->validates($this->request->data,$key='validate1'))
    {
        $this->Session->setFlash($this->Bonjour->errors,'error');
        return false;
    }
..............................
code ici
}
function methode2()
{
    $this->loadModel('Bonjour');
//IDEM
//noter la presence du 2e parametre, indiquant quel sous tableau de la propieté $this->validate utilisé pour la validation
    if(!$this->Bonjour->validates($this->request->data,$key='validate2'))
    {
        $this->Session->setFlash($this->Bonjour->errors,'error');
        return false;
    }
..............................
code ici
}
}

Note:

  • si une même vue contient plusieurs formulaires(donc gérés par la même méthode), le même principe s'applique à l’intérieur de cette méthode.
  • si votre application ne comporte qu'un seul formulaire, la structure originelle (grafickart) reste valable, et dans ce cas la méthode "validates()" s’exécute sans second argument comme au départ.

voilà, j’espère que cela aidera certains d'entre vous.

Vraiment pas une bonne idée de faire plusieurs variables pour la validation. Il y a moyen de pousser un peu plus la chose. Il faut faire attention avec ce qui semblerait être de la réflexion (property_exists). Enfin, ça risque de fonctionner... heureusement, c'est toi qui a écrit le code de la classe Model. :-)

<?php 
class Model
{
    public $validate = ];
    public function validate(array $data)
    {
        // traitement avec $this->validate, qui sera définie dans les sous classes 
        return /* tes erreurs */ ]; // ou $this->errors = /* ... */ ];
    }
}
class Bonjour extends Model
{
    // À la limite, tu pourrais charger des règles venu d'une
    // base de données. Ce n'est pas nécessairement ce que
    // l'on veut généralement. Un tableau se remplit dynamiquement,
    // mais une variable, pas vraiment (ça serait pas une bonne idée de 
    // créer des variable "incrémenté").
    public $validate =

        'Add' => 

            'champs1' => 'regle1', 'message1' ],
            // ...
        ],
        'Edit' => 

            'champs1' => 'regle1', 'message1' ],
            // ...
        ]
    ]
}
class BonjoursController extends Controller
{
    public function index()
    {
        // $this->request->data pourrait avoir un nom...
        // 
        // 'Add' : 
        //      
        // 'champs1' => 'la valeur'
        // // ...   
        // ]
        // ]
        // 
        $this->Bonjour->validate($this->request->data);
    }
}
iriven
Auteur

salut Ramzz1994 et merci pour ton intervention. encore une fois je ne vois pas comment en empilant toutes les regles dans un super tableau coe tu l'as fais et sans modification aucune de la methode "validates()" tu pourras valider tes formulaires.
prenons par exemple la structure de ton tableau, et la vue "edit",j'imagine que tu voudrais que la validation de ton formulaire sur cette vue porte sur les regles definies dans

$this->validate'edit']

, or la methode

validates()

telle qu'elle est ecrite essayera de valider aussi les regles de

$this->validate'add']

. bien-sur les champs renseignés dans le 2e sous tableau ne faisant pas parti de notre formulaire, la validation de ceux-ci nous retournera systématiquement des erreurs.
aussi tu as ecrit : "Il faut faire attention avec ce qui semblerait être de la réflexion (property_exists). Enfin, ça risque de fonctionner... heureusement, c'est toi qui a écrit le code de la classe Model". pour moi il s'agit là d'une remarque inutile et pas du tout pertinente car un developpeur ne va jamais se lancer tête baissée dans la création d'une application avec un framework dont il ne maîtrise pas. lorsque vous créez une application avec cake, codeigniter, zend, joomla ou tout autre systeme, c'est que vous savez comment ils sont structurés, et si tel n'etait pas les cas et que vous vous trouviez dans l'obligation de les utiliser, vous deviez d'abords prendre du temps pour les étudier via la doc ou les forums.
tu comprends donc que lorsque je fais une modif dans une methode de ma classe, je sais comment dorénavant je lui passerai les variables.
surtout que je prend la peine de m'assurer que les autres applications ne me retourneront pas d'erreur en cas de validation.

if($property and property_exists($this,$property)) $this->validate = $this->$property;

si tu est réfractaire à la déclaration de plusieurs tableaux(ce qui pour le coup est une bonne chose car permet de centraliser,la validation dans une application: je l'adopte), en gardant la structure de ton super tableau , et en restant dans l'esprit de ma proposition , la methode validates() pourrait ressembler à ceci:

class Model
{
    public $validate = ];

public function validates($data,$key=null)
{   
    if($key) 
    $this->validate = (isset($this->validate$key]) and count($this->validate$key]))? $this->validate$key] : null;
    if(!$this->validate)
    {
      $this->errors] = 'Aucune règle de validation n\'a été definie';
      return false;
    }
..........................
suite du code
}
/*
*
*/
}

dans le model de notre application

class Bonjour extends Model
{

var $validate = array(
                        'add' => array(
                                        'champs1'=>array('regle1','message1'),
                                        'champs2'=>array('regle2','message2'),
                                        'champs3'=>array('regle3','message3')
                                      ),
                        'edit' => array(
                                        'champsA'=>array('regleA','messageA'),
                                        'champsB'=>array('regleB','messageB'),
                                        'champsC'=>array('regleC','messageC')
                                        )
                    );
}

et dans le controller on aura:

class BonjoursController extends Controller
{
    public function index()
    {
        $this->loadModel('Bonjour');
        // noter la presence du 2e parametre, indiquant quel sous tableau de la 
        // proprieté validate sera utilisé pour la validation
        if(!$this->Bonjour->validates($this->request->data, $key='edit'))
        {
            $this->Session->setFlash($this->Bonjour->errors,'error');
            return false;
        }
    }
/*
*
*/
}

je constate egalement(même si tu le déconseille finalement) que tu parles des pattern de validation qu pourraient provenir d'une bdd. moi je le deconseille fortement, non seulement pour des raisons de perfomance, mais aussi d'usage. on ne va pas consommer les ressources de notre serveur mysql juste pour recuperer les patterns necessaires à la validation d'un formulaire. ce qui faudrai c'est créer une classe de validation, validant les differents types de données qu'il est possible de poster depuis un formulaire,et l'appeller dans notre methode "validates()", ce qui boostera d'avantage notre rythme de travail, car les regles etant deja definies dans la classe, nous n'auront pour chaque champs qu'à renseigner le type de données auxquelles s'attendre.

exemple:

var $validate = array(
            'login'=>array('notempty','email'),
            'password'=>array('notempty','passwd'=>array('min'=>'8','max'=>'32'))   

             );

et dans le fichier model.php, notre methode de validation ressemblerai à ceci.

public function validates($data,$key=null)
{   
    if($key) 
    $this->validate = (isset($this->validate$key]) and count($this->validate$key]))? $this->validate$key] : null;
    if(!$this->validate)
    {
      $this->errors] = 'Aucune règle de validation n\'a été definie';
      return false;
    }
    $validation = new Validation($data);//j'ai intégré un autoloader au mvc
    foreach($this->validate as $field=>$rules)
    {   
        if(!is_array($rules))
        {
            if(strpos($rules,',')!==false) $rules = explode(',',trim($rules,','));
            else $rules = array($rules);
        }
        foreach($rules as $each=>$rule)
            if(!$validation->matchRules($each,$field,$rule)) $this->errors$field] = $validation->message;       
    }//endforeach
    //irivenmvc::debug($this->errors); 
    if(isset($this->Form))  $this->Form->errors = $this->errors;
    if(!count($this->errors)) return true;
    return false;
}

NB: mes propositions sont testées et 100% fonctionnelles.