我有几个类使用Taggable特性来设置几个学说实体共有的标签系统(Project,Note,...)。
这些实体与这些标签之间的关系是一种ManyToMany关系,我不能做多方向的。
我的问题:当我删除项目实体时,它会从项目表中删除,但此项目与标记之间的 project_tag 表中的关系不是删除。然后,如果我创建一个新的Project实体,则抛出异常。
An exception exists while executing 'INSERT INTO project_tag (project_id, tag_id) VALUES (?,?)' With params [2, 4]:
SQLSTATE [23000]: Integrity constraint violation: 19 UNIQUE constraint failed: project_tag.project_id, project_tag.tag_id
实体:
标签
/**
* Tag
*
* @ORM\Table(name="tag")
* @ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
*/
class Tag
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* @ORM\Column(name="last_use_at", type="datetime", nullable=false)
* @var \DateTime
*/
private $lastUseAt;
public function __construct()
{
$this->lastUseAt = new \DateTime();
}
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Tag
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return \DateTime
*/
public function getLastUseAt(): \DateTime
{
return $this->lastUseAt;
}
/**
* @param \DateTime $lastUseAt
*/
public function setLastUseAt(\DateTime $lastUseAt)
{
$this->lastUseAt = $lastUseAt;
}
}
加标签
trait Taggable
{
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag", cascade={"persist"})
*/
protected $tags;
/**
* Add tag
*
* @param Tag $tag
*
* @return $this
*/
public function addTag(Tag $tag)
{
$tag->setLastUseAt(new \DateTime());
$this->tags[] = $tag;
return $this;
}
/**
* Remove tag
*
* @param Tag $tag
*/
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag);
}
/**
* Get tags
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
}
项目
/**
* Project
*
* @ORM\Table(name="project")
* @ORM\Entity(repositoryClass="AppBundle\Repository\ProjectRepository")
*/
class Project
{
use Taggable;
}
注意
class Note
{
use Taggable;
}
这是唯一的解决方案还是我的注释不完整/不正确? 我试过JoinColumns,JoinTable和onDelete =" cascade"但没有任何作用。
与此同时,我在豁免之前提出了这条指令。
$project->getTags()->clear();
控制器中操作的完整代码:
/**
* @Route("/project/{id}/delete", name="project_delete")
*/
public function deleteAction($id) {
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('AppBundle:Project')->find($id);
if(!$project) {
return $this->redirectToRoute('index');
}
$project->getTags()->clear();
$em->remove($project);
$em->flush();
return $this->redirectToRoute('index');
}
答案 0 :(得分:2)
我设法解决了这个问题。这是我的解决方案,适用于SQLite连接。
创建一个监听kernel.request事件的eventListener:
namespace AppBundle\EventListener;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class RequestListener
{
/**
* @var Registry
*/
private $doctrine;
public function __construct(Registry $doctrine)
{
$this->doctrine = $doctrine;
}
public function onKernelRequest(GetResponseEvent $event)
{
$this->doctrine->getConnection()->exec('PRAGMA foreign_keys = ON');
}
}
服务声明
app.event_listener.request_listener:
class: AppBundle\EventListener\RequestListener
arguments:
- '@doctrine'
tags:
- { name: kernel.event_listener, event: kernel.request }
答案 1 :(得分:2)
我认为我找到了一个更好的解决方案:您可以在Doctrine配置中设置PRAGMA。喜欢:
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_sqlite'
#server_version: '5.7'
#charset: utf8mb4
#default_table_options:
#charset: utf8mb4
#collate: utf8mb4_unicode_ci
url: '%env(resolve:DATABASE_URL)%'
options:
'PRAGMA foreign_keys': 'ON'
我刚刚在Symfony 4应用程序上进行了尝试,重新创建了数据库,并使用DB Browser for SQLite进行了测试,并且运行正常。
希望这会有所帮助
答案 2 :(得分:0)
我认为问题在于您将特征Taggable
设置为ManyToMany关系的拥有方,但是您正在删除反面并期望某些事情发生。 Doctrine只会检查关系的拥有方,以便持续进行任何更改。有关此问题,请参阅here。
您可以通过将Taggable
与每个关系的反面相对应,或者通过手动告诉学说删除拥有方来解决。
第一种解决方案可能不适合您,因为您不会(轻松)指定多个反面。 (你确定一个特质是正确的方法吗?)
第二种解决方案很简单。在您的deleteTag($ tag)函数的Project
等实体中,请在拥有方调用删除函数(例如deleteProject($project)
。如果不存在,则必须创建。
class Project
{
use Taggable;
public function deleteTag($tag)
{
$this->tags->removeElement($tag);
// persist on the owning side
$tag->deleteProject($this);
}
}
编辑:
看到完整代码后,看起来您正在正确删除。现在你需要告诉学说要贯彻这一点。有关详细信息,请参阅此post,但基本上您可以将此特征更改为:
trait Taggable
{
/**
* @var ArrayCollection
*
* @ORM\ManyToMany(
* targetEntity="AppBundle\Entity\Tag",
* cascade={"persist"},
* onDelete="CASCADE"
* )
*/
protected $tags;
// ...
}