UniqueEntityValidatorTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bridge\Doctrine\Tests\Validator\Constraints;
  11. use Doctrine\Common\Collections\ArrayCollection;
  12. use Doctrine\Common\Persistence\ManagerRegistry;
  13. use Doctrine\Common\Persistence\ObjectManager;
  14. use Doctrine\Common\Persistence\ObjectRepository;
  15. use Doctrine\ORM\Tools\SchemaTool;
  16. use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
  17. use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity;
  18. use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity;
  19. use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNullableNameEntity;
  20. use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity;
  21. use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  22. use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
  23. use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
  24. use Symfony\Component\Validator\Validation;
  25. /**
  26. * @author Bernhard Schussek <bschussek@gmail.com>
  27. */
  28. class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
  29. {
  30. const EM_NAME = 'foo';
  31. /**
  32. * @var ObjectManager
  33. */
  34. protected $em;
  35. /**
  36. * @var ManagerRegistry
  37. */
  38. protected $registry;
  39. /**
  40. * @var ObjectRepository
  41. */
  42. protected $repository;
  43. protected function getApiVersion()
  44. {
  45. return Validation::API_VERSION_2_5;
  46. }
  47. protected function setUp()
  48. {
  49. $this->em = DoctrineTestHelper::createTestEntityManager();
  50. $this->registry = $this->createRegistryMock($this->em);
  51. $this->createSchema($this->em);
  52. parent::setUp();
  53. }
  54. protected function createRegistryMock(ObjectManager $em = null)
  55. {
  56. $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock();
  57. $registry->expects($this->any())
  58. ->method('getManager')
  59. ->with($this->equalTo(self::EM_NAME))
  60. ->will($this->returnValue($em));
  61. return $registry;
  62. }
  63. protected function createRepositoryMock()
  64. {
  65. $repository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository')
  66. ->setMethods(array('findByCustom', 'find', 'findAll', 'findOneBy', 'findBy', 'getClassName'))
  67. ->getMock()
  68. ;
  69. return $repository;
  70. }
  71. protected function createEntityManagerMock($repositoryMock)
  72. {
  73. $em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')
  74. ->getMock()
  75. ;
  76. $em->expects($this->any())
  77. ->method('getRepository')
  78. ->will($this->returnValue($repositoryMock))
  79. ;
  80. $classMetadata = $this->getMockBuilder('Doctrine\Common\Persistence\Mapping\ClassMetadata')->getMock();
  81. $classMetadata
  82. ->expects($this->any())
  83. ->method('hasField')
  84. ->will($this->returnValue(true))
  85. ;
  86. $reflParser = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionParser')
  87. ->disableOriginalConstructor()
  88. ->getMock()
  89. ;
  90. $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty')
  91. ->setConstructorArgs(array($reflParser, 'property-name'))
  92. ->setMethods(array('getValue'))
  93. ->getMock()
  94. ;
  95. $refl
  96. ->expects($this->any())
  97. ->method('getValue')
  98. ->will($this->returnValue(true))
  99. ;
  100. $classMetadata->reflFields = array('name' => $refl);
  101. $em->expects($this->any())
  102. ->method('getClassMetadata')
  103. ->will($this->returnValue($classMetadata))
  104. ;
  105. return $em;
  106. }
  107. protected function createValidator()
  108. {
  109. return new UniqueEntityValidator($this->registry);
  110. }
  111. private function createSchema(ObjectManager $em)
  112. {
  113. $schemaTool = new SchemaTool($em);
  114. $schemaTool->createSchema(array(
  115. $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'),
  116. $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity'),
  117. $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNullableNameEntity'),
  118. $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'),
  119. $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity'),
  120. ));
  121. }
  122. /**
  123. * This is a functional test as there is a large integration necessary to get the validator working.
  124. */
  125. public function testValidateUniqueness()
  126. {
  127. $constraint = new UniqueEntity(array(
  128. 'message' => 'myMessage',
  129. 'fields' => array('name'),
  130. 'em' => self::EM_NAME,
  131. ));
  132. $entity1 = new SingleIntIdEntity(1, 'Foo');
  133. $entity2 = new SingleIntIdEntity(2, 'Foo');
  134. $this->validator->validate($entity1, $constraint);
  135. $this->assertNoViolation();
  136. $this->em->persist($entity1);
  137. $this->em->flush();
  138. $this->validator->validate($entity1, $constraint);
  139. $this->assertNoViolation();
  140. $this->validator->validate($entity2, $constraint);
  141. $this->buildViolation('myMessage')
  142. ->atPath('property.path.name')
  143. ->setInvalidValue('Foo')
  144. ->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
  145. ->assertRaised();
  146. }
  147. public function testValidateCustomErrorPath()
  148. {
  149. $constraint = new UniqueEntity(array(
  150. 'message' => 'myMessage',
  151. 'fields' => array('name'),
  152. 'em' => self::EM_NAME,
  153. 'errorPath' => 'bar',
  154. ));
  155. $entity1 = new SingleIntIdEntity(1, 'Foo');
  156. $entity2 = new SingleIntIdEntity(2, 'Foo');
  157. $this->em->persist($entity1);
  158. $this->em->flush();
  159. $this->validator->validate($entity2, $constraint);
  160. $this->buildViolation('myMessage')
  161. ->atPath('property.path.bar')
  162. ->setInvalidValue('Foo')
  163. ->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
  164. ->assertRaised();
  165. }
  166. public function testValidateUniquenessWithNull()
  167. {
  168. $constraint = new UniqueEntity(array(
  169. 'message' => 'myMessage',
  170. 'fields' => array('name'),
  171. 'em' => self::EM_NAME,
  172. ));
  173. $entity1 = new SingleIntIdEntity(1, null);
  174. $entity2 = new SingleIntIdEntity(2, null);
  175. $this->em->persist($entity1);
  176. $this->em->persist($entity2);
  177. $this->em->flush();
  178. $this->validator->validate($entity1, $constraint);
  179. $this->assertNoViolation();
  180. }
  181. public function testValidateUniquenessWithIgnoreNullDisabled()
  182. {
  183. $constraint = new UniqueEntity(array(
  184. 'message' => 'myMessage',
  185. 'fields' => array('name', 'name2'),
  186. 'em' => self::EM_NAME,
  187. 'ignoreNull' => false,
  188. ));
  189. $entity1 = new DoubleNameEntity(1, 'Foo', null);
  190. $entity2 = new DoubleNameEntity(2, 'Foo', null);
  191. $this->validator->validate($entity1, $constraint);
  192. $this->assertNoViolation();
  193. $this->em->persist($entity1);
  194. $this->em->flush();
  195. $this->validator->validate($entity1, $constraint);
  196. $this->assertNoViolation();
  197. $this->validator->validate($entity2, $constraint);
  198. $this->buildViolation('myMessage')
  199. ->atPath('property.path.name')
  200. ->setInvalidValue('Foo')
  201. ->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
  202. ->assertRaised();
  203. }
  204. /**
  205. * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
  206. */
  207. public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgnoreNullEnabled()
  208. {
  209. $constraint = new UniqueEntity(array(
  210. 'message' => 'myMessage',
  211. 'fields' => array('name', 'name2'),
  212. 'em' => self::EM_NAME,
  213. 'ignoreNull' => true,
  214. ));
  215. $entity1 = new SingleIntIdEntity(1, null);
  216. $this->validator->validate($entity1, $constraint);
  217. }
  218. public function testNoValidationIfFirstFieldIsNullAndNullValuesAreIgnored()
  219. {
  220. $constraint = new UniqueEntity(array(
  221. 'message' => 'myMessage',
  222. 'fields' => array('name', 'name2'),
  223. 'em' => self::EM_NAME,
  224. 'ignoreNull' => true,
  225. ));
  226. $entity1 = new DoubleNullableNameEntity(1, null, 'Foo');
  227. $entity2 = new DoubleNullableNameEntity(2, null, 'Foo');
  228. $this->validator->validate($entity1, $constraint);
  229. $this->assertNoViolation();
  230. $this->em->persist($entity1);
  231. $this->em->flush();
  232. $this->validator->validate($entity1, $constraint);
  233. $this->assertNoViolation();
  234. $this->validator->validate($entity2, $constraint);
  235. $this->assertNoViolation();
  236. }
  237. public function testValidateUniquenessWithValidCustomErrorPath()
  238. {
  239. $constraint = new UniqueEntity(array(
  240. 'message' => 'myMessage',
  241. 'fields' => array('name', 'name2'),
  242. 'em' => self::EM_NAME,
  243. 'errorPath' => 'name2',
  244. ));
  245. $entity1 = new DoubleNameEntity(1, 'Foo', 'Bar');
  246. $entity2 = new DoubleNameEntity(2, 'Foo', 'Bar');
  247. $this->validator->validate($entity1, $constraint);
  248. $this->assertNoViolation();
  249. $this->em->persist($entity1);
  250. $this->em->flush();
  251. $this->validator->validate($entity1, $constraint);
  252. $this->assertNoViolation();
  253. $this->validator->validate($entity2, $constraint);
  254. $this->buildViolation('myMessage')
  255. ->atPath('property.path.name2')
  256. ->setInvalidValue('Bar')
  257. ->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
  258. ->assertRaised();
  259. }
  260. public function testValidateUniquenessUsingCustomRepositoryMethod()
  261. {
  262. $constraint = new UniqueEntity(array(
  263. 'message' => 'myMessage',
  264. 'fields' => array('name'),
  265. 'em' => self::EM_NAME,
  266. 'repositoryMethod' => 'findByCustom',
  267. ));
  268. $repository = $this->createRepositoryMock();
  269. $repository->expects($this->once())
  270. ->method('findByCustom')
  271. ->will($this->returnValue(array()))
  272. ;
  273. $this->em = $this->createEntityManagerMock($repository);
  274. $this->registry = $this->createRegistryMock($this->em);
  275. $this->validator = $this->createValidator();
  276. $this->validator->initialize($this->context);
  277. $entity1 = new SingleIntIdEntity(1, 'foo');
  278. $this->validator->validate($entity1, $constraint);
  279. $this->assertNoViolation();
  280. }
  281. public function testValidateUniquenessWithUnrewoundArray()
  282. {
  283. $constraint = new UniqueEntity(array(
  284. 'message' => 'myMessage',
  285. 'fields' => array('name'),
  286. 'em' => self::EM_NAME,
  287. 'repositoryMethod' => 'findByCustom',
  288. ));
  289. $entity = new SingleIntIdEntity(1, 'foo');
  290. $repository = $this->createRepositoryMock();
  291. $repository->expects($this->once())
  292. ->method('findByCustom')
  293. ->will(
  294. $this->returnCallback(function () use ($entity) {
  295. $returnValue = array(
  296. $entity,
  297. );
  298. next($returnValue);
  299. return $returnValue;
  300. })
  301. )
  302. ;
  303. $this->em = $this->createEntityManagerMock($repository);
  304. $this->registry = $this->createRegistryMock($this->em);
  305. $this->validator = $this->createValidator();
  306. $this->validator->initialize($this->context);
  307. $this->validator->validate($entity, $constraint);
  308. $this->assertNoViolation();
  309. }
  310. /**
  311. * @dataProvider resultTypesProvider
  312. */
  313. public function testValidateResultTypes($entity1, $result)
  314. {
  315. $constraint = new UniqueEntity(array(
  316. 'message' => 'myMessage',
  317. 'fields' => array('name'),
  318. 'em' => self::EM_NAME,
  319. 'repositoryMethod' => 'findByCustom',
  320. ));
  321. $repository = $this->createRepositoryMock();
  322. $repository->expects($this->once())
  323. ->method('findByCustom')
  324. ->will($this->returnValue($result))
  325. ;
  326. $this->em = $this->createEntityManagerMock($repository);
  327. $this->registry = $this->createRegistryMock($this->em);
  328. $this->validator = $this->createValidator();
  329. $this->validator->initialize($this->context);
  330. $this->validator->validate($entity1, $constraint);
  331. $this->assertNoViolation();
  332. }
  333. public function resultTypesProvider()
  334. {
  335. $entity = new SingleIntIdEntity(1, 'foo');
  336. return array(
  337. array($entity, array($entity)),
  338. array($entity, new \ArrayIterator(array($entity))),
  339. array($entity, new ArrayCollection(array($entity))),
  340. );
  341. }
  342. public function testAssociatedEntity()
  343. {
  344. $constraint = new UniqueEntity(array(
  345. 'message' => 'myMessage',
  346. 'fields' => array('single'),
  347. 'em' => self::EM_NAME,
  348. ));
  349. $entity1 = new SingleIntIdEntity(1, 'foo');
  350. $associated = new AssociationEntity();
  351. $associated->single = $entity1;
  352. $associated2 = new AssociationEntity();
  353. $associated2->single = $entity1;
  354. $this->em->persist($entity1);
  355. $this->em->persist($associated);
  356. $this->em->flush();
  357. $this->validator->validate($associated, $constraint);
  358. $this->assertNoViolation();
  359. $this->em->persist($associated2);
  360. $this->em->flush();
  361. $this->validator->validate($associated2, $constraint);
  362. $this->buildViolation('myMessage')
  363. ->atPath('property.path.single')
  364. ->setInvalidValue($entity1)
  365. ->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
  366. ->assertRaised();
  367. }
  368. public function testAssociatedEntityWithNull()
  369. {
  370. $constraint = new UniqueEntity(array(
  371. 'message' => 'myMessage',
  372. 'fields' => array('single'),
  373. 'em' => self::EM_NAME,
  374. 'ignoreNull' => false,
  375. ));
  376. $associated = new AssociationEntity();
  377. $associated->single = null;
  378. $this->em->persist($associated);
  379. $this->em->flush();
  380. $this->validator->validate($associated, $constraint);
  381. $this->assertNoViolation();
  382. }
  383. /**
  384. * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
  385. * @expectedExceptionMessage Object manager "foo" does not exist.
  386. */
  387. public function testDedicatedEntityManagerNullObject()
  388. {
  389. $constraint = new UniqueEntity(array(
  390. 'message' => 'myMessage',
  391. 'fields' => array('name'),
  392. 'em' => self::EM_NAME,
  393. ));
  394. $this->em = null;
  395. $this->registry = $this->createRegistryMock($this->em);
  396. $this->validator = $this->createValidator();
  397. $this->validator->initialize($this->context);
  398. $entity = new SingleIntIdEntity(1, null);
  399. $this->validator->validate($entity, $constraint);
  400. }
  401. /**
  402. * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
  403. * @expectedExceptionMessage Unable to find the object manager associated with an entity of class "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity"
  404. */
  405. public function testEntityManagerNullObject()
  406. {
  407. $constraint = new UniqueEntity(array(
  408. 'message' => 'myMessage',
  409. 'fields' => array('name'),
  410. // no "em" option set
  411. ));
  412. $this->em = null;
  413. $this->registry = $this->createRegistryMock($this->em);
  414. $this->validator = $this->createValidator();
  415. $this->validator->initialize($this->context);
  416. $entity = new SingleIntIdEntity(1, null);
  417. $this->validator->validate($entity, $constraint);
  418. }
  419. public function testValidateUniquenessOnNullResult()
  420. {
  421. $repository = $this->createRepositoryMock();
  422. $repository
  423. ->method('find')
  424. ->will($this->returnValue(null))
  425. ;
  426. $this->em = $this->createEntityManagerMock($repository);
  427. $this->registry = $this->createRegistryMock($this->em);
  428. $this->validator = $this->createValidator();
  429. $this->validator->initialize($this->context);
  430. $constraint = new UniqueEntity(array(
  431. 'message' => 'myMessage',
  432. 'fields' => array('name'),
  433. 'em' => self::EM_NAME,
  434. ));
  435. $entity = new SingleIntIdEntity(1, null);
  436. $this->em->persist($entity);
  437. $this->em->flush();
  438. $this->validator->validate($entity, $constraint);
  439. $this->assertNoViolation();
  440. }
  441. }