bug #21656 [DoctrineBridge] Fixed validating custom doctrine type columns (dmaicher)

This PR was merged into the 3.2 branch.

Discussion
----------

[DoctrineBridge] Fixed validating custom doctrine type columns

| Q             | A
| ------------- | ---
| Branch?       | 3.1
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #21619
| License       | MIT
| Doc PR        | -

This fixes #21619 by not assuming the invalid `$value` is a Doctrine entity if its an object

Commits
-------

ad59370241 [DoctrineBridge] Fixed validating custom doctrine type columns
This commit is contained in:
Fabien Potencier 2017-02-21 07:00:37 -08:00
commit fbcd227763
5 changed files with 154 additions and 2 deletions

View File

@ -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\Bridge\Doctrine\Tests\Fixtures;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper;
/** @Entity */
class SingleIntIdStringWrapperNameEntity
{
/** @Id @Column(type="integer") */
protected $id;
/** @Column(type="string_wrapper", nullable=true) */
public $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}

View File

@ -0,0 +1,36 @@
<?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\Bridge\Doctrine\Tests\Fixtures\Type;
class StringWrapper
{
/**
* @var string
*/
private $string;
/**
* @param string $string
*/
public function __construct($string = null)
{
$this->string = $string;
}
/**
* @return string
*/
public function getString()
{
return $this->string;
}
}

View File

@ -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\Bridge\Doctrine\Tests\Fixtures\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\StringType;
class StringWrapperType extends StringType
{
/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value instanceof StringWrapper ? $value->getString() : null;
}
/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return new StringWrapper($value);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'string_wrapper';
}
}

View File

@ -15,6 +15,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\DBAL\Types\Type;
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
use Symfony\Bridge\Doctrine\Test\TestRepositoryFactory;
use Symfony\Bridge\Doctrine\Tests\Fixtures\Employee;
@ -26,6 +27,8 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNullableNameEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2;
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
@ -64,6 +67,10 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
$config = DoctrineTestHelper::createTestConfiguration();
$config->setRepositoryFactory($this->repositoryFactory);
if (!Type::hasType('string_wrapper')) {
Type::addType('string_wrapper', 'Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapperType');
}
$this->em = DoctrineTestHelper::createTestEntityManager($config);
$this->registry = $this->createRegistryMock($this->em);
$this->createSchema($this->em);
@ -150,6 +157,7 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Person'),
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Employee'),
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity'),
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity'),
));
}
@ -700,4 +708,31 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised();
}
public function testValidateUniquenessWithCustomDoctrineTypeValue()
{
$constraint = new UniqueEntity(array(
'message' => 'myMessage',
'fields' => array('name'),
'em' => self::EM_NAME,
));
$existingEntity = new SingleIntIdStringWrapperNameEntity(1, new StringWrapper('foo'));
$this->em->persist($existingEntity);
$this->em->flush();
$newEntity = new SingleIntIdStringWrapperNameEntity(2, new StringWrapper('foo'));
$this->validator->validate($newEntity, $constraint);
$expectedValue = 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper")';
$this->buildViolation('myMessage')
->atPath('property.path.name')
->setParameter('{{ value }}', $expectedValue)
->setInvalidValue($existingEntity->name)
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised();
}
}

View File

@ -176,9 +176,15 @@ class UniqueEntityValidator extends ConstraintValidator
return $this->formatValue($value, self::PRETTY_DATE);
}
// non unique value is a composite PK
if ($class->getName() !== $idClass = get_class($value)) {
$identifiers = $em->getClassMetadata($idClass)->getIdentifierValues($value);
// non unique value might be a composite PK that consists of other entity objects
if ($em->getMetadataFactory()->hasMetadataFor($idClass)) {
$identifiers = $em->getClassMetadata($idClass)->getIdentifierValues($value);
} else {
// this case might happen if the non unique column has a custom doctrine type and its value is an object
// in which case we cannot get any identifiers for it
$identifiers = array();
}
} else {
$identifiers = $class->getIdentifierValues($value);
}