Add custom doctrine subscriber for SQLite

Since SQLite doesn’t handle cascade remove by default, we need to handle it manually.

Also some refacto
This commit is contained in:
Jeremy Benoist 2016-10-01 14:01:13 +02:00
parent 98efffc2a6
commit 191564b7f7
No known key found for this signature in database
GPG key ID: BCA73962457ACC3C
5 changed files with 144 additions and 11 deletions

View file

@ -106,4 +106,17 @@ class AnnotationRepository extends EntityRepository
->getQuery()
->getSingleResult();
}
/**
* Remove all annotations for a user id.
* Used when a user want to reset all informations
*
* @param int $userId
*/
public function removeAllByUserId($userId)
{
$this->getEntityManager()
->createQuery('DELETE FROM Wallabag\AnnotationBundle\Entity\Annotation a WHERE a.user = '.$userId)
->execute();
}
}

View file

@ -237,25 +237,26 @@ class ConfigController extends Controller
switch ($type) {
case 'annotations':
$em->createQuery('DELETE FROM Wallabag\AnnotationBundle\Entity\Annotation a WHERE a.user = '.$this->getUser()->getId())
->execute();
$this->getDoctrine()
->getRepository('WallabagAnnotationBundle:Annotation')
->removeAllByUserId($this->getUser()->getId());
break;
case 'tags':
$tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($this->getUser()->getId());
$this->removeAllTagsByUserId($this->getUser()->getId());
break;
if (empty($tags)) {
break;
case 'entries':
// SQLite doesn't care about cascading remove, so we need to manually remove associated stuf
// otherwise they won't be removed ...
if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) {
$this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId());
$this->removeAllTagsByUserId($this->getUser()->getId());
}
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTags($this->getUser()->getId(), $tags);
break;
case 'entries':
$em->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = '.$this->getUser()->getId())
->execute();
->removeAllByUserId($this->getUser()->getId());
}
$this->get('session')->getFlashBag()->add(
@ -266,6 +267,24 @@ class ConfigController extends Controller
return $this->redirect($this->generateUrl('config').'#set3');
}
/**
* Remove all tags for a given user.
*
* @param int $userId
*/
private function removeAllTagsByUserId($userId)
{
$tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId);
if (empty($tags)) {
return;
}
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTags($userId, $tags);
}
/**
* Validate that a rule can be edited/deleted by the current user.
*

View file

@ -329,4 +329,17 @@ class EntryRepository extends EntityRepository
return $qb->getQuery()->getSingleScalarResult();
}
/**
* Remove all entries for a user id.
* Used when a user want to reset all informations
*
* @param int $userId
*/
public function removeAllByUserId($userId)
{
$this->getEntityManager()
->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = '.$userId)
->execute();
}
}

View file

@ -88,6 +88,17 @@ services:
arguments:
- WallabagCoreBundle:Tag
wallabag_core.listener.registration_confirmed:
class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener
arguments:
- "@doctrine.orm.entity_manager"
- "%wallabag_core.theme%"
- "%wallabag_core.items_on_page%"
- "%wallabag_core.rss_limit%"
- "%wallabag_core.language%"
tags:
- { name: kernel.event_subscriber }
wallabag_core.helper.entries_export:
class: Wallabag\CoreBundle\Helper\EntriesExport
arguments:
@ -129,3 +140,10 @@ services:
arguments:
- '@twig'
- '%kernel.debug%'
wallabag_core.subscriber.sqlite_cascade_delete:
class: Wallabag\CoreBundle\Subscriber\SQLiteCascadeDeleteSubscriber
arguments:
- "@doctrine"
tags:
- { name: doctrine.event_subscriber }

View file

@ -0,0 +1,70 @@
<?php
namespace Wallabag\CoreBundle\Subscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Wallabag\CoreBundle\Entity\Entry;
use Doctrine\Bundle\DoctrineBundle\Registry;
/**
* SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry.
* Foreign Key Support can be enabled by running `PRAGMA foreign_keys = ON;` at runtime (AT RUNTIME !).
* But it needs a compilation flag that not all SQLite instance has ...
*
* @see https://www.sqlite.org/foreignkeys.html#fk_enable
*/
class SQLiteCascadeDeleteSubscriber implements EventSubscriber
{
private $doctrine;
/**
* @param \Doctrine\Bundle\DoctrineBundle\Registry $doctrine
*/
public function __construct(Registry $doctrine)
{
$this->doctrine = $doctrine;
}
/**
* @return array
*/
public function getSubscribedEvents()
{
return [
'preRemove',
];
}
/**
* We removed everything related to the upcoming removed entry because SQLite can't handle it on it own.
* We do it in the preRemove, because we can't retrieve tags in the postRemove (because the entry id is gone)
*
* @param LifecycleEventArgs $args
*/
public function preRemove(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver ||
!$entity instanceof Entry) {
return;
}
$em = $this->doctrine->getManager();
if (null !== $entity->getTags()) {
foreach ($entity->getTags() as $tag) {
$entity->removeTag($tag);
}
}
if (null !== $entity->getAnnotations()) {
foreach ($entity->getAnnotations() as $annotation) {
$em->remove($annotation);
}
}
$em->flush();
}
}