Doctrine 不会在我的 OneToMany 关联中保存数据

Posted

技术标签:

【中文标题】Doctrine 不会在我的 OneToMany 关联中保存数据【英文标题】:Doctrine doesn't save data in my OneToMany association 【发布时间】:2021-06-16 02:06:03 【问题描述】:

我使用 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 执行相同操作时它起作用。我忘了提到它与我在管理面板中的问题非常相似,因为当我进入新闻管理并尝试将项目添加到新闻时它可以在没有任何修复的情况下工作,当我尝试从新闻管理中的新闻中删除项目时它也按预期工作。所以在 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);
    

【问题讨论】:

【参考方案1】:

首先,您的ProjectsAdmin:prePersistProjectsAdmin:preUpdate 方法似乎没有完成任何事情。其次,您的事件侦听器看起来也不正确。我不相信任何这些代码都是必要的,因为您应该能够在实体级别解决这个问题。

NewsAdmin 中一切正常的原因是News 是关系的拥有方,因此当您从Projects 方编辑关系时,您还必须调用@987654326 上的相应方法@ 边。另外,您说您可以成功地将News 项目添加到Projects 但无法删除它们,所以让我们将您的Projects:addNews 方法与您的Projects:removeNews 方法进行比较。您将看到 working Projects:addNews 方法影响 owning News 方面的更改,而 non-working 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);

【讨论】:

感谢您的回答。我已经在我的代码中实现了$news->setProject(null);,但问题仍然存在。如果我从 ProjectsAdmin 中删除功能 prePersist 和 preUpdate,则此行 $news->setProject($this); 也将不起作用。我也不明白我应该如何在新闻端调用相应的方法。我已经在新闻实体中有方法 setProject。【参考方案2】:

嗯,经过数小时的研究,我终于找到了解决我的一个问题的方法。但首先我应该解释为什么我在事件监听器中使用那段代码:

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);
    

如果我不正确,请纠正我,我应该在那里添加什么?

对于解决方案,我真的发现我认为不是最好的解决方案,但它确实有效,老实说我很高兴。 我创建了项目控制器:

/**
 * @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();
        
    

它按预期工作。此代码成功地将我的新闻绑定到该项目。我真的不明白为什么在我的控制器中它可以工作,但事件侦听器中的 PreUpdate 却没有。任何解释将不胜感激。如果您告诉我这项任务的最佳解决方案,我将不胜感激。也许迁移?而且我仍然想知道我应该如何从项目中删除并使用管理面板添加到项目中,因为它不起作用。

【讨论】:

以上是关于Doctrine 不会在我的 OneToMany 关联中保存数据的主要内容,如果未能解决你的问题,请参考以下文章

Doctrine 对一对多关系进行了许多查询

Symfony 3 - Doctrine 2 - 在 oneToMany 关系上的 orderBy 不起作用

Doctrine 中的 OneToMany 和 ManyToOne 关系 - 奇怪的映射错误

Symfony2/Doctrine:如何使用 OneToMany 将实体重新保存为级联新行

Symfony / Doctrine:OneToMany插入导致null id

Symfony 5,Doctrine queryBuilder OneToMany 关系