/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php

https://github.com/adrienbrault/doctrine2 · PHP · 379 lines · 230 code · 68 blank · 81 comment · 0 complexity · 03e2b8af58bb488e28b824a866b9a0e0 MD5 · raw file

  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Tests\ORM\Functional;
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use Doctrine\ORM\Annotation as ORM;
  6. use Doctrine\ORM\Tools\SchemaTool;
  7. use Doctrine\Tests\OrmFunctionalTestCase;
  8. use ProxyManager\Proxy\GhostObjectInterface;
  9. /**
  10. * @group DDC-952
  11. */
  12. class OneToOneEagerLoadingTest extends OrmFunctionalTestCase
  13. {
  14. protected function setUp()
  15. {
  16. parent::setUp();
  17. $schemaTool = new SchemaTool($this->em);
  18. try {
  19. $schemaTool->createSchema(
  20. [
  21. $this->em->getClassMetadata(Train::class),
  22. $this->em->getClassMetadata(TrainDriver::class),
  23. $this->em->getClassMetadata(TrainOwner::class),
  24. $this->em->getClassMetadata(Waggon::class),
  25. $this->em->getClassMetadata(TrainOrder::class),
  26. ]
  27. );
  28. } catch (\Exception $e) {
  29. }
  30. }
  31. /**
  32. * @group non-cacheable
  33. */
  34. public function testEagerLoadOneToOneOwningSide()
  35. {
  36. $train = new Train(new TrainOwner("Alexander"));
  37. $driver = new TrainDriver("Benjamin");
  38. $waggon = new Waggon();
  39. $train->setDriver($driver);
  40. $train->addWaggon($waggon);
  41. $this->em->persist($train); // cascades
  42. $this->em->flush();
  43. $this->em->clear();
  44. $sqlCount = count($this->sqlLoggerStack->queries);
  45. $train = $this->em->find(get_class($train), $train->id);
  46. self::assertNotInstanceOf(GhostObjectInterface::class, $train->driver);
  47. self::assertEquals('Benjamin', $train->driver->name);
  48. self::assertCount($sqlCount + 1, $this->sqlLoggerStack->queries);
  49. }
  50. /**
  51. * @group non-cacheable
  52. */
  53. public function testEagerLoadOneToOneNullOwningSide()
  54. {
  55. $train = new Train(new TrainOwner("Alexander"));
  56. $this->em->persist($train); // cascades
  57. $this->em->flush();
  58. $this->em->clear();
  59. $sqlCount = count($this->sqlLoggerStack->queries);
  60. $train = $this->em->find(get_class($train), $train->id);
  61. self::assertNotInstanceOf(GhostObjectInterface::class, $train->driver);
  62. self::assertNull($train->driver);
  63. self::assertCount($sqlCount + 1, $this->sqlLoggerStack->queries);
  64. }
  65. /**
  66. * @group non-cacheable
  67. */
  68. public function testEagerLoadOneToOneInverseSide()
  69. {
  70. $owner = new TrainOwner("Alexander");
  71. $train = new Train($owner);
  72. $this->em->persist($train); // cascades
  73. $this->em->flush();
  74. $this->em->clear();
  75. $sqlCount = count($this->sqlLoggerStack->queries);
  76. $driver = $this->em->find(get_class($owner), $owner->id);
  77. self::assertNotInstanceOf(GhostObjectInterface::class, $owner->train);
  78. self::assertNotNull($owner->train);
  79. self::assertCount($sqlCount + 1, $this->sqlLoggerStack->queries);
  80. }
  81. /**
  82. * @group non-cacheable
  83. */
  84. public function testEagerLoadOneToOneNullInverseSide()
  85. {
  86. $driver = new TrainDriver("Dagny Taggert");
  87. $this->em->persist($driver);
  88. $this->em->flush();
  89. $this->em->clear();
  90. self::assertNull($driver->train);
  91. $sqlCount = count($this->sqlLoggerStack->queries);
  92. $driver = $this->em->find(get_class($driver), $driver->id);
  93. self::assertNotInstanceOf(GhostObjectInterface::class, $driver->train);
  94. self::assertNull($driver->train);
  95. self::assertCount($sqlCount + 1, $this->sqlLoggerStack->queries);
  96. }
  97. public function testEagerLoadManyToOne()
  98. {
  99. $train = new Train(new TrainOwner("Alexander"));
  100. $waggon = new Waggon();
  101. $train->addWaggon($waggon);
  102. $this->em->persist($train); // cascades
  103. $this->em->flush();
  104. $this->em->clear();
  105. $waggon = $this->em->find(get_class($waggon), $waggon->id);
  106. self::assertNotInstanceOf(GhostObjectInterface::class, $waggon->train);
  107. self::assertNotNull($waggon->train);
  108. }
  109. /**
  110. * @group non-cacheable
  111. */
  112. public function testEagerLoadWithNullableColumnsGeneratesLeftJoinOnBothSides()
  113. {
  114. $train = new Train(new TrainOwner("Alexander"));
  115. $driver = new TrainDriver("Benjamin");
  116. $train->setDriver($driver);
  117. $this->em->persist($train);
  118. $this->em->flush();
  119. $this->em->clear();
  120. $this->em->find(get_class($train), $train->id);
  121. self::assertSQLEquals(
  122. 'SELECT t0."id" AS c1, t0."driver_id" AS c2, t4."id" AS c3, t4."name" AS c5, t0."owner_id" AS c6, t8."id" AS c7, t8."name" AS c9 FROM "Train" t0 LEFT JOIN "TrainDriver" t4 ON t0."driver_id" = t4."id" INNER JOIN "TrainOwner" t8 ON t0."owner_id" = t8."id" WHERE t0."id" = ?',
  123. $this->sqlLoggerStack->queries[$this->sqlLoggerStack->currentQuery]['sql']
  124. );
  125. $this->em->clear();
  126. $this->em->find(get_class($driver), $driver->id);
  127. self::assertSQLEquals(
  128. 'SELECT t0."id" AS c1, t0."name" AS c2, t4."id" AS c3, t4."driver_id" AS c5, t4."owner_id" AS c6 FROM "TrainOwner" t0 LEFT JOIN "Train" t4 ON t4."owner_id" = t0."id" WHERE t0."id" IN (?)',
  129. $this->sqlLoggerStack->queries[$this->sqlLoggerStack->currentQuery]['sql']
  130. );
  131. }
  132. /**
  133. * @group non-cacheable
  134. */
  135. public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoinOnOwningSide()
  136. {
  137. $waggon = new Waggon();
  138. // It should have a train
  139. $train = new Train(new TrainOwner("Alexander"));
  140. $train->addWaggon($waggon);
  141. $this->em->persist($train);
  142. $this->em->flush();
  143. $this->em->clear();
  144. $this->em->find(get_class($waggon), $waggon->id);
  145. // The last query is the eager loading of the owner of the train
  146. self::assertSQLEquals(
  147. 'SELECT t0."id" AS c1, t0."name" AS c2, t4."id" AS c3, t4."driver_id" AS c5, t4."owner_id" AS c6 FROM "TrainOwner" t0 LEFT JOIN "Train" t4 ON t4."owner_id" = t0."id" WHERE t0."id" IN (?)',
  148. $this->sqlLoggerStack->queries[$this->sqlLoggerStack->currentQuery]['sql']
  149. );
  150. // The one before is the fetching of the waggon and train
  151. self::assertSQLEquals(
  152. 'SELECT t0."id" AS c1, t0."train_id" AS c2, t4."id" AS c3, t4."driver_id" AS c5, t4."owner_id" AS c6 FROM "Waggon" t0 INNER JOIN "Train" t4 ON t0."train_id" = t4."id" WHERE t0."id" = ?',
  153. $this->sqlLoggerStack->queries[$this->sqlLoggerStack->currentQuery - 1]['sql']
  154. );
  155. }
  156. /**
  157. * @group non-cacheable
  158. */
  159. public function testEagerLoadWithNonNullableColumnsGeneratesLeftJoinOnNonOwningSide()
  160. {
  161. $owner = new TrainOwner('Alexander');
  162. $train = new Train($owner);
  163. $this->em->persist($train);
  164. $this->em->flush();
  165. $this->em->clear();
  166. $this->em->find(get_class($owner), $owner->id);
  167. self::assertSQLEquals(
  168. 'SELECT t0."id" AS c1, t0."name" AS c2, t4."id" AS c3, t4."driver_id" AS c5, t4."owner_id" AS c6 FROM "TrainOwner" t0 LEFT JOIN "Train" t4 ON t4."owner_id" = t0."id" WHERE t0."id" = ?',
  169. $this->sqlLoggerStack->queries[$this->sqlLoggerStack->currentQuery]['sql']
  170. );
  171. }
  172. /**
  173. * @group DDC-1946
  174. */
  175. public function testEagerLoadingDoesNotBreakRefresh()
  176. {
  177. $train = new Train(new TrainOwner('Johannes'));
  178. $order = new TrainOrder($train);
  179. $this->em->persist($train);
  180. $this->em->persist($order);
  181. $this->em->flush();
  182. $this->em->getConnection()->exec("UPDATE TrainOrder SET train_id = NULL");
  183. self::assertSame($train, $order->train);
  184. $this->em->refresh($order);
  185. self::assertNull($order->train, "Train reference was not refreshed to NULL.");
  186. }
  187. }
  188. /**
  189. * @ORM\Entity
  190. */
  191. class Train
  192. {
  193. /**
  194. * @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue
  195. * @var int
  196. */
  197. public $id;
  198. /**
  199. * Owning side
  200. * @ORM\OneToOne(targetEntity=TrainDriver::class, inversedBy="train", fetch="EAGER", cascade={"persist"})
  201. * @ORM\JoinColumn(nullable=true)
  202. */
  203. public $driver;
  204. /**
  205. * Owning side
  206. * @ORM\OneToOne(targetEntity=TrainOwner::class, inversedBy="train", fetch="EAGER", cascade={"persist"})
  207. * @ORM\JoinColumn(nullable=false)
  208. */
  209. public $owner;
  210. /**
  211. * @ORM\OneToMany(targetEntity=Waggon::class, mappedBy="train", cascade={"persist"})
  212. */
  213. public $waggons;
  214. public function __construct(TrainOwner $owner)
  215. {
  216. $this->waggons = new ArrayCollection();
  217. $this->setOwner($owner);
  218. }
  219. public function setDriver(TrainDriver $driver)
  220. {
  221. $this->driver = $driver;
  222. $driver->setTrain($this);
  223. }
  224. public function setOwner(TrainOwner $owner)
  225. {
  226. $this->owner = $owner;
  227. $owner->setTrain($this);
  228. }
  229. public function addWaggon(Waggon $w)
  230. {
  231. $w->setTrain($this);
  232. $this->waggons[] = $w;
  233. }
  234. }
  235. /**
  236. * @ORM\Entity
  237. */
  238. class TrainDriver
  239. {
  240. /** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
  241. public $id;
  242. /** @ORM\Column(type="string") */
  243. public $name;
  244. /**
  245. * Inverse side
  246. * @ORM\OneToOne(targetEntity=Train::class, mappedBy="driver", fetch="EAGER")
  247. */
  248. public $train;
  249. public function __construct($name)
  250. {
  251. $this->name = $name;
  252. }
  253. public function setTrain(Train $t)
  254. {
  255. $this->train = $t;
  256. }
  257. }
  258. /**
  259. * @ORM\Entity
  260. */
  261. class TrainOwner
  262. {
  263. /** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
  264. public $id;
  265. /** @ORM\Column(type="string") */
  266. public $name;
  267. /**
  268. * Inverse side
  269. * @ORM\OneToOne(targetEntity=Train::class, mappedBy="owner", fetch="EAGER")
  270. */
  271. public $train;
  272. public function __construct($name)
  273. {
  274. $this->name = $name;
  275. }
  276. public function setTrain(Train $t)
  277. {
  278. $this->train = $t;
  279. }
  280. }
  281. /**
  282. * @ORM\Entity
  283. */
  284. class Waggon
  285. {
  286. /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
  287. public $id;
  288. /**
  289. * @ORM\ManyToOne(targetEntity=Train::class, inversedBy="waggons", fetch="EAGER")
  290. * @ORM\JoinColumn(nullable=false)
  291. */
  292. public $train;
  293. public function setTrain($train)
  294. {
  295. $this->train = $train;
  296. }
  297. }
  298. /**
  299. * @ORM\Entity
  300. */
  301. class TrainOrder
  302. {
  303. /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
  304. public $id;
  305. /** @ORM\OneToOne(targetEntity = Train::class, fetch = "EAGER") */
  306. public $train;
  307. public function __construct(Train $train)
  308. {
  309. $this->train = $train;
  310. }
  311. }