infinite-networks / InfiniteFormBundle

A collection of useful form types and extensions for Symfony.
169 stars 40 forks source link

Handling Polycollections errors #51

Open 3kynox opened 8 years ago

3kynox commented 8 years ago

Hello !

I'm looking to manage errors with polycollections. It's easy to manage normal fields or moyensComm validation using validation.yml file

brunoBundle\Entity\Contact:
    properties:
        moyensComm:
            - Count:
                min: 1
                max: 5
                minMessage: "Vous devez spécifier au moins un moyen de communication"
                maxMessage: "Vous ne pouvez ajouter plus de {{ limit }} moyen(s) de communication"
        text:
            - Length:
                min: 5
                max: 20
                minMessage: "Vous devez spécifier au minimum 5 caractères pour le champ 'Text'"
                maxMessage: "Vous êtes limités à {{ limit }} caractères pour le champ 'Text'"

But about Polycollections, not sure how to specify constraints.

Thanks for help.

jmclean commented 8 years ago

First you need to tell Symfony to apply validation rules to the entities in the moyensComm array:

brunoBundle\Entity\Contact:
    properties:
        moyensComm:
            - Valid: ~

And then you can add validation rules for your derived classes:

brunoBundle\Entity\Email:
    properties:
        email:
            - Email: ~
            - NotBlank: ~

You may also need to turn off error_bubbling on the polycollection to make some errors (like Count in your example) show up in the right place:

        $builder
            ->add('moyensComm', 'infinite_form_polycollection', array(
                'error_bubbling' => false,
3kynox commented 8 years ago

Thanks jm, this is working (if no moyensComm is defined, showing error at the right place and it adds regex pattern property to the email input field in my example).

I face another problem about tests (using codeception) and polycollections. I'm writing a method to test form data validation that's look like that :

// brunoBundle/Tests/unit/Form/ContactTypeTest.php

use brunoBundle\Form\ContactType;
use brunoBundle\Entity\Contact;
use brunoBundle\Entity\Telephone;

use Symfony\Component\Form\Test\TypeTestCase;

class ContactTypeTest extends TypeTestCase
{
    public function testSubmitValidData()
    {
        $formData = array(
            'text' => 'Client n°3',
            'textarea' => 'Encore du texte ...',
            'email' => 'test3@test.com',
            'entier' => 51,
            'money' => 51,
            'date' => new \DateTime('2016/01/01'),
            'gender' => 2,
            'moyensComm' => array('0' => array('numero' => '00 00 00 00 10')),
        );

        $object = new Contact();
        $object->setText('Client n°3');
        $object->setTextarea('Encore du texte ...');
        $object->setEmail('test3@test.com');
        $object->setEntier(51);
        $object->setMoney(51);
        $object->setDate(new \DateTime('2016/01/01'));

        $telephone = new Telephone();
        $telephone->setNumero('00 00 00 00 10');
        $telephone->setContact($object);

        $object->addMoyensComm($telephone);

        $object->getMoyensComm();

        $type = new ContactType($object);
        $form = $this->factory->create($type);

        // submit the data to the form directly
        $form->submit($formData);

        $this->assertTrue($form->isSynchronized());
        $this->assertEquals($object, $form->getData());

        $view = $form->createView();
        $children = $view->children;

        foreach (array_keys($formData) as $key) {
            $this->assertArrayHasKey($key, $children);
        }
    }
}

What I do here is build an array with correct values, then building a Contact object and comparing both.

The problem is it returns error :

Test telephone (TelephoneTest::testTelephone)                                                                                                Ok
Test check telephone bdd (TelephoneTest::testCheckTelephoneBdd)                                                                              Ok
Test change numero (TelephoneTest::testChangeNumero)                                                                                         Ok
ContactTypeTest::testSubmitValidData                                                                                                         Error
--------------------------------------------------------------------------------------------------------------------------------------------------

Time: 8.66 seconds, Memory: 27.75Mb

There was 1 error:

---------
1) ContactTypeTest::testSubmitValidData

  [Symfony\Component\Form\Exception\InvalidArgumentException] Could not load type "infinite_form_polycollection"  

#1  D:\clients\courtier-web\formType\www\vendor\symfony\symfony\src\Symfony\Component\Form\FormFactory.php:82
#2  D:\clients\courtier-web\formType\www\vendor\symfony\symfony\src\Symfony\Component\Form\FormBuilder.php:106
#3  D:\clients\courtier-web\formType\www\vendor\symfony\symfony\src\Symfony\Component\Form\FormBuilder.php:267
#4  D:\clients\courtier-web\formType\www\vendor\symfony\symfony\src\Symfony\Component\Form\FormBuilder.php:215
#5  D:\clients\courtier-web\formType\www\vendor\symfony\symfony\src\Symfony\Component\Form\FormFactory.php:39
#6  D:\clients\courtier-web\formType\www\src\brunoBundle\Tests\unit\Form\ContactTypeTest.php:46
#7  ContactTypeTest->testSubmitValidData

FAILURES!
Tests: 13, Assertions: 34, Errors: 1.

Am I doing this the wrong way ?

jmclean commented 8 years ago

You're on the right track but see also Symfony's documentation on testing forms with dependencies.

class ContactTypeTest extends TypeTestCase
{
    protected function getExtensions()
    {
        $adresse = new AdresseType();
        $contact = new ContactType();
        $email = new EmailType();
        $mobile = new MobileType();
        $telephone = new TelephoneType();
        $polycollection = new PolyCollectionType();

        return array(
            new PreloadedExtension(
                array(
                    $adresse->getName() => $adresse,
                    $contact->getName() => $contact,
                    $email->getName() => $email,
                    $mobile->getName() => $mobile,
                    $telephone->getName() => $telephone,
                    $polycollection->getName() => $polycollection,
                ),
                array()
            ),
        );
    }
3kynox commented 8 years ago

Thanks for answer jm,

I saw this Symfony doc page but I was getting pain to adapt code to polycollection situation.

I did last code you post but I still have error _could not load type "infinite_formpolycollection

I try to find how the name is given, it should not be the same currently.

EDIT : Finally got dump that works inside unit test (using ob_flush()) and

var_dump($polycollection->getName()) returns string(21) "brunobundle_moyencomm"

jmclean commented 8 years ago

What do your use statements look like now? You'll need use Infinite\FormBundle\Form\Type\PolycollectionType; in there.