简体   繁体   English

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

[英]Doctrine doesn't save data in my OneToMany association

I use Symfony 4 with Sonata Admin.我将 Symfony 4 与 Sonata Admin 一起使用。 I have One Project To Many News association.我有一个项目到多个新闻协会。 And I found a problem trying in my Project admin page add some news and update the Project.我在我的项目管理页面中尝试添加一些新闻并更新项目时发现了一个问题。 The problem is News have not been added to the Project.问题是新闻尚未添加到项目中。 And then I solved this problem by adding this code into my ProjectsAdmin.php file:然后我通过将此代码添加到我的 ProjectsAdmin.php 文件中解决了这个问题:

 public function prePersist($project)
    {
        $this->preUpdate($project);
    }

    public function preUpdate($project)
    {
        $project->setNews($project->getNews());
    }

But there is still some problems.但仍然存在一些问题。 The second one is I can't delete News from Project, after clicking update button nothing happens.第二个是我无法从项目中删除新闻,单击更新按钮后没有任何反应。 Sure if I use 'orphanRemoval=true' in my Projects Entity for field news it would work but it would delete News that I only want to remove from Project.当然,如果我在我的项目实体中使用“orphanRemoval=true”来获取现场新闻,它会起作用,但它会删除我只想从项目中删除的新闻。 How can I fix this problem?我该如何解决这个问题?

And the last but not least I have PreUpdate event listener that checks: if I update Project entity than add to this Project all News.最后但并非最不重要的一点是,我有 PreUpdate 事件侦听器来检查:如果我更新项目实体而不是添加到这个项目中的所有新闻。 The thing is it doesn't work when I do it for Projects Entity but when I do the same for News Entity it works.问题是当我为 Projects Entity 执行此操作时它不起作用,但当我为 News Entity 执行相同操作时它起作用。 I forgot to mention that it is so similar with my problems in admin panel, because when I go to News Admin and there try to add Project to News it works without any fixes and when I try to delete Project from News in News Admin it also works as expected.我忘了提到它与我在管理面板中的问题非常相似,因为当我 go 到新闻管理员并尝试将项目添加到新闻时它可以在没有任何修复的情况下工作,当我尝试从新闻管理员中的新闻中删除项目时它也按预期工作。 So on inversedBy side everything works but on mappedBy side I have problems.所以在 inversedBy 方面一切正常,但在 mappedBy 方面我有问题。

Here is my Event Listener:这是我的事件监听器:

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

My Projects Entity:我的项目实体:

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

News Entity:新闻实体:

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

Projects Repository:项目存储库:

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

News Repository:新闻资料库:

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

First of all your ProjectsAdmin:prePersist and ProjectsAdmin:preUpdate methods do not appear to accomplish anything.首先,您的ProjectsAdmin:prePersistProjectsAdmin:preUpdate方法似乎没有完成任何事情。 Second, your event listener does not look right either.其次,您的事件侦听器看起来也不正确。 I do not believe any of those bits of code are necessary as you should be able to solve this at the entity level.我不相信这些代码中的任何一个都是必要的,因为您应该能够在实体级别解决这个问题。

The reason everything works as expected in the NewsAdmin is that News is the owning side of the relation, so when you edit the relation from the Projects side you must also call the corresponding methods on the News side.NewsAdmin中一切都按预期工作的原因是News是关系的拥有方,因此当您从Projects端编辑关系时,您还必须在News端调用相应的方法。 Also, you say you are able to successfully add News items to Projects but cannot remove them so, let's compare your Projects:addNews method with your Projects:removeNews method.此外,您说您能够成功地将News项目添加到Projects中但无法删除它们,所以让我们将您的Projects:addNews方法与您的Projects:removeNews方法进行比较。 You will see the working Projects:addNews method affects change on the owning News side, while the non-working Projects:removeNews method only acts on the Projects side.您将看到有效Projects:addNews方法会影响拥有News端的更改,而非工作的Projects:removeNews方法仅作用于Projects端。 You should only need to modify Projects:removeNews to set the News:project property to null .您只需修改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);
}

Well, after hours of researching I finally found a solution for one of my problems.好吧,经过数小时的研究,我终于找到了解决我的一个问题的方法。 But firstly I should explain why exactly I use that piece of code in my event listener:但首先我应该解释为什么我在我的事件监听器中使用那段代码:

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

Let's imagine you have an API with many entities and database full of content.假设您有一个 API,其中包含许多实体和充满内容的数据库。 And one day your project manager say we need to use this API for two different clients that have exactly the same methods but different design and different content.有一天,您的项目经理说我们需要将这个 API 用于两个不同的客户,他们的方法完全相同,但设计和内容不同。 So I created a new Entity that I called Project and I want all the existing content in the database bind to the new Project with id 1 that I've already created in my Sonata admin.因此,我创建了一个名为 Project 的新实体,我希望数据库中的所有现有内容都绑定到我已经在 Sonata 管理员中创建的 ID 为 1 的新项目。 I have almost 5000 users and I want using this PreUpdate update my Projects entity and all the 5000 users after click on "Update" button should be bound to Project id 1. (In code I used news instead users but with the same purpose.) But it hasn't worked.我有近 5000 个用户,我想使用此 PreUpdate 更新我的项目实体,并且单击“更新”按钮后的所有 5000 个用户应绑定到项目 ID 1。(在代码中,我使用新闻而不是用户,但目的相同。)但它没有奏效。 Actually it has when I use it in reverse way when I updated News, Project successfully bound.实际上,当我更新新闻时以相反的方式使用它时,项目成功绑定。 And I really was disappointed, because as far as I'm concerned I have all the methods in my entity Project to successfully set News.我真的很失望,因为就我而言,我的实体项目中拥有成功设置新闻的所有方法。 Once again Projects entity:再次项目实体:

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

Correct me if I'm not right, what should I add there?如果我不正确,请纠正我,我应该在那里添加什么?

To the solution, I really found I think not the best one but it worked and to be honest I'm happy.对于解决方案,我真的发现我认为不是最好的解决方案,但它确实有效,老实说我很高兴。 I created Projects controller:我创建了项目 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();
        }
    }
}

And it's worked as expected.它按预期工作。 This code successfully bound my news to the project.此代码成功地将我的新闻绑定到该项目。 What I really can't understand why in my controller it worked but PreUpdate in Event Listener doesn't.我真的不明白为什么在我的 controller 中它可以工作,但事件侦听器中的 PreUpdate 却没有。 Any explanation would be highly appreciated.任何解释将不胜感激。 Also I would be thankful if you tell me the best solution for this task.如果您告诉我这项任务的最佳解决方案,我将不胜感激。 Maybe migrations?也许迁移? And I still want to know what should I do with remove from project and add to project using admin panel, because it's not working.而且我仍然想知道我应该如何从项目中删除并使用管理面板添加到项目中,因为它不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM