Symfony 级联持久化或合并复制

Symfony cascade persist or merge duplication

我的级联持久化或合并有问题,我的应用程序有两个实体任务和 table (task_implements)

当我创建任务时,我选择链接的文化:

TaskController new()

if ( $form->isSubmitted() && $form->isValid() ) {
        // Cultures
        foreach ( $form->get('cultures')->getData() as $culture) {
            // Set correct status
            if ( $task->getUser() === null ) {
                $task->setStatus( 2 );
            } else {
                $task->setStatus( 3 );
            }
            $task->setCulture( $culture );

            // Implements

            foreach ( $form->get('implements')->getData() as $implement) {
                $taskImplement = new TaskImplement();
                $taskImplement->setImplement( $implement );

                $task->addImplement( $taskImplement );
            }

            $this->em->merge( $task );
            $this->em->flush();
        }


        $this->addFlash('success', 'Nouvelle tache ajoutée avec succès');
        return $this->redirectToRoute('admin_task_index');
    }

任务新类型

->add('implements', EntityType::class, [
            'class' => Implement::class,
            'query_builder' => function( ImplementRepository $ir ) {
                return $ir->createQueryBuilder('i');
            },
            'choice_label' => function( Implement $implement ) {
                return $implement->getName();
            },
            'mapped' => false,
            'required' => false,
            'expanded' => true,
            'multiple' => true
        ])

任务实体

/**
 * @ORM\OneToMany(targetEntity=TaskImplement::class, mappedBy="task", orphanRemoval=true, cascade={"persist", "merge"})
 */
private $implements;

public function addImplement(TaskImplement $taskImplement): self
{
    if (!$this->implements->contains($taskImplement)) {
        $this->implements[] = $taskImplement;
        $taskImplement->setTask($this);
    }

    return $this;
}

期望的行为

如果我举个例子,如果我选择 3 种文化应用程序创建 3 个任务并在每个任务中创建 2 个实现绑定

id / culture_id
#1 10
#2 11
#3 12

在task_implement

 id / task_id / implement_id
 #1 1 20
 #2 1 21
 #3 2 20
 #4 2 21
 #5 3 20
 #6 3 21

我得到的

     id / culture_id
     #1 10
     #2 11
     #3 12

于 task_implement

     id / task_id / implement_id

     #1 | 1 | 20 @
     #2 | 1 | 21 

     #3 | 2 | 20 @
     #4 | 2 | 21 @
     #5 | 2 | 20 @
     #6 | 2 | 21 

     #7 | 3 | 20 @
     #8 | 3 | 21 @
     #9 | 3 | 20 @
     #10 | 3 | 21 @
     #11 | 3 | 20 @
     #12 | 3 | 21

我不明白为什么它不能在数据库中的每条记录之间清除,如果我放入 4 个区域性我在最后一个 TaskImplement 上有 4 个条目,它会重复

感谢您的帮助

显然,merge 会在您每次调用它时创建一个新的任务条目(这可能意味着,您的 $task 没有 ID。

在外部 foreach 的第一个循环中,您添加了 2 个同样没有 ID 的 TaskImplements。您的任务现在有 2 个 TaskImplements(这是您此时想要的)。

在外部 foreach 的第二个循环中,您添加 另一个 2 个 TaskImplements - 现在您的任务有 4 个 TaskImplements。

解决方案选项:

  • 您需要在第一个外部 foreach 之后停止添加 TaskImplements(这会很奇怪,但会起作用)或者
  • 设置你的任务实现而不是添加它们或
  • 在添加新任务之前清除任务工具的任务对象或
  • 您在添加任务工具之前“克隆”了任务(创建一个新任务可能就足够了吗?)。

事后思考

您对 merge 的使用非常有趣。使用 persist,您应该只有 one Task,其中包含 6 个 TaskImplement 并且只有最后一个文化集。老实说,在没有任务 ID 的情况下进行合并感觉像是一种反模式。

我假设您的表单本身有 data_type 任务,这基本上是错误的,因为您是 editing/creating 多项任务。您处理表单时就好像您有多项任务一样,但实际上您只有一项任务。你的问题的根源在于错误的语义......

没关系,我在提交时成功地完成了我的控制器代码

if ( $form->isSubmitted() && $form->isValid() ) {
        // Cultures
        foreach ( $form->get('cultures')->getData() as $culture) {
            // Set correct status
            if ( $task->getUser() === null ) {
                $task->setStatus( 2 );
            } else {
                $task->setStatus( 3 );
            }
            $task->setCulture( $culture );

            $this->em->persist( $task );

            // Implements

            foreach ( $form->get('implements')->getData() as $implement) {
                $taskImplement = new TaskImplement();
                $taskImplement->setImplement( $implement );
                $taskImplement->setTask( $task );

                $this->em->persist( $taskImplement );
            }

            $this->em->flush();

            $this->em->clear( Task::class );
            $this->em->clear( TaskImplement::class );
        }



        $this->addFlash('success', 'Nouvelle tache ajoutée avec succès');
        return $this->redirectToRoute('admin_task_index');
    }

我只是清除每个实体上的两个实体

并且正在工作