Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotations: Use Annotation to build Fieldset don't preserve Validator/filter #41

Open
michalbundyra opened this issue Jan 15, 2020 · 2 comments

Comments

@michalbundyra
Copy link
Member

This issue has been moved from the zendframework repository as part of the bug migration program as outlined here - http://framework.zend.com/blog/2016-04-11-issue-closures.html


Original Issue: https://api.github.com/repos/zendframework/zendframework/issues/7686
User: @gregGit
Created On: 2016-03-14T13:33:17Z
Updated At: 2016-03-15T00:15:27Z
Body
I'm using Form Annotation in a project with DoctrineORM 2.0. So i wan't to have all my fields specifications in my entities including the Validator/Filter for each element.
When i need to use an entity, i first build a form (without annotation) and add to this blank form a fieldset representing an entity which i build with annotation. By this way i can more than one entities fieldset on the same form (and collection if i need it).
Then i realise there is a pb with this method, because as the entities Form is a fieldset, all validators/filter are loose (they are not applied to the form).

It's not really a bug because input filters applies only to form, not to fieldset. But for me it's logical and pratical to define ZF2 Validator/Filter and entity's column specification (ie a varchar(20) not null column need to have StringLength and Required validator's).

The following simple exemple illustrate the problem :

Using a new skeleton application with doctrine common, i build a very simple class :

<?php

namespace Application\Form;

use Zend\Form\Annotation as Form;
/**
 * @Form\Name("test")
 */
class Test 
{
    /**
     * @var string
     *
     * @Form\Attributes({"type":"text", "required":true})
     * @Form\Options({"label":"Max 5 car field :"})
     * @Form\Required(true)
     * @Form\Filter({"name": "StripTags"})
     * @Form\Filter({"name": "StringTrim"})
     * @Form\Validator({"name":"StringLength", "options":{"encoding" : "UTF-8","min":1, "max":5}})
     */
    private $max5field;

}

Then in Index controller, i add the folowing

public function testAction() {

        $builder = new \Zend\Form\Annotation\AnnotationBuilder();
        $form = $builder->createForm(new \Application\Form\Test());
        $form->setAttribute('method', 'POST');
        $form->add(array(
            'name' => 'submit',
            'attributes' => array(
                'type' => 'submit',
                'value' => 'OK',
                'id' => 'submitbutton',
            ),
        ));

        $result="";
        $request = $this->getRequest();
        if ($request->isPost()) {

           $form->setData($request->getPost());
            if ($form->isValid()) {
                $result="Form is Valid";
            } else {
                $result = print_r($form->getMessages(), true);
            }
        }
        return new ViewModel(array('form'=>$form, 'result'=>$result));
    }

And finally the view test.phtml only contains :

<?php echo $this->form($form);
echo $result;
?>

By this all is ok, validator and filter are applied.

Now i want to use the same in a fieldset, so in controller i build form like this :

        $builder = new \Zend\Form\Annotation\AnnotationBuilder();
        $form=new \Zend\Form\Form();
        $fieldset = $builder->createForm(new \Application\Form\Test());
        $form->add($fieldset);
        ....

With this no validator/filter are applied even if i had the annotation @Form\Type("\Zend\Form\Fieldset")

So if you use annotation to build a fieldset, validator/filter applyed to fieldset elements are ignored.

Regarding to ZF2 FormFactory code it seem to be "normal" because prepareAndInjectInputFilter method is only call in configureForm method.

To be complete, my workaround is to create a new Form Factroy (wich extend Zend/Form/Factory) and redefine configureFieldset like this :

public function configureFieldset(FieldsetInterface $fieldset, $spec)
    {
       $fieldset=parent::configureFieldset($fieldset, $spec);
       $fieldset->input_filter_factory= $spec['input_filter'];
       return $fieldset;
    }

This factory is used by my annotationBuilder (using setFormFactory method)

Then all my Entities Fieldset Classes implement inputFilterProvider and the get method is :

   public function getInputFilterSpecification()
    {
        if (isset($this->input_filter_factory)){
            return $this->input_filter_factory;
        }        
        else {
            return array();
        }
    }

Then it works as i want, Validator/Filter defined in the entity annotation are set in the form using the fieldset.



Originally posted by @GeeH at zendframework/zend-form#87

@michalbundyra
Copy link
Member Author

Hi !!!
I am facing the same issue. I' am trying your fix but in my case input_filter_factory in my entity is always null...

This is my form :

class WebtvForm extends Form
{
    private $formSpecification;

    /**
     * Constructor
     */
    public function __construct (ObjectManager $objectManager)
    {
        // we want to ignore the name passed
        parent::__construct('entity-create-form');
        $this->setAttribute('method', 'post');
        $entity = new Webtv();

        $builder = new ZFBuilder($objectManager);
        $builder->setFormFactory(new ZNFormFactory());
        //Add the fieldset, and set it as the base fieldset
        $fieldset = $builder->createForm($entity);
        $this->formSpecification = $builder->getFormSpecification($entity)->getArrayCopy();

        $fieldset->setHydrator(new DoctrineHydrator($objectManager));
        $fieldset->setName("Webtv");
        $fieldset->setUseAsBaseFieldset(true);

        //\Zend\Debug\Debug::dump($this->formSpecification);

        $this->add($fieldset);
        /*
        $this->add(array(
            'type' => 'Zend\Form\Element\Csrf',
            'name' => 'csrf'
        ));
        */
        $this->add(array(
            'name' => 'submit',
            'attributes' => array(
                'type' => 'submit',
                'value' => 'Save'
            )
        ));
    }

My form factory, that part is called without problem

<?php
namespace ZN\Form;
use Zend\Form\FieldsetInterface;

class Factory extends \Zend\Form\Factory {
    /**
     * @var FieldsetInterface
     */
    private $fieldset;
    public function configureFieldset(FieldsetInterface $fieldset, $spec)
    {
        $fieldset=parent::configureFieldset($fieldset, $spec);
        $fieldset->input_filter_factory  = $spec['input_filter'];
        $this->input_filter_factory = $spec['input_filter'];
        return $fieldset;
    }

}

My entity, getInputFilterSpecification is called but input_filter_factory is null

/**
 * Webtv
 *
 * @ORM\Table(name="webtv")
 * @ORM\Entity
 * @Annotation\Name("Webtv")
 * @Annotation\Type("fieldset")
 */
class Webtv implements InputFilterProviderInterface
{

    public function getInputFilterSpecification()
    {
        if (isset($this->input_filter_factory)){
            return $this->input_filter_factory;
        }
        else {
            return array();
        }
    }

And finally my controller

    public function editAction()
    {
        $viewModel = new ViewModel();
        $viewModel->setTerminal(true);
        $webtv = new Webtv();

        $form = $this->getServiceLocator()->get('WebtvForm');

        if($this->getRequest()->getQuery("id",null)!=null) {
            $webtv = $this->getWebtvService()->loadById($this->getRequest()->getQuery("id"));
            $form->bind($webtv);
        }
        /** @var Form $form */
        if ($this->getRequest()->isPost()) {
            $params = $this->getRequest()->getPost();
            $form->setData( $params );
            if ($form->isValid()) {
                try{
                    $this->getWebtvService()->save( $form->getData() );
                    $this->getWebtvService()->getEntityManager()->flush();
                    $this->flashMessenger()->addSuccessMessage('Saved');
                    $this->redirect()->toRoute('administration',array("controller"=>"webtv","action"=>"list"));
                }catch (\Exception $error){

                    print($error->getMessage());
                    print($error->getTraceAsString());
                    exit();
                    \Zend\Debug\Debug::dump($error);
                }
            }
        }
        $viewModel->setVariable("form",$form);
        return $viewModel;
    }

Originally posted by @cybercandyman at zendframework/zend-form#87 (comment)

@michalbundyra
Copy link
Member Author

So my solution is a way different :

1- I have to inject in my form factory the FormElementManager because FormElementManager is instantiated and not called from ServiceManager, so my custom config was not loading.
2- Create a custom class Fieldset implementing InputFilterProviderInterface and called by FormElementManager

I am almost happy with that, but I am surprised that validation of fieldset created from annotation was not implemented.


Originally posted by @cybercandyman at zendframework/zend-form#87 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant