[英]Doctrine doesn't save data in my OneToMany association
我將 Symfony 4 與 Sonata Admin 一起使用。 我有一個項目到多個新聞協會。 我在我的項目管理頁面中嘗試添加一些新聞並更新項目時發現了一個問題。 問題是新聞尚未添加到項目中。 然后我通過將此代碼添加到我的 ProjectsAdmin.php 文件中解決了這個問題:
public function prePersist($project)
{
$this->preUpdate($project);
}
public function preUpdate($project)
{
$project->setNews($project->getNews());
}
但仍然存在一些問題。 第二個是我無法從項目中刪除新聞,單擊更新按鈕后沒有任何反應。 當然,如果我在我的項目實體中使用“orphanRemoval=true”來獲取現場新聞,它會起作用,但它會刪除我只想從項目中刪除的新聞。 我該如何解決這個問題?
最后但並非最不重要的一點是,我有 PreUpdate 事件偵聽器來檢查:如果我更新項目實體而不是添加到這個項目中的所有新聞。 問題是當我為 Projects Entity 執行此操作時它不起作用,但當我為 News Entity 執行相同操作時它起作用。 我忘了提到它與我在管理面板中的問題非常相似,因為當我 go 到新聞管理員並嘗試將項目添加到新聞時它可以在沒有任何修復的情況下工作,當我嘗試從新聞管理員中的新聞中刪除項目時它也按預期工作。 所以在 inversedBy 方面一切正常,但在 mappedBy 方面我有問題。
這是我的事件監聽器:
public function PreUpdate(LifecycleEventArgs $args): void {
$entity = $args->getEntity();
$newsRepo = $args->getEntityManager()->getRepository(News::class);
if ($entity instanceof Projects) {
foreach ($newsRepo as $new){
$news = $args->getEntityManager()->getReference(News::class, $new->getId());
$entity->setNews($news);
}
}
}
我的項目實體:
/**
* @ORM\Entity(repositoryClass=ProjectsRepository::class)
* @ORM\HasLifecycleCallbacks()
*/
class Projects {
/**
* @ORM\OneToMany(targetEntity=News::class, mappedBy="project", orphanRemoval=true)
*/
private $news;
public function __construct() {
$this->news = new ArrayCollection();
}
/**
* @return Collection|News[]
*/
public function getNews(): Collection {
return $this->news;
}
/**
* @param mixed $news
* @return Projects
*/
public function setNews($news) {
if (count($news) > 0) {
foreach ($news as $i) {
$this->addNews($i);
}
}
return $this;
}
/**
* @param News $news
*/
public function addNews(News $news) {
$news->setProject($this);
$this->news->add($news);
}
/**
* @param News $news
*/
public function removeNews(News $news) {
$this->news->removeElement($news);
}
}
新聞實體:
/**
* @ORM\Entity(repositoryClass="App\Repository\NewsRepository")
* @ORM\HasLifecycleCallbacks()
*/
class News {
/**
* @ORM\ManyToOne(targetEntity=Projects::class, inversedBy="news")
* @ORM\JoinColumn(nullable=true)
*/
private $project;
public function getProject(): ?Projects {
return $this->project;
}
public function setProject(?Projects $project): self {
$this->project = $project;
return $this;
}
}
項目存儲庫:
/**
* @method Projects|null find($id, $lockMode = null, $lockVersion = null)
* @method Projects|null findOneBy(array $criteria, array $orderBy = null)
* @method Projects[] findAll()
* @method Projects[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ProjectsRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Projects::class);
}
}
新聞資料庫:
/**
* @method News|null find($id, $lockMode = null, $lockVersion = null)
* @method News|null findOneBy(array $criteria, array $orderBy = null)
* @method News[] findAll()
* @method News[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class NewsRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, News::class);
}
}
首先,您的ProjectsAdmin:prePersist
和ProjectsAdmin:preUpdate
方法似乎沒有完成任何事情。 其次,您的事件偵聽器看起來也不正確。 我不相信這些代碼中的任何一個都是必要的,因為您應該能夠在實體級別解決這個問題。
在NewsAdmin
中一切都按預期工作的原因是News
是關系的擁有方,因此當您從Projects
端編輯關系時,您還必須在News
端調用相應的方法。 此外,您說您能夠成功地將News
項目添加到Projects
中但無法刪除它們,所以讓我們將您的Projects:addNews
方法與您的Projects:removeNews
方法進行比較。 您將看到有效的Projects:addNews
方法會影響擁有News
端的更改,而非工作的Projects:removeNews
方法僅作用於Projects
端。 您只需修改Projects:removeNews
以將News:project
屬性設置為null
。
/**
* @param News $news
*/
public function addNews(News $news) {
// next line makes this work
$news->setProject($this);
$this->news->add($news);
}
/**
* @param News $news
*/
public function removeNews(News $news) {
// make this work the same way with next line
$news->setProject(null);
$this->news->removeElement($news);
}
好吧,經過數小時的研究,我終於找到了解決我的一個問題的方法。 但首先我應該解釋為什么我在我的事件監聽器中使用那段代碼:
public function PreUpdate(LifecycleEventArgs $args): void {
$entity = $args->getEntity();
$newsRepo = $args->getEntityManager()->getRepository(News::class);
if ($entity instanceof Projects) {
foreach ($newsRepo as $new){
$news = $args->getEntityManager()->getReference(News::class, $new->getId());
$entity->setNews($news);
}
}
}
假設您有一個 API,其中包含許多實體和充滿內容的數據庫。 有一天,您的項目經理說我們需要將這個 API 用於兩個不同的客戶,他們的方法完全相同,但設計和內容不同。 因此,我創建了一個名為 Project 的新實體,我希望數據庫中的所有現有內容都綁定到我已經在 Sonata 管理員中創建的 ID 為 1 的新項目。 我有近 5000 個用戶,我想使用此 PreUpdate 更新我的項目實體,並且單擊“更新”按鈕后的所有 5000 個用戶應綁定到項目 ID 1。(在代碼中,我使用新聞而不是用戶,但目的相同。)但它沒有奏效。 實際上,當我更新新聞時以相反的方式使用它時,項目成功綁定。 我真的很失望,因為就我而言,我的實體項目中擁有成功設置新聞的所有方法。 再次項目實體:
/**
* @ORM\Entity(repositoryClass=ProjectsRepository::class)
* @ORM\HasLifecycleCallbacks()
*/
class Projects {
/**
* @ORM\OneToMany(targetEntity=News::class, mappedBy="project", orphanRemoval=true)
*/
private $news;
public function __construct() {
$this->news = new ArrayCollection();
}
/**
* @return Collection|News[]
*/
public function getNews(): Collection {
return $this->news;
}
/**
* @param mixed $news
* @return Projects
*/
public function setNews($news) {
if (count($news) > 0) {
foreach ($news as $i) {
$this->addNews($i);
}
}
return $this;
}
/**
* @param News $news
*/
public function addNews(News $news) {
$news->setProject($this);
$this->news->add($news);
}
/**
* @param News $news
*/
public function removeNews(News $news) {
$this->news->removeElement($news);
}
}
如果我不正確,請糾正我,我應該在那里添加什么?
對於解決方案,我真的發現我認為不是最好的解決方案,但它確實有效,老實說我很高興。 我創建了項目 controller:
/**
* @Route("/api",name="api_")
*/
class ProjectsController extends AbstractFOSRestController {
/**
* @Rest\Get("/v2/projects/update_entity", name="news_update_entity")
* @param \App\Entity\News $news
* @return \Symfony\Component\HttpFoundation\Response
*/
public function updateEntity(NewsRepository $newsRepository, ProjectsRepository $projectsRepository, EntityManagerInterface $em): void {
$news = $newsRepository->findAll();
$project = $projectsRepository->findOneBy(['id' => 44]);
foreach ($news as $new) {
if ($new->getProject()){
continue;
}
$newsUpdate = $em->getReference(News::class, $new->getId());
$projectPersist = $project->setNews($newsUpdate);
$em->persist($projectPersist);
$em->flush();
}
}
}
它按預期工作。 此代碼成功地將我的新聞綁定到該項目。 我真的不明白為什么在我的 controller 中它可以工作,但事件偵聽器中的 PreUpdate 卻沒有。 任何解釋將不勝感激。 如果您告訴我這項任務的最佳解決方案,我將不勝感激。 也許遷移? 而且我仍然想知道我應該如何從項目中刪除並使用管理面板添加到項目中,因為它不起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.