Skip to content

Commit

Permalink
invalidate forms on transformation failures
Browse files Browse the repository at this point in the history
  • Loading branch information
xabbuh committed Oct 31, 2018
1 parent 25c2975 commit 14fba95
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
Expand Up @@ -71,6 +71,11 @@
<argument type="service" id="form.choice_list_factory"/>
</service>

<service id="form.type_extension.form.transformation_failure_handling" class="Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension">
<tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FormType" />
<argument type="service" id="translator" on-invalid="ignore" />
</service>

<!-- FormTypeHttpFoundationExtension -->
<service id="form.type_extension.form.http_foundation" class="Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension">
<argument type="service" id="form.type_extension.form.request_handler" />
Expand Down
13 changes: 12 additions & 1 deletion src/Symfony/Component/Form/Extension/Core/CoreExtension.php
Expand Up @@ -16,8 +16,10 @@
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator;
use Symfony\Component\Form\Extension\Core\Type\TransformationFailureExtension;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
* Represents the main form extension, which loads the core functionality.
Expand All @@ -28,11 +30,13 @@ class CoreExtension extends AbstractExtension
{
private $propertyAccessor;
private $choiceListFactory;
private $translator;

public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null)
public function __construct(PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null, TranslatorInterface $translator = null)
{
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
$this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor));
$this->translator = $translator;
}

protected function loadTypes()
Expand Down Expand Up @@ -74,4 +78,11 @@ protected function loadTypes()
new Type\ColorType(),
);
}

protected function loadTypeExtensions()
{
return array(
new TransformationFailureExtension($this->translator),
);
}
}
@@ -0,0 +1,67 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Extension\Core\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Translation\TranslatorInterface;

/**
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
*/
class TransformationFailureListener implements EventSubscriberInterface
{
private $translator;

public function __construct(TranslatorInterface $translator = null)
{
$this->translator = $translator;
}

public static function getSubscribedEvents()
{
return array(
FormEvents::POST_SUBMIT => array('convertTransformationFailureToFormError', -1024),
);
}

public function convertTransformationFailureToFormError(FormEvent $event)
{
$form = $event->getForm();

if (null === $form->getTransformationFailure() || !$form->isValid()) {
return;
}

$childrenSynchronized = true;

foreach ($form as $child) {
if (!$child->isSynchronized()) {
$childrenSynchronized = false;
break;
}
}

if ($childrenSynchronized) {
$clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : \gettype($form->getViewData());
$message = 'This value is not valid.';

if (null !== $this->translator) {
$message = $this->translator->trans($message, array('{{ value }}' => $clientDataAsString));
}

$form->addError(new FormError($message, 'This value is not valid.', array('{{ value }}' => $clientDataAsString), null, $form->getTransformationFailure()));
}
}
}
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Extension\Core\Type;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\EventListener\TransformationFailureListener;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
*/
class TransformationFailureExtension extends AbstractTypeExtension
{
private $translator;

public function __construct(TranslatorInterface $translator = null)
{
$this->translator = $translator;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
if (!isset($options['invalid_message']) && !isset($options['invalid_message_parameters'])) {
$builder->addEventSubscriber(new TransformationFailureListener($this->translator));
}
}

public function getExtendedType()
{
return 'Symfony\Component\Form\Extension\Core\Type\FormType';
}
}
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Tests\Extension\Core;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\Extension\Core\CoreExtension;
use Symfony\Component\Form\FormFactoryBuilder;

class CoreExtensionTest extends TestCase
{
public function testTransformationFailuresAreConvertedIntoFormErrors()
{
$formFactoryBuilder = new FormFactoryBuilder();
$formFactory = $formFactoryBuilder->addExtension(new CoreExtension())
->getFormFactory();

$form = $formFactory->createBuilder()
->add('foo', 'Symfony\Component\Form\Extension\Core\Type\DateType')
->getForm();
$form->submit('foo');

$this->assertFalse($form->isValid());
}
}

0 comments on commit 14fba95

Please sign in to comment.