首页 > 解决方案 > Doctrine 不会在我的 OneToMany 关联中保存数据

问题描述

我使用 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);
    }
}

标签: symfonydoctrine-ormdoctrineassociationssonata-admin

解决方案


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

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 却没有。任何解释将不胜感激。如果您告诉我这项任务的最佳解决方案,我将不胜感激。也许迁移?而且我仍然想知道我应该如何从项目中删除并使用管理面板添加到项目中,因为它不起作用。


推荐阅读