SoftDeleteableEntityTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <?php
  2. namespace Gedmo\SoftDeleteable;
  3. use Tool\BaseTestCaseORM;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\Common\Util\Debug,
  6. SoftDeleteable\Fixture\Entity\Article,
  7. SoftDeleteable\Fixture\Entity\Comment,
  8. SoftDeleteable\Fixture\Entity\User,
  9. SoftDeleteable\Fixture\Entity\Page,
  10. SoftDeleteable\Fixture\Entity\MegaPage,
  11. SoftDeleteable\Fixture\Entity\Module,
  12. SoftDeleteable\Fixture\Entity\OtherArticle,
  13. SoftDeleteable\Fixture\Entity\OtherComment,
  14. SoftDeleteable\Fixture\Entity\Child,
  15. Gedmo\SoftDeleteable\SoftDeleteableListener;
  16. /**
  17. * These are tests for SoftDeleteable behavior
  18. *
  19. * @author Gustavo Falco <comfortablynumb84@gmail.com>
  20. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  21. * @author Patrik Votoček <patrik@votocek.cz>
  22. * @link http://www.gediminasm.org
  23. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  24. */
  25. class SoftDeleteableEntityTest extends BaseTestCaseORM
  26. {
  27. const ARTICLE_CLASS = 'SoftDeleteable\Fixture\Entity\Article';
  28. const COMMENT_CLASS = 'SoftDeleteable\Fixture\Entity\Comment';
  29. const PAGE_CLASS = 'SoftDeleteable\Fixture\Entity\Page';
  30. const MEGA_PAGE_CLASS = 'SoftDeleteable\Fixture\Entity\MegaPage';
  31. const MODULE_CLASS = 'SoftDeleteable\Fixture\Entity\Module';
  32. const OTHER_ARTICLE_CLASS = 'SoftDeleteable\Fixture\Entity\OtherArticle';
  33. const OTHER_COMMENT_CLASS = 'SoftDeleteable\Fixture\Entity\OtherComment';
  34. const USER_CLASS = 'SoftDeleteable\Fixture\Entity\User';
  35. const MAPPED_SUPERCLASS_CHILD_CLASS = 'SoftDeleteable\Fixture\Entity\Child';
  36. const SOFT_DELETEABLE_FILTER_NAME = 'soft-deleteable';
  37. private $softDeleteableListener;
  38. protected function setUp()
  39. {
  40. parent::setUp();
  41. $evm = new EventManager();
  42. $this->softDeleteableListener = new SoftDeleteableListener();
  43. $evm->addEventSubscriber($this->softDeleteableListener);
  44. $config = $this->getMockAnnotatedConfig();
  45. $config->addFilter(self::SOFT_DELETEABLE_FILTER_NAME, 'Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter');
  46. $this->em = $this->getMockSqliteEntityManager($evm, $config);
  47. $this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);
  48. }
  49. /**
  50. * @test
  51. */
  52. public function shouldBeAbleToHardDeleteSoftdeletedItems()
  53. {
  54. $repo = $this->em->getRepository(self::USER_CLASS);
  55. $newUser = new User();
  56. $newUser->setUsername($username = 'test_user');
  57. $this->em->persist($newUser);
  58. $this->em->flush();
  59. $user = $repo->findOneBy(array('username' => $username));
  60. $this->assertNull($user->getDeletedAt());
  61. $this->em->remove($user);
  62. $this->em->flush();
  63. $user = $repo->findOneBy(array('username' => $username));
  64. $this->assertNull($user);
  65. }
  66. /**
  67. * @test
  68. */
  69. public function shouldSoftlyDeleteIfColumnNameDifferFromPropertyName()
  70. {
  71. $repo = $this->em->getRepository(self::USER_CLASS);
  72. $newUser = new User();
  73. $username = 'test_user';
  74. $newUser->setUsername($username);
  75. $this->em->persist($newUser);
  76. $this->em->flush();
  77. $user = $repo->findOneBy(array('username' => $username));
  78. $this->assertNull($user->getDeletedAt());
  79. $this->em->remove($user);
  80. $this->em->flush();
  81. $user = $repo->findOneBy(array('username' => $username));
  82. $this->assertNull($user, "User should be filtered out");
  83. // now deatcivate filter and attempt to hard delete
  84. $this->em->getFilters()->disable(self::SOFT_DELETEABLE_FILTER_NAME);
  85. $user = $repo->findOneBy(array('username' => $username));
  86. $this->assertNotNull($user, "User should be fetched when filter is disabled");
  87. $this->em->remove($user);
  88. $this->em->flush();
  89. $user = $repo->findOneBy(array('username' => $username));
  90. $this->assertNull($user, "User is still available after hard delete");
  91. }
  92. public function testSoftDeleteable()
  93. {
  94. $repo = $this->em->getRepository(self::ARTICLE_CLASS);
  95. $commentRepo = $this->em->getRepository(self::COMMENT_CLASS);
  96. $comment = new Comment();
  97. $commentField = 'comment';
  98. $commentValue = 'Comment 1';
  99. $comment->setComment($commentValue);
  100. $art0 = new Article();
  101. $field = 'title';
  102. $value = 'Title 1';
  103. $art0->setTitle($value);
  104. $art0->addComment($comment);
  105. $this->em->persist($art0);
  106. $this->em->flush();
  107. $art = $repo->findOneBy(array($field => $value));
  108. $this->assertNull($art->getDeletedAt());
  109. $this->assertNull($comment->getDeletedAt());
  110. $this->em->remove($art);
  111. $this->em->flush();
  112. $art = $repo->findOneBy(array($field => $value));
  113. $this->assertNull($art);
  114. $comment = $commentRepo->findOneBy(array($commentField => $commentValue));
  115. $this->assertNull($comment);
  116. // Now we deactivate the filter so we test if the entity appears in the result
  117. $this->em->getFilters()->disable(self::SOFT_DELETEABLE_FILTER_NAME);
  118. $art = $repo->findOneBy(array($field => $value));
  119. $this->assertTrue(is_object($art));
  120. $this->assertTrue(is_object($art->getDeletedAt()));
  121. $this->assertTrue($art->getDeletedAt() instanceof \DateTime);
  122. $comment = $commentRepo->findOneBy(array($commentField => $commentValue));
  123. $this->assertTrue(is_object($comment));
  124. $this->assertTrue(is_object($comment->getDeletedAt()));
  125. $this->assertTrue($comment->getDeletedAt() instanceof \DateTime);
  126. $this->em->createQuery('UPDATE '.self::ARTICLE_CLASS.' a SET a.deletedAt = NULL')->execute();
  127. $this->em->refresh($art);
  128. $this->em->refresh($comment);
  129. // Now we try with a DQL Delete query
  130. $this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);
  131. $dql = sprintf('DELETE FROM %s a WHERE a.%s = :%s',
  132. self::ARTICLE_CLASS, $field, $field);
  133. $query = $this->em->createQuery($dql);
  134. $query->setParameter($field, $value);
  135. $query->setHint(
  136. \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
  137. 'Gedmo\SoftDeleteable\Query\TreeWalker\SoftDeleteableWalker'
  138. );
  139. $query->execute();
  140. $art = $repo->findOneBy(array($field => $value));
  141. $this->assertNull($art);
  142. // Now we deactivate the filter so we test if the entity appears in the result
  143. $this->em->getFilters()->disable(self::SOFT_DELETEABLE_FILTER_NAME);
  144. $this->em->clear();
  145. $art = $repo->findOneBy(array($field => $value));
  146. $this->assertTrue(is_object($art));
  147. $this->assertTrue(is_object($art->getDeletedAt()));
  148. $this->assertTrue($art->getDeletedAt() instanceof \DateTime);
  149. // Inheritance tree DELETE DQL
  150. $this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);
  151. $megaPageRepo = $this->em->getRepository(self::MEGA_PAGE_CLASS);
  152. $module = new Module();
  153. $module->setTitle('Module 1');
  154. $page = new MegaPage();
  155. $page->setTitle('Page 1');
  156. $page->addModule($module);
  157. $module->setPage($page);
  158. $this->em->persist($page);
  159. $this->em->persist($module);
  160. $this->em->flush();
  161. $dql = sprintf('DELETE FROM %s p',
  162. self::PAGE_CLASS);
  163. $query = $this->em->createQuery($dql);
  164. $query->setHint(
  165. \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
  166. 'Gedmo\SoftDeleteable\Query\TreeWalker\SoftDeleteableWalker'
  167. );
  168. $query->execute();
  169. $p = $megaPageRepo->findOneBy(array('title' => 'Page 1'));
  170. $this->assertNull($p);
  171. // Now we deactivate the filter so we test if the entity appears in the result
  172. $this->em->getFilters()->disable(self::SOFT_DELETEABLE_FILTER_NAME);
  173. $this->em->clear();
  174. $p = $megaPageRepo->findOneBy(array('title' => 'Page 1'));
  175. $this->assertTrue(is_object($p));
  176. $this->assertTrue(is_object($p->getDeletedAt()));
  177. $this->assertTrue($p->getDeletedAt() instanceof \DateTime);
  178. // Test of #301
  179. $this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);
  180. $otherArticleRepo = $this->em->getRepository(self::OTHER_ARTICLE_CLASS);
  181. $otherCommentRepo = $this->em->getRepository(self::OTHER_COMMENT_CLASS);
  182. $otherArt = new OtherArticle();
  183. $otherComment = new OtherComment();
  184. $otherArt->setTitle('Page 1');
  185. $otherComment->setComment('Comment');
  186. $otherArt->addComment($otherComment);
  187. $otherComment->setArticle($otherArt);
  188. $this->em->persist($otherArt);
  189. $this->em->persist($otherComment);
  190. $this->em->flush();
  191. $this->em->refresh($otherArt);
  192. $this->em->refresh($otherComment);
  193. $artId = $otherArt->getId();
  194. $commentId = $otherComment->getId();
  195. $this->em->remove($otherArt);
  196. $this->em->flush();
  197. $foundArt = $otherArticleRepo->findOneBy(array('id' => $artId));
  198. $foundComment = $otherCommentRepo->findOneBy(array('id' => $commentId));
  199. $this->assertNull($foundArt);
  200. $this->assertTrue(is_object($foundComment));
  201. $this->assertInstanceOf(self::OTHER_COMMENT_CLASS, $foundComment);
  202. $this->em->getFilters()->disable(self::SOFT_DELETEABLE_FILTER_NAME);
  203. $foundArt = $otherArticleRepo->findOneById($artId);
  204. $foundComment = $otherCommentRepo->findOneById($commentId);
  205. $this->assertTrue(is_object($foundArt));
  206. $this->assertTrue(is_object($foundArt->getDeletedAt()));
  207. $this->assertTrue($foundArt->getDeletedAt() instanceof \DateTime);
  208. $this->assertTrue(is_object($foundComment));
  209. $this->assertInstanceOf(self::OTHER_COMMENT_CLASS, $foundComment);
  210. }
  211. /**
  212. * Make sure that soft delete also works when configured on a mapped superclass
  213. */
  214. public function testMappedSuperclass()
  215. {
  216. $child = new Child();
  217. $child->setTitle('test title');
  218. $this->em->persist($child);
  219. $this->em->flush();
  220. $this->em->remove($child);
  221. $this->em->flush();
  222. $this->em->clear();
  223. $repo = $this->em->getRepository(self::MAPPED_SUPERCLASS_CHILD_CLASS);
  224. $this->assertNull($repo->findOneById($child->getId()));
  225. $this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);
  226. $this->assertNotNull($repo->findById($child->getId()));
  227. }
  228. public function testSoftDeleteableFilter()
  229. {
  230. $filter = $this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);
  231. $filter->disableForEntity(self::USER_CLASS);
  232. $repo = $this->em->getRepository(self::USER_CLASS);
  233. $newUser = new User();
  234. $username = 'test_user';
  235. $newUser->setUsername($username);
  236. $this->em->persist($newUser);
  237. $this->em->flush();
  238. $user = $repo->findOneBy(array('username' => $username));
  239. $this->assertNull($user->getDeletedAt());
  240. $this->em->remove($user);
  241. $this->em->flush();
  242. $user = $repo->findOneBy(array('username' => $username));
  243. $this->assertNotNull($user->getDeletedAt());
  244. $filter->enableForEntity(self::USER_CLASS);
  245. $user = $repo->findOneBy(array('username' => $username));
  246. $this->assertNull($user);
  247. }
  248. public function testPostSoftDeleteEventIsDispatched()
  249. {
  250. $subscriber = $this->getMock(
  251. "Doctrine\Common\EventSubscriber",
  252. array(
  253. "getSubscribedEvents",
  254. "preSoftDelete",
  255. "postSoftDelete"
  256. )
  257. );
  258. $subscriber->expects($this->once())
  259. ->method("getSubscribedEvents")
  260. ->will($this->returnValue(array(SoftDeleteableListener::PRE_SOFT_DELETE, SoftDeleteableListener::POST_SOFT_DELETE)));
  261. $subscriber->expects($this->exactly(2))
  262. ->method("preSoftDelete")
  263. ->with($this->anything());
  264. $subscriber->expects($this->exactly(2))
  265. ->method("postSoftDelete")
  266. ->with($this->anything());
  267. $this->em->getEventManager()->addEventSubscriber($subscriber);
  268. $repo = $this->em->getRepository(self::ARTICLE_CLASS);
  269. $commentRepo = $this->em->getRepository(self::COMMENT_CLASS);
  270. $comment = new Comment();
  271. $commentField = 'comment';
  272. $commentValue = 'Comment 1';
  273. $comment->setComment($commentValue);
  274. $art0 = new Article();
  275. $field = 'title';
  276. $value = 'Title 1';
  277. $art0->setTitle($value);
  278. $art0->addComment($comment);
  279. $this->em->persist($art0);
  280. $this->em->flush();
  281. $art = $repo->findOneBy(array($field => $value));
  282. $this->assertNull($art->getDeletedAt());
  283. $this->assertNull($comment->getDeletedAt());
  284. $this->em->remove($art);
  285. $this->em->flush();
  286. }
  287. protected function getUsedEntityFixtures()
  288. {
  289. return array(
  290. self::ARTICLE_CLASS,
  291. self::PAGE_CLASS,
  292. self::MEGA_PAGE_CLASS,
  293. self::MODULE_CLASS,
  294. self::COMMENT_CLASS,
  295. self::USER_CLASS,
  296. self::OTHER_ARTICLE_CLASS,
  297. self::OTHER_COMMENT_CLASS,
  298. self::MAPPED_SUPERCLASS_CHILD_CLASS,
  299. );
  300. }
  301. }