ObjectConstructorTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <?php
  2. namespace JMS\Serializer\Tests\Serializer\Doctrine;
  3. use Doctrine\Common\Annotations\AnnotationReader;
  4. use Doctrine\Common\Annotations\Reader;
  5. use Doctrine\Common\Persistence\AbstractManagerRegistry;
  6. use Doctrine\Common\Persistence\ManagerRegistry;
  7. use Doctrine\DBAL\Connection;
  8. use Doctrine\DBAL\DriverManager;
  9. use Doctrine\ORM\Configuration;
  10. use Doctrine\ORM\EntityManager;
  11. use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
  12. use Doctrine\ORM\ORMException;
  13. use Doctrine\ORM\Tools\SchemaTool;
  14. use Doctrine\ORM\UnitOfWork;
  15. use JMS\Serializer\Builder\CallbackDriverFactory;
  16. use JMS\Serializer\Builder\DefaultDriverFactory;
  17. use JMS\Serializer\Construction\DoctrineObjectConstructor;
  18. use JMS\Serializer\Construction\ObjectConstructorInterface;
  19. use JMS\Serializer\Construction\UnserializeObjectConstructor;
  20. use JMS\Serializer\DeserializationContext;
  21. use JMS\Serializer\Metadata\ClassMetadata;
  22. use JMS\Serializer\Metadata\Driver\DoctrineTypeDriver;
  23. use JMS\Serializer\Serializer;
  24. use JMS\Serializer\SerializerBuilder;
  25. use JMS\Serializer\Tests\Fixtures\Doctrine\Author;
  26. use JMS\Serializer\Tests\Fixtures\Doctrine\IdentityFields\Server;
  27. use JMS\Serializer\Tests\Fixtures\Doctrine\SingleTableInheritance\Excursion;
  28. use JMS\Serializer\VisitorInterface;
  29. class ObjectConstructorTest extends \PHPUnit_Framework_TestCase
  30. {
  31. /** @var ManagerRegistry */
  32. private $registry;
  33. /** @var Serializer */
  34. private $serializer;
  35. /** @var VisitorInterface */
  36. private $visitor;
  37. /** @var DeserializationContext */
  38. private $context;
  39. public function testFindEntity()
  40. {
  41. $em = $this->registry->getManager();
  42. $author = new Author('John', 5);
  43. $em->persist($author);
  44. $em->flush();
  45. $em->clear();
  46. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  47. $type = array('name' => Author::class, 'params' => array());
  48. $class = new ClassMetadata(Author::class);
  49. $constructor = new DoctrineObjectConstructor($this->registry, $fallback);
  50. $authorFetched = $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  51. $this->assertEquals($author, $authorFetched);
  52. }
  53. public function testFindManagedEntity()
  54. {
  55. $em = $this->registry->getManager();
  56. $author = new Author('John', 5);
  57. $em->persist($author);
  58. $em->flush();
  59. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  60. $type = array('name' => Author::class, 'params' => array());
  61. $class = new ClassMetadata(Author::class);
  62. $constructor = new DoctrineObjectConstructor($this->registry, $fallback);
  63. $authorFetched = $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  64. $this->assertSame($author, $authorFetched);
  65. }
  66. public function testMissingAuthor()
  67. {
  68. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  69. $type = array('name' => Author::class, 'params' => array());
  70. $class = new ClassMetadata(Author::class);
  71. $constructor = new DoctrineObjectConstructor($this->registry, $fallback);
  72. $author = $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  73. $this->assertNull($author);
  74. }
  75. public function testMissingAuthorFallback()
  76. {
  77. $author = new Author('John');
  78. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  79. $fallback->expects($this->once())->method('construct')->willReturn($author);
  80. $type = array('name' => Author::class, 'params' => array());
  81. $class = new ClassMetadata(Author::class);
  82. $constructor = new DoctrineObjectConstructor($this->registry, $fallback, DoctrineObjectConstructor::ON_MISSING_FALLBACK);
  83. $authorFetched = $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  84. $this->assertSame($author, $authorFetched);
  85. }
  86. public function testMissingNotManaged()
  87. {
  88. $author = new \JMS\Serializer\Tests\Fixtures\DoctrinePHPCR\Author('foo');
  89. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  90. $fallback->expects($this->once())->method('construct')->willReturn($author);
  91. $type = array('name' => Author::class, 'params' => array());
  92. $class = new ClassMetadata(Author::class);
  93. $constructor = new DoctrineObjectConstructor($this->registry, $fallback, DoctrineObjectConstructor::ON_MISSING_FALLBACK);
  94. $authorFetched = $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  95. $this->assertSame($author, $authorFetched);
  96. }
  97. public function testReference()
  98. {
  99. $em = $this->registry->getManager();
  100. $author = new Author('John', 5);
  101. $em->persist($author);
  102. $em->flush();
  103. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  104. $type = array('name' => Author::class, 'params' => array());
  105. $class = new ClassMetadata(Author::class);
  106. $constructor = new DoctrineObjectConstructor($this->registry, $fallback, DoctrineObjectConstructor::ON_MISSING_FALLBACK);
  107. $authorFetched = $constructor->construct($this->visitor, $class, 5, $type, $this->context);
  108. $this->assertSame($author, $authorFetched);
  109. }
  110. /**
  111. * @expectedException \JMS\Serializer\Exception\ObjectConstructionException
  112. */
  113. public function testMissingAuthorException()
  114. {
  115. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  116. $type = array('name' => Author::class, 'params' => array());
  117. $class = new ClassMetadata(Author::class);
  118. $constructor = new DoctrineObjectConstructor($this->registry, $fallback, DoctrineObjectConstructor::ON_MISSING_EXCEPTION);
  119. $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  120. }
  121. /**
  122. * @expectedException \JMS\Serializer\Exception\InvalidArgumentException
  123. */
  124. public function testInvalidArg()
  125. {
  126. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  127. $type = array('name' => Author::class, 'params' => array());
  128. $class = new ClassMetadata(Author::class);
  129. $constructor = new DoctrineObjectConstructor($this->registry, $fallback, 'foo');
  130. $constructor->construct($this->visitor, $class, ['id' => 5], $type, $this->context);
  131. }
  132. public function testMissingData()
  133. {
  134. $author = new Author('John');
  135. $fallback = $this->getMockBuilder(ObjectConstructorInterface::class)->getMock();
  136. $fallback->expects($this->once())->method('construct')->willReturn($author);
  137. $type = array('name' => Author::class, 'params' => array());
  138. $class = new ClassMetadata(Author::class);
  139. $constructor = new DoctrineObjectConstructor($this->registry, $fallback, 'foo');
  140. $authorFetched = $constructor->construct($this->visitor, $class, ['foo' => 5], $type, $this->context);
  141. $this->assertSame($author, $authorFetched);
  142. }
  143. public function testNamingForIdentifierColumnIsConsidered()
  144. {
  145. $serializer = $this->createSerializerWithDoctrineObjectConstructor();
  146. /** @var EntityManager $em */
  147. $em = $this->registry->getManager();
  148. $server = new Server('Linux', '127.0.0.1', 'home');
  149. $em->persist($server);
  150. $em->flush();
  151. $em->clear();
  152. $jsonData = '{"ip_address":"127.0.0.1", "server_id_extracted":"home", "name":"Windows"}';
  153. /** @var Server $serverDeserialized */
  154. $serverDeserialized = $serializer->deserialize($jsonData, Server::class, 'json');
  155. static::assertSame(
  156. $em->getUnitOfWork()->getEntityState($serverDeserialized),
  157. UnitOfWork::STATE_MANAGED
  158. );
  159. }
  160. protected function setUp()
  161. {
  162. $this->visitor = $this->getMockBuilder('JMS\Serializer\VisitorInterface')->getMock();
  163. $this->context = $this->getMockBuilder('JMS\Serializer\DeserializationContext')->getMock();
  164. $connection = $this->createConnection();
  165. $entityManager = $this->createEntityManager($connection);
  166. $this->registry = $registry = new SimpleBaseManagerRegistry(
  167. function ($id) use ($connection, $entityManager) {
  168. switch ($id) {
  169. case 'default_connection':
  170. return $connection;
  171. case 'default_manager':
  172. return $entityManager;
  173. default:
  174. throw new \RuntimeException(sprintf('Unknown service id "%s".', $id));
  175. }
  176. }
  177. );
  178. $this->serializer = SerializerBuilder::create()
  179. ->setMetadataDriverFactory(new CallbackDriverFactory(
  180. function (array $metadataDirs, Reader $annotationReader) use ($registry) {
  181. $defaultFactory = new DefaultDriverFactory();
  182. return new DoctrineTypeDriver($defaultFactory->createDriver($metadataDirs, $annotationReader), $registry);
  183. }
  184. ))
  185. ->build();
  186. $this->prepareDatabase();
  187. }
  188. private function prepareDatabase()
  189. {
  190. /** @var EntityManager $em */
  191. $em = $this->registry->getManager();
  192. $tool = new SchemaTool($em);
  193. $tool->createSchema($em->getMetadataFactory()->getAllMetadata());
  194. }
  195. private function createConnection()
  196. {
  197. $con = DriverManager::getConnection(array(
  198. 'driver' => 'pdo_sqlite',
  199. 'memory' => true,
  200. ));
  201. return $con;
  202. }
  203. private function createEntityManager(Connection $con)
  204. {
  205. $cfg = new Configuration();
  206. $cfg->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), array(
  207. __DIR__ . '/../../Fixtures/Doctrine',
  208. )));
  209. $cfg->setAutoGenerateProxyClasses(true);
  210. $cfg->setProxyNamespace('JMS\Serializer\DoctrineProxy');
  211. $cfg->setProxyDir(sys_get_temp_dir() . '/serializer-test-proxies');
  212. $em = EntityManager::create($con, $cfg);
  213. return $em;
  214. }
  215. /**
  216. * @return \JMS\Serializer\SerializerInterface
  217. */
  218. private function createSerializerWithDoctrineObjectConstructor()
  219. {
  220. return SerializerBuilder::create()
  221. ->setObjectConstructor(
  222. new DoctrineObjectConstructor(
  223. $this->registry,
  224. new UnserializeObjectConstructor(),
  225. DoctrineObjectConstructor::ON_MISSING_FALLBACK
  226. )
  227. )
  228. ->addDefaultHandlers()
  229. ->build();
  230. }
  231. }
  232. \Doctrine\DBAL\Types\Type::addType('Author', 'Doctrine\DBAL\Types\StringType');
  233. \Doctrine\DBAL\Types\Type::addType('some_custom_type', 'Doctrine\DBAL\Types\StringType');
  234. class SimpleBaseManagerRegistry extends AbstractManagerRegistry
  235. {
  236. private $services = array();
  237. private $serviceCreator;
  238. public function __construct($serviceCreator, $name = 'anonymous', array $connections = array('default' => 'default_connection'), array $managers = array('default' => 'default_manager'), $defaultConnection = null, $defaultManager = null, $proxyInterface = 'Doctrine\Common\Persistence\Proxy')
  239. {
  240. if (null === $defaultConnection) {
  241. $defaultConnection = key($connections);
  242. }
  243. if (null === $defaultManager) {
  244. $defaultManager = key($managers);
  245. }
  246. parent::__construct($name, $connections, $managers, $defaultConnection, $defaultManager, $proxyInterface);
  247. if (!is_callable($serviceCreator)) {
  248. throw new \InvalidArgumentException('$serviceCreator must be a valid callable.');
  249. }
  250. $this->serviceCreator = $serviceCreator;
  251. }
  252. public function getService($name)
  253. {
  254. if (isset($this->services[$name])) {
  255. return $this->services[$name];
  256. }
  257. return $this->services[$name] = call_user_func($this->serviceCreator, $name);
  258. }
  259. public function resetService($name)
  260. {
  261. unset($this->services[$name]);
  262. }
  263. public function getAliasNamespace($alias)
  264. {
  265. foreach (array_keys($this->getManagers()) as $name) {
  266. $manager = $this->getManager($name);
  267. if ($manager instanceof EntityManager) {
  268. try {
  269. return $manager->getConfiguration()->getEntityNamespace($alias);
  270. } catch (ORMException $ex) {
  271. // Probably mapped by another entity manager, or invalid, just ignore this here.
  272. }
  273. } else {
  274. throw new \LogicException(sprintf('Unsupported manager type "%s".', get_class($manager)));
  275. }
  276. }
  277. throw new \RuntimeException(sprintf('The namespace alias "%s" is not known to any manager.', $alias));
  278. }
  279. }