Skip to content

Commit

Permalink
[Doctrine Bridge] fix UniqueEntityValidator for composite object prim…
Browse files Browse the repository at this point in the history
…ary keys
  • Loading branch information
dmaicher committed Jan 14, 2017
1 parent cbb5332 commit 3fd23b7
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 1 deletion.
@@ -0,0 +1,52 @@
<?php

namespace Symfony\Bridge\Doctrine\Tests\Fixtures;
use Doctrine\ORM\Mapping as ORM;

/**
* an entity that has two objects (class without toString methods) as primary key
*
* @ORM\Entity
*/
class CompositeObjectNoToStringIdEntity
{
/**
* @var SingleIntIdNoToStringEntity
*
* @ORM\Id
* @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"})
* @ORM\JoinColumn(name="object_one_id")
*/
protected $objectOne;

/**
* @var SingleIntIdNoToStringEntity
*
* @ORM\Id
* @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"})
* @ORM\JoinColumn(name="object_two_id")
*/
protected $objectTwo;

public function __construct(SingleIntIdNoToStringEntity $objectOne, SingleIntIdNoToStringEntity $objectTwo)
{
$this->objectOne = $objectOne;
$this->objectTwo = $objectTwo;
}

/**
* @return SingleIntIdNoToStringEntity
*/
public function getObjectOne()
{
return $this->objectOne;
}

/**
* @return SingleIntIdNoToStringEntity
*/
public function getObjectTwo()
{
return $this->objectTwo;
}
}
Expand Up @@ -17,6 +17,7 @@
use Doctrine\Common\Persistence\ObjectRepository;
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
use Symfony\Bridge\Doctrine\Test\TestRepositoryFactory;
use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity;
Expand Down Expand Up @@ -140,6 +141,7 @@ private function createSchema(ObjectManager $em)
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'),
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity'),
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2'),
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity'),
));
}

Expand Down Expand Up @@ -561,4 +563,33 @@ public function testEntityManagerNullObject()

$this->validator->validate($entity, $constraint);
}

public function testValidateUniquenessWithCompositeObjectNoToStringIdEntity()
{
$constraint = new UniqueEntity(array(
'message' => 'myMessage',
'fields' => array('objectOne', 'objectTwo'),
'em' => self::EM_NAME,
));

$objectOne = new SingleIntIdNoToStringEntity(1, 'foo');
$objectTwo = new SingleIntIdNoToStringEntity(2, 'bar');
$entity = new CompositeObjectNoToStringIdEntity($objectOne, $objectTwo);

$this->em->persist($entity);
$this->em->flush();

$newEntity = new CompositeObjectNoToStringIdEntity($objectOne, $objectTwo);

$this->validator->validate($newEntity, $constraint);

$expectedValue = 'Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity" identified by "(Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity" identified by "1"), (Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity" identified by "2")"';

$this->buildViolation('myMessage')
->atPath('property.path.objectOne')
->setParameter('{{ value }}', '"'.$expectedValue.'"')
->setInvalidValue($expectedValue)
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->assertRaised();
}
}
Expand Up @@ -12,6 +12,8 @@
namespace Symfony\Bridge\Doctrine\Validator\Constraints;

use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
Expand Down Expand Up @@ -128,7 +130,7 @@ public function validate($entity, Constraint $constraint)
$invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]];

if (is_object($invalidValue) && !method_exists($invalidValue, '__toString')) {
$invalidValue = sprintf('Object of class "%s" identified by "%s"', get_class($entity), implode(', ', $class->getIdentifierValues($entity)));
$invalidValue = $this->buildInvalidValueString($em, $class, $entity);
}

$this->context->buildViolation($constraint->message)
Expand All @@ -138,4 +140,25 @@ public function validate($entity, Constraint $constraint)
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
->addViolation();
}

/**
* @param ObjectManager $em
* @param ClassMetadata $class
* @param object $entity
*
* @return string
*/
private function buildInvalidValueString(ObjectManager $em, ClassMetadata $class, $entity)
{
$identifiers = array_map(function ($identifier) use ($em) {
// identifiers can be objects (without any __toString method) if its a composite PK
if (is_object($identifier) && !method_exists($identifier, '__toString')) {
return sprintf('(%s)', $this->buildInvalidValueString($em, $em->getClassMetadata(get_class($identifier)), $identifier));
}

return $identifier;
}, $class->getIdentifierValues($entity));

return sprintf('Object of class "%s" identified by "%s"', get_class($entity), implode(', ', $identifiers));
}
}

0 comments on commit 3fd23b7

Please sign in to comment.