Skip to content

Commit

Permalink
merged branch bschussek/issue5844 (PR #6137)
Browse files Browse the repository at this point in the history
This PR was merged into the master branch.

Commits
-------

586a16e [Validator] Changed DefaultTranslator::getLocale() to always return 'en'
58bfd60 [Validator] Improved the inline documentation of DefaultTranslator
cd662cc [Validator] Added ExceptionInterface, BadMethodCallException and InvalidArgumentException
e00e5ec [Validator] Fixed failing test
cc0df0a [Validator] Changed validator to support pluralized messages by default
56d61eb [Form][Validator] Added BC breaks in unstable code to the CHANGELOG
1e34e91 [Form] Added upgrade instructions to the UPGRADE file
b94a256 [DoctrineBridge] Adapted DoctrineBridge to translator integration in the validator
c96a051 [FrameworkBundle] Adapted FrameworkBundle to translator integration in the validator
92a3b27 [TwigBridge] Adapted TwigBridge to translator integration in the validator
e7eb5b0 [Form] Adapted Form component to translator integration in the validator
46f751c [Validator] Extracted message interpolation logic of ConstraintViolation and used the Translation component for that

Discussion
----------

[Validator] Integrated the Translator in the Validator component

Bug fix: no
Feature addition: no
Backwards compatibility break: yes
Symfony2 tests pass: yes
Fixes the following tickets: #5844, #6117
Todo: -
License of the code: MIT
Documentation PR: -

This PR allows to replace the default message substitution strategy in the validator (`strtr()`) by passing an implementation of `Symfony\Component\Translation\TranslatorInterface`. The motivation for this are both #5844 and the need to replace the translation strategy in Drupal's integration of the Validator.

In the stand-alone usage of the validator, both the translator and the default translation domain can now be passed to `ValidatorBuilderInterface`:

```php
$validator = Validation::createValidatorBuilder()
    ->setTranslator(new MyTranslator())
    ->setTranslationDomain('validators')
    ->getValidator();
```

References:

* #5844
* #6117
* #6129
* [Add a validation framework to Drupal 8](http://drupal.org/node/1845546)
* [Add the symfony validator component to core despite Symfony potentially releasing BC-breaking updates after 2.3.](http://drupal.org/node/1849564)

---------------------------------------------------------------------------

by Tobion at 2012-11-28T08:53:25Z

no BC break? Looking at ValidatorBuilderInterface there is definitely one.

---------------------------------------------------------------------------

by bschussek at 2012-11-28T08:55:01Z

ValidatorBuilderInterface is not part of the stable API. You are not supposed to implement this interface.

---------------------------------------------------------------------------

by Tobion at 2012-11-28T09:01:07Z

We're not only documenting bc breaks for stable API, otherwise we could remove 90% of the upgrade file since few methods are tagged with API.
An interface that nobody should implement?

---------------------------------------------------------------------------

by bschussek at 2012-11-28T09:30:02Z

The question is what to consider a BC break. Something will always break for someone. Should we consequently mark everything as BC break? I don't think so.

For example, since 2.1, you are supposed to use `Validation::createValidator*()` for creating a validator. Because of that, I won't consider changing the constructor signature of `Validator` a BC break anymore from 2.1 on.

The same for the unstable interfaces. These are currently meant to be used only, that is, type hint against them and call their methods. But we don't guarantee that we won't add methods to them.

---------------------------------------------------------------------------

by Tobion at 2012-11-28T09:38:19Z

I agree that almost any change could be considered a BC break. So we probably need to better define what a BC break is and what not. Otherwise Symfony will stop evolving after 2.3 because from then on Fabien wanted to prevent BC breaks which is almost impossible in a strict definition of bc break.

---------------------------------------------------------------------------

by fabpot at 2012-11-28T11:37:22Z

BC breaks should always be documented, and we guarantee BC only for things tagged with @api. I'm going to update the docs to make things clearer.

---------------------------------------------------------------------------

by bschussek at 2012-11-28T13:09:57Z

@fabpot I documented these changes now in the CHANGELOG: af99ebb1206ac92889b7193ba1ecc12bf2617e85

Are we sure we want to document *all* BC breaks from now on, even in non-@api code? This could rather scare people looking at our changelogs (lots of BC BREAKS there).

---------------------------------------------------------------------------

by fago at 2012-11-28T17:29:58Z

Unfortunately, it turns out the symfony translator interface does not mach the Drupal translation system as well as we initally thought, see http://drupal.org/node/1852106. Given that, this would integrating the validator component into Drupal even harder, because it introduces the dependency on the (unwanted) translation component. :(

---------------------------------------------------------------------------

by stof at 2012-11-28T18:19:36Z

If this does not help Drupal anyway, maybe #5844 is a better way to manage translations for people using the validator outside forms ?
and the Drupal guys would simply follow a similar approach, but based on their own translator instead of the symfony one.

---------------------------------------------------------------------------

by fago at 2012-11-28T18:50:12Z

Yeah. The only problem I see with the approach of #5844 is that *after* validation only the translated messages are available. We'd need to have access to the untranslated messages also.

---------------------------------------------------------------------------

by fago at 2012-11-29T09:49:47Z

As our translation system handles translating pluralized messages differently, the current ExecutionContextInterface::addViolation() method poses a problem also. We need to pass on - both the single and plural - message, as the message gets chosen during translation, see http://api.drupal.org/api/drupal/core!includes!common.inc/function/format_plural/8
So maybe, we could allow adding an already created ConstraintViolation object also? Then, we could implement a "PluralConstraintViolation" class that takes both message templates.

---------------------------------------------------------------------------

by bschussek at 2012-12-03T15:52:36Z

I updated this PR to support pluralized messages by default in the validator. This should solve the problem of the Drupal guys, because their implementation of `TranslatorInterface::transChoice($id, $number, ...)` can now simply split the $id by pipes (`|`) and pass the parts to their own `format_plural($count, $singular, $plural, ...)` function.

For us, it breaks BC because translation catalog sources had to be adapted.

---------------------------------------------------------------------------

by fabpot at 2012-12-03T16:25:52Z

Most of the XLF files are broken (the end is missing now).

IIUC, we now have a hard dependency on the Translation component, which is something we wanted to avoid.

---------------------------------------------------------------------------

by fabpot at 2012-12-03T16:27:56Z

Oops, clicked on the "comment" button too fast.

So, the dependency is hard (you need to install the dep) but light as we only rely on the translation interface from the component (when using the default translator). It looks acceptable to me, especially because we now use Composer to manage dependencies.

---------------------------------------------------------------------------

by bschussek at 2012-12-03T16:54:10Z

@fabpot Thanks for the hint. Going to fix this.

---------------------------------------------------------------------------

by bschussek at 2012-12-04T11:34:43Z

@fabpot Fixed.

---------------------------------------------------------------------------

by bschussek at 2012-12-07T12:40:24Z

Is there anything missing for this PR to be merged?
  • Loading branch information
fabpot committed Jan 9, 2013
2 parents ed43098 + 8603bae commit 3b737b2
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CHANGELOG
* deprecated FormException and introduced ExceptionInterface instead
* [BC BREAK] FormException is now an interface
* protected FormBuilder methods from being called when it is turned into a FormConfigInterface with getFormConfig()
* [BC BREAK] inserted argument `$message` in the constructor of `FormError`

2.1.0
-----
Expand Down
1 change: 1 addition & 0 deletions Extension/Validator/ViolationMapper/ViolationMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form
// Only add the error if the form is synchronized
if ($this->acceptsErrors($scope)) {
$scope->addError(new FormError(
$violation->getMessage(),
$violation->getMessageTemplate(),
$violation->getMessageParameters(),
$violation->getMessagePluralization()
Expand Down
18 changes: 13 additions & 5 deletions FormError.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
*/
class FormError
{
/**
* @var string
*/
private $message;

/**
* The template for the error message
* @var string
Expand All @@ -41,16 +46,19 @@ class FormError
*
* Any array key in $messageParameters will be used as a placeholder in
* $messageTemplate.
* @see \Symfony\Component\Translation\Translator
*
* @param string $messageTemplate The template for the error message
* @param string $message The translated error message
* @param string|null $messageTemplate The template for the error message
* @param array $messageParameters The parameters that should be
* substituted in the message template.
* @param integer|null $messagePluralization The value for error message pluralization
*
* @see \Symfony\Component\Translation\Translator
*/
public function __construct($messageTemplate, array $messageParameters = array(), $messagePluralization = null)
public function __construct($message, $messageTemplate = null, array $messageParameters = array(), $messagePluralization = null)
{
$this->messageTemplate = $messageTemplate;
$this->message = $message;
$this->messageTemplate = $messageTemplate ?: $message;
$this->messageParameters = $messageParameters;
$this->messagePluralization = $messagePluralization;
}
Expand All @@ -62,7 +70,7 @@ public function __construct($messageTemplate, array $messageParameters = array()
*/
public function getMessage()
{
return strtr($this->messageTemplate, $this->messageParameters);
return $this->message;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions Tests/AbstractDivLayoutTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
public function testRow()
{
$form = $this->factory->createNamed('name', 'text');
$form->addError(new FormError('Error!'));
$form->addError(new FormError('[trans]Error![/trans]'));
$view = $form->createView();
$html = $this->renderRow($view);

Expand Down Expand Up @@ -58,7 +58,7 @@ public function testRowOverrideVariables()
public function testRepeatedRow()
{
$form = $this->factory->createNamed('name', 'repeated');
$form->addError(new FormError('Error!'));
$form->addError(new FormError('[trans]Error![/trans]'));
$view = $form->createView();
$html = $this->renderRow($view);

Expand Down Expand Up @@ -398,7 +398,7 @@ public function testNestedFormError()
)
->getForm();

$form->get('child')->addError(new FormError('Error!'));
$form->get('child')->addError(new FormError('[trans]Error![/trans]'));

$this->assertWidgetMatchesXpath($form->createView(), array(),
'/div
Expand Down
8 changes: 4 additions & 4 deletions Tests/AbstractLayoutTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly
public function testErrors()
{
$form = $this->factory->createNamed('name', 'text');
$form->addError(new FormError('Error 1'));
$form->addError(new FormError('Error 2'));
$form->addError(new FormError('[trans]Error 1[/trans]'));
$form->addError(new FormError('[trans]Error 2[/trans]'));
$view = $form->createView();
$html = $this->renderErrors($view);

Expand Down Expand Up @@ -1151,7 +1151,7 @@ public function testDateErrorBubbling()
{
$child = $this->factory->createNamed('date', 'date');
$form = $this->factory->createNamed('form', 'form')->add($child);
$child->addError(new FormError('Error!'));
$child->addError(new FormError('[trans]Error![/trans]'));
$view = $form->createView();

$this->assertEmpty($this->renderErrors($view));
Expand Down Expand Up @@ -1676,7 +1676,7 @@ public function testTimeErrorBubbling()
{
$child = $this->factory->createNamed('time', 'time');
$form = $this->factory->createNamed('form', 'form')->add($child);
$child->addError(new FormError('Error!'));
$child->addError(new FormError('[trans]Error![/trans]'));
$view = $form->createView();

$this->assertEmpty($this->renderErrors($view));
Expand Down
6 changes: 3 additions & 3 deletions Tests/AbstractTableLayoutTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest
public function testRow()
{
$form = $this->factory->createNamed('name', 'text');
$form->addError(new FormError('Error!'));
$form->addError(new FormError('[trans]Error![/trans]'));
$view = $form->createView();
$html = $this->renderRow($view);

Expand Down Expand Up @@ -91,7 +91,7 @@ public function testRepeatedRow()
public function testRepeatedRowWithErrors()
{
$form = $this->factory->createNamed('name', 'repeated');
$form->addError(new FormError('Error!'));
$form->addError(new FormError('[trans]Error![/trans]'));
$view = $form->createView();
$html = $this->renderRow($view);

Expand Down Expand Up @@ -250,7 +250,7 @@ public function testNestedFormError()
)
->getForm();

$form->get('child')->addError(new FormError('Error!'));
$form->get('child')->addError(new FormError('[trans]Error![/trans]'));

$this->assertWidgetMatchesXpath($form->createView(), array(),
'/table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class ValidationListenerTest extends \PHPUnit_Framework_TestCase

private $message;

private $messageTemplate;

private $params;

protected function setUp()
Expand All @@ -63,17 +65,13 @@ protected function setUp()
$this->violationMapper = $this->getMock('Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface');
$this->listener = new ValidationListener($this->validator, $this->violationMapper);
$this->message = 'Message';
$this->messageTemplate = 'Message template';
$this->params = array('foo' => 'bar');
}

private function getConstraintViolation($code = null)
{
return new ConstraintViolation($this->message, $this->params, null, 'prop.path', null, null, $code);
}

private function getFormError()
{
return new FormError($this->message, $this->params);
return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'prop.path', null, null, $code);
}

private function getBuilder($name = 'name', $propertyPath = null, $dataClass = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
*/
private $message;

/**
* @var string
*/
private $messageTemplate;

/**
* @var array
*/
Expand All @@ -62,6 +67,7 @@ protected function setUp()
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$this->mapper = new ViolationMapper();
$this->message = 'Message';
$this->messageTemplate = 'Message template';
$this->params = array('foo' => 'bar');
}

Expand Down Expand Up @@ -101,15 +107,15 @@ private function getDataMapper()
*/
protected function getConstraintViolation($propertyPath)
{
return new ConstraintViolation($this->message, $this->params, null, $propertyPath, null);
return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, $propertyPath, null);
}

/**
* @return FormError
*/
protected function getFormError()
{
return new FormError($this->message, $this->params);
return new FormError($this->message, $this->messageTemplate, $this->params);
}

public function testMapToVirtualFormIfDataDoesNotMatch()
Expand Down

0 comments on commit 3b737b2

Please sign in to comment.