Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
Bonjour à tous,
je suis confronté à différents problèmes au niveau des mes formulaires.
J'ai créé, dans un premier temps, un formulaire pour l'ajout et l'édition d'un utilisateur (rien de plus classique..) qui étends Zend\Form\Form. Au niveau de ce formulaire, j'ai le validator Db\NoRecordExists au niveau du champ username et le champ password en requis.
Ce que je faisais jusqu'à maintenant, c'est d'enlever le validateur au niveau de mon action edit du controller et mettre le champ du password en non requis :
[lang=php] public function editAction() { $form = $this->form; $form->getInputFilter()->get('password')->setRequired(false); $form->getInputFilter()->remove('username'); // le reste de mon action ... }
Jusque là, tout fonctionnait bien !
Mais...
Le problème, c'est que j'ai besoin de ce formulaire dans une autre partie de mon code (dans un module différent en fait) et d'après ce que j'ai vu, il est préférable d'utiliser des fieldsets. J'ai donc refactorisé mon formulaire, ajouter un fieldset et un form en accord avec la doc de ZF2.
Depuis que j'utilise le fieldset, je n'arrive pas à enlever le validateur comme précédemment et mettre le champ "password" en non-requis.
D'après la doc, on peut utiliser le "validationGroup". D'après ce que j'ai compris, c'est utile lorsqu'on à plusieurs formulaires pour un seul fieldset (je me trompe ?).
Dans mon cas, je ne vois pas trop l'intérêt d'utiliser deux formulaires qui seraient totalement identiques sauf au niveau de la validation.
Par ailleurs, en mettant en place le fieldset, je remarque qu'il m'est impossible de "binder" mes formulaires avec les données récupérées de la base de données (pour l'édition notamment) :
[lang=php] public function editAction(){ $form = $this->form; $user = $this->userService->find($id); // je récupère un object $form->bind($user); }
Avec ceci, mon formulaire d'édition est vide...
(J'ai également essayé de récupérer un tableau et de le "binder" avec la méthode 'bindValue'... même résultat).
Une idée pour résoudre ces différents problème ?
Merci d'avance,
Romain
Dernière modification par RomainG (19-08-2015 11:51:15)
Hors ligne
Bonjour,
Est il possible de nous montrer ta classe form (avec donc la déclaration de ton fieldset (this->add)) et ton fieldset(au moins pour ce qui est de ton hydrateur.)
Je suspecte que si ton bind ne fait rien c'est que ton hydrateur est mal configuré. (pour que ton bind puisse te mettre les bon champs au bons endroit il faut que le form soit en parfaite "harmonie" avec ton entité. Et cela c'est l'hydrateur qui s'en charge. Les champ de ton fieldset (les champs de ton formulaire) doivent donc être les mêmes que ceux déclaré dans tes entités.
Les fieldsets servent en effet a etre reutilisé plusieurs fois, et ne seront mis a jour en base que les champs déclaré dans le validationGroup.
Pour que le setRequired(false/true) marche il faut aussi penser a faire setAllowEmpty(false/true) sinon cela ne pourra marcher.
Hors ligne
Salut,
voici le code que tu m'as demandé :
Fieldset :
[lang=php] <?php namespace User\Form; use Zend\Form\Fieldset; use Zend\Stdlib\Hydrator\ClassMethods; use User\Model\User; use Zend\Form\Element\Hidden; use Zend\Form\Element\Text; use Zend\Form\Element\Password; use Zend\Form\Element\Select; use Zend\InputFilter\InputFilterProviderInterface; use Zend\ServiceManager\ServiceLocatorInterface; use Zend\Db\Adapter\AdapterInterface; class UserFieldset extends Fieldset implements InputFilterProviderInterface { protected $dbAdapter; /* (non-PHPdoc) * @see \Zend\Form\Fieldset::__construct() */ public function __construct(AdapterInterface $dbAdapter) { parent::__construct('user'); $this->dbAdapter = $dbAdapter; $this->setHydrator(new ClassMethods(false)); $this->setObject(new User()); $this->setAttribute('method', 'post'); $this->addElements(); } private function _getAdapter() { return $this->dbAdapter; } public function addElements() { $iduser = new Hidden('iduser'); $rebuildpwdlabel = new Hidden('rebuildpwdlabel'); $rebuildpwdlabel->setValue('rebuildpwdlabel'); $rebuildpwdmsgconfirm = new Hidden('rebuildpwdmsgconfirm'); $rebuildpwdmsgconfirm->setValue('rebuildpwdmsgconfirm'); $userid = new Text('userid'); $userid->setOptions(array('label' => 'login', 'column-size' => 'lg-8'))->setLabelAttributes(array('class' => 'col-lg-4')); $password = new Password('password'); $password->setOptions(array('label' => 'password', 'column-size' => 'lg-8'))->setLabelAttributes(array('class' => 'col-lg-4')); $profile = new Select('profile'); $profile->setOptions(array('label' => 'profile', 'column-size' => 'lg-8'))->setLabelAttributes(array('class' => 'col-lg-4'))->setDisableInArrayValidator(true); $this->add($iduser) ->add($rebuildpwdlabel) ->add($rebuildpwdmsgconfirm) ->add($userid) ->add($password) ->add($profile); } /* (non-PHPdoc) * @see \Zend\InputFilter\InputFilterProviderInterface::getInputFilterSpecification() */ public function getInputFilterSpecification() { return [ 'userid' => [ 'required' => true, 'validators' => [ [ 'name' => 'Db\NoRecordExists', 'options' => [ 'table' => 'user', 'field' => 'userid', 'adapter' => $this->_getAdapter() ] ] ] ], 'password' => [ 'required' => true ], 'profile' => [ 'required' => true ] ]; } }
Le form :
[lang=php] <?php namespace User\Form; use Zend\Form\Form; use User\Model\User; use Zend\Stdlib\Hydrator\ClassMethods; use Zend\Form\Element\Hidden; use Zend\Form\Element\Text; use Zend\Form\Element\Password; use Zend\Form\Element\Select; use Zend\Form\Element\Button; class UserForm extends Form{ public function __construct($name = null, $options = array()){ parent::__construct('user'); $this->setHydrator(new ClassMethods(false)); $this->setObject(new User()); $this->setAttribute('method', 'post'); } public function init() { $this->add([ 'name' => 'user', 'type' => 'UserForm', 'options' => [ 'use_as_base_fieldset' => true, 'label' => false ] ]); $this->addElements(); } public function addElements(){ $button = new Button('submit'); $button->setOptions(array('label' => 'submit', 'column-size' => 'lg-8 col-lg-offset-4', 'fontAwesome' => 'check'))->setAttributes(array('id' => 'submitbutton', 'class' => 'btn-primary', 'type' => 'submit')); $this->add($button); } }
Pour l'appel au Fieldset dans la class Form, j'utilise une factory :
Dans module.config.php :
[lang=php] 'form_elements' => [ 'factories' => [ 'User\Form\UserFieldset' => 'User\Factory\UserFieldsetFactory', ], 'aliases' => [ 'UserForm' => 'User\Form\UserFieldset', ] ],
Et la factory en elle-même :
[lang=php] <?php namespace User\Factory; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; use User\Form\UserFieldset; class UserFieldsetFactory implements FactoryInterface { /* (non-PHPdoc) * @see \Zend\ServiceManager\FactoryInterface::createService() */ public function createService(ServiceLocatorInterface $serviceLocator) { $realServiceLocator = $serviceLocator->getServiceLocator(); return new UserFieldset($realServiceLocator->get('Zend\Db\Adapter\Adapter')); } }
Je précise quand même que je n'utilise pas Doctrine pour ce projet...
Hors ligne
Oui tu n'utilise pas doctrine mais tu as une "entité" User qui est hydraté par la classe de Zend par ce constructeur et je voulais m'assurer qu'il était bien fait :
[lang=php] public function __construct($name = null, $options = array()){ parent::__construct('user'); $this->setHydrator(new ClassMethods(false)); $this->setObject(new User()); $this->setAttribute('method', 'post'); }
je regarde tout cela et je te dis si je trouve un truc.
Hors ligne
Salut, le validationGroup permet justement de faire ce dont tu as besoin. En gros si tu as un formulaire avec un fieldset user tu peux le réutiliser de manière différentes.
- Imagine le premier cas l'inscription :
Tu as besoin d'afficher un certains nombre de champs, dans ce cas tu vas surement même tous les utiliser, tu peux donc faire un validationGroup avec tous les champs (ou ne pas en spécifier)
- Second cas l'utilisateur peut modifier son profil :
Tu vas utiliser le même formulaire mais avec moins de champs. Par exemple l'utilisateur peut modifier uniquement son age et son pseudo. Tu auras un validation group avec juste age et pseudo.
- Troisième cas tu es admin et tu veux modifier le profil de quelqu'un :
Dans ce cas tu vas avoir besoin par exemple de presque tous les champs sauf le mot de passe. Hop un autre validation group
Enfin si jamais tu as des règles différentes du genre les contraintes d'intégrité. Tu vas avoir lors de l'inscription devoir t'assurer que le mail n'existe pas déjà par exemple. Alors que dans le cas d'une modification ce filtre bloquera la validation de ton formulaire si l'utilisateur ne modifie pas son mail (et oui il existe déjà) tu dois donc t'assurer qu'il est seulement unique et associé à cet utilisateur. Ce que je fais dans ce cas c'est 2 fabriques. Une pour créer le formulaire en mode update et l'autre en mode create. Dans mon constructeur j'ai juste un boolean $update = false (par default) et suivant mon besoin en update ou create j'utilise l'une ou l'autre fabrique.
PS : le validation group tu peux le définir dans ton contrôleur
Hors ligne
Merci pour vos réponses.
Je n'avais pas bien saisi l'utilisation du validationGroup.
Par contre, je n'arrive toujours pas à "binder" mes données avec le formulaire lors de l'édition.
Pourtant, si je débug $user, je récupère bien mon objet, comme prévu :
[lang=php] object(User\Model\User)[490] protected 'iduser' => string '9' (length=1) protected 'userid' => string 'admin3' (length=6) protected 'password' => string 'cc77b4dd5b66bb3873bcb618446032e3' (length=32) protected 'profile' => string '11' (length=2)
Se pourrait-il que ce soit lié au fait que j'ai deux champs "fictifs" (rebuildpwdlabel et rebuildpwdmsgconfirm) ?
J'ai un peu de mal à y croire mais sait-on jamais...
Merci d'avance,
Romain
Hors ligne
Bonjour,
ça peut paraître con ce que je dit, mais c'est un bug possible pour que ton bind, sans te donner de message d'erreur ne t'affiche rien dans les champs, donc je me lance.
Dans ton objet User\Model\User, as tu les getters et les setters de tes propriétés ? Et avec la visibilité public ? (A moins que cela ne marche que pour doctrine et les hydrateurs doctrine) Vu que tu utilises TableGateway, il est possible que ce fonctionnement d'hydration soit différent (méthode exchangeArray ou getArrayCopy)
Hors ligne
Salut,
j'ai effectivement les getters/setters des propriétés dans mon objet User\Model\User. Les getters/setters ont une visibilité public alors que les propriétés ont une visibilité en protected.
Ce que je ne comprends pas c'est que le bind fonctionne parfaitement tant que je n'utilise pas de fieldset... Dans mes autres classes de formulaires qui étendent de Form, je n'ai aucun problème pour l'édition. Mes champs se remplissent automatiquement avec le bind...
Hors ligne
Hello, c'est normal puisque pour que ton bind fonctionne faut que tu lui indiques dans quel fieldset le faire.
En gros si tu as un formulaire et que dedans tu as un fieldset dans la clé "user", pour que le formulaire puisse être binder correctement il faut que ton objet User soit dans une clé "user" sinon il pourra pas faire le matching de ton fieldset "user" avec ton modèle sauf peut être si tu utilises le paramètre "user_as_base_fieldset" comme ça :
[lang=php] $this->add( array( 'name' => 'category', 'type' => 'Application\Form\Fieldset\CategoryFieldset', 'options' => array( 'use_as_base_fieldset' => true, ), ) );
Hors ligne
C'est justement ça qui est bizarre car j'utilise le paramètre 'use_as_base_fieldset' à true comme indiqué dans le tuto (voir le code que j'ai indiqué au post #3). Et pourtant je n'arrive pas à récupérer les infos...
Hors ligne
Désolé pour le double post mais je viens de faire un test et je trouve le résultat plutôt étrange...
J'ai un autre fieldset dans mon application et dans celui-ci, je récupère parfaitement les données de la BDD après un bind.
Par contre, sur mon fieldset "user", c'est toujours le même problème... J'ai donc débugé mon form après avoir fait le bind et comparé à celui qui fonctionne. Ce que j'observe c'est que je me retrouve quand même avec les bonnes données au niveau de mon form mais elles ne s'affichent pas dans la vue...
Voici ce que me retourne le debug de mon form au niveau de ma vue :
[lang=php] object(User\Form\UserForm)[497] protected 'attributes' => array (size=2) 'method' => string 'post' (length=4) 'name' => string 'user' (length=4) protected 'bindAs' => int 17 protected 'bindOnValidate' => int 0 protected 'baseFieldset' => object(User\Form\UserFieldset)[521] protected 'dbAdapter' => object(Zend\Db\Adapter\Adapter)[450] protected 'driver' => object(Zend\Db\Adapter\Driver\Pdo\Pdo)[451] ... protected 'platform' => object(Zend\Db\Adapter\Platform\Mysql)[455] ... protected 'profiler' => null protected 'queryResultSetPrototype' => object(Zend\Db\ResultSet\ResultSet)[456] ... protected 'lastPreparedStatement' => null protected 'factory' => object(Zend\Form\Factory)[514] protected 'inputFilterFactory' => object(Zend\InputFilter\Factory)[515] ... protected 'formElementManager' => object(Zend\Form\FormElementManager)[106] ... protected 'elements' => array (size=6) 'iduser' => object(Zend\Form\Element\Hidden)[534] ... 'rebuildpwdlabel' => object(Zend\Form\Element\Hidden)[535] ... 'rebuildpwdmsgconfirm' => object(Zend\Form\Element\Hidden)[536] ... 'userid' => object(Zend\Form\Element\Text)[537] ... 'password' => object(Zend\Form\Element\Password)[538] ... 'profile' => object(Zend\Form\Element\Select)[539] ... protected 'fieldsets' => array (size=0) empty protected 'messages' => array (size=0) empty protected 'iterator' => object(Zend\Stdlib\PriorityList)[522] protected 'items' => array (size=6) ... protected 'serial' => int 6 protected 'isLIFO' => int -1 protected 'count' => int 6 protected 'sorted' => boolean true protected 'hydrator' => object(Zend\Stdlib\Hydrator\ClassMethods)[523] private 'hydrationMethodsCache' => array (size=0) ... private 'extractionMethodsCache' => array (size=1) ... protected 'underscoreSeparatedKeys' => boolean false private 'callableMethodFilter' => object(Zend\Stdlib\Hydrator\Filter\OptionalParametersFilter)[528] ... protected 'strategies' => object(ArrayObject)[524] ... protected 'namingStrategy' => null protected 'filterComposite' => object(Zend\Stdlib\Hydrator\Filter\FilterComposite)[525] ... protected 'object' => object(User\Model\User)[490] protected 'iduser' => string '2' (length=1) protected 'userid' => string 'admin2' (length=6) protected 'password' => string '165947a298f45f8c8c52584d3bff1053' (length=32) protected 'salt' => string 'f1a4b308675c2a30bf6e207a9fffb374' (length=32) protected 'profile' => string '11' (length=2) protected 'useAsBaseFieldset' => boolean true protected 'allowedObjectBindingClass' => null protected 'attributes' => array (size=2) 'name' => string 'user' (length=4) 'method' => string 'post' (length=4) protected 'label' => null protected 'labelAttributes' => array (size=0) empty protected 'labelOptions' => array (size=0) empty protected 'options' => array (size=2) 'use_as_base_fieldset' => boolean true 'label' => boolean false protected 'value' => null protected 'data' => null protected 'filter' => null protected 'useInputFilterDefaults' => boolean true protected 'hasAddedInputFilterDefaults' => boolean false protected 'hasValidated' => boolean false protected 'isValid' => boolean false protected 'isPrepared' => boolean false protected 'preferFormInputFilter' => boolean true protected 'hasSetPreferFormInputFilter' => boolean false protected 'wrapElements' => boolean false protected 'validationGroup' => array (size=1) 'user' => array (size=1) 0 => string 'profile' (length=7) protected 'factory' => object(Zend\Form\Factory)[514] protected 'inputFilterFactory' => object(Zend\InputFilter\Factory)[515] protected 'defaultFilterChain' => object(Zend\Filter\FilterChain)[516] ... protected 'defaultValidatorChain' => object(Zend\Validator\ValidatorChain)[518] ... protected 'inputFilterManager' => object(Zend\InputFilter\InputFilterPluginManager)[118] ... protected 'formElementManager' => object(Zend\Form\FormElementManager)[106] protected 'invokableClasses' => array (size=33) ... protected 'shareByDefault' => boolean false protected 'allowOverride' => boolean true protected 'autoAddInvokableClass' => boolean true protected 'creationOptions' => null protected 'serviceLocator' => object(Zend\ServiceManager\ServiceManager)[2] ... protected 'canonicalNames' => array (size=11) ... protected 'factories' => array (size=3) ... protected 'abstractFactories' => array (size=0) ... protected 'delegators' => array (size=0) ... protected 'pendingAbstractFactoryRequests' => array (size=0) ... protected 'nestedContextCounter' => int -1 protected 'nestedContext' => array (size=0) ... protected 'shared' => array (size=4) ... protected 'instances' => array (size=0) ... protected 'aliases' => array (size=3) ... protected 'initializers' => array (size=3) ... protected 'peeringServiceManagers' => array (size=1) ... protected 'retrieveFromPeeringManagerFirst' => boolean true protected 'throwExceptionInCreate' => boolean true protected 'canonicalNamesReplacements' => array (size=5) ... protected 'serviceManagerCaller' => null protected 'elements' => array (size=2) 'submit' => object(Zend\Form\Element\Button)[540] protected 'attributes' => array (size=4) ... protected 'label' => string 'submit' (length=6) protected 'labelAttributes' => array (size=0) ... protected 'labelOptions' => array (size=0) ... protected 'messages' => array (size=0) ... protected 'options' => array (size=3) ... protected 'value' => null 'redirect' => object(Zend\Form\Element\Hidden)[563] protected 'attributes' => array (size=2) ... protected 'label' => null protected 'labelAttributes' => array (size=0) ... protected 'labelOptions' => array (size=0) ... protected 'messages' => array (size=0) ... protected 'options' => array (size=1) ... protected 'value' => string 'https://www.otv2-console.romain.dev.ot.local/public/user' (length=56) protected 'fieldsets' => array (size=1) 'user' => object(User\Form\UserFieldset)[521] protected 'dbAdapter' => object(Zend\Db\Adapter\Adapter)[450] ... protected 'factory' => object(Zend\Form\Factory)[514] ... protected 'elements' => array (size=6) ... protected 'fieldsets' => array (size=0) ... protected 'messages' => array (size=0) ... protected 'iterator' => object(Zend\Stdlib\PriorityList)[522] ... protected 'hydrator' => object(Zend\Stdlib\Hydrator\ClassMethods)[523] ... protected 'object' => object(User\Model\User)[490] ... protected 'useAsBaseFieldset' => boolean true protected 'allowedObjectBindingClass' => null protected 'attributes' => array (size=2) ... protected 'label' => null protected 'labelAttributes' => array (size=0) ... protected 'labelOptions' => array (size=0) ... protected 'options' => array (size=2) ... protected 'value' => null protected 'messages' => array (size=0) empty protected 'iterator' => object(Zend\Stdlib\PriorityList)[498] protected 'items' => array (size=3) 'user' => array (size=3) ... 'submit' => array (size=3) ... 'redirect' => array (size=3) ... protected 'serial' => int 3 protected 'isLIFO' => int -1 protected 'count' => int 3 protected 'sorted' => boolean false protected 'hydrator' => object(Zend\Stdlib\Hydrator\ClassMethods)[500] private 'hydrationMethodsCache' => array (size=0) empty private 'extractionMethodsCache' => array (size=0) empty protected 'underscoreSeparatedKeys' => boolean false private 'callableMethodFilter' => object(Zend\Stdlib\Hydrator\Filter\OptionalParametersFilter)[508] protected 'strategies' => object(ArrayObject)[504] protected 'namingStrategy' => null protected 'filterComposite' => object(Zend\Stdlib\Hydrator\Filter\FilterComposite)[505] protected 'orFilter' => object(ArrayObject)[506] ... protected 'andFilter' => object(ArrayObject)[507] ... protected 'object' => object(User\Model\User)[490] protected 'iduser' => string '2' (length=1) protected 'userid' => string 'admin2' (length=6) protected 'password' => string '165947a298f45f8c8c52584d3bff1053' (length=32) protected 'salt' => string 'f1a4b308675c2a30bf6e207a9fffb374' (length=32) protected 'profile' => string '11' (length=2) protected 'useAsBaseFieldset' => boolean false protected 'allowedObjectBindingClass' => null protected 'label' => null protected 'labelAttributes' => array (size=0) empty protected 'labelOptions' => array (size=0) empty protected 'options' => array (size=0) empty protected 'value' => null
D'après ce que je comprends avec ce débug, c'est que la propriété "object" du form est ni plus ni moins les champs de mon formulaire.
Mais la grande question demeure... Pourquoi je n'ai rien dans qui s'affiche dans mes champs, en particulier pour ce formulaire ??? Quelque chose m'échappe ?
[EDIT] : OK. j'ai trouvé d'où venait le problème !
Dans mon form, j'ajoutais les champs du type :
[lang=php] $id = new Hidden('iduser');
alors qu'en mettant le nom en CamelCase, ça fonctionne :
[lang=php] $id = new Hidden('idUser');
C'est subtile mais il fallait le trouver !
Merci à vous pour votre aide !
Dernière modification par RomainG (19-08-2015 11:50:57)
Hors ligne