将所有记录插入到 Symfony 中的联结表中

Posted

技术标签:

【中文标题】将所有记录插入到 Symfony 中的联结表中【英文标题】:Insert all records into junction table in Symfony 【发布时间】:2018-08-22 16:36:06 【问题描述】:

我有一个下一个表格:quiz、question 和 question_quiz。我有一个多对多的关系。 插入适用于表格测验和问题,但在表格 question_quiz 中只插入了一条记录。

我的桌子

“问题”,没关系。

+----+--------+
| id | title  |
+----+--------+
| 61 | Title1 |
| 62 | Title2 |
| 63 | Title3 |
+----+--------+

“测验”,没关系。

+----+-------+--------+
| id | name  | status |
+----+-------+--------+
| 27 | Name1 |      0 |
+----+-------+--------+

“问题测验”

+-------------+---------+
| question_id | quiz_id |
+-------------+---------+
|          61 |      27 |
+-------------+---------+

最后一张表必须插入61,62,63 quiestion_id,但只插入单条记录。

我的控制器的片段。

   $quiz = new Quiz();
   $question = new Question();
   $form = $this->get('form.factory')->createNamed('quiz', QuizType::class, $quiz);
   $quiz->getQuestions()->add($question);
   $question->addQuiz($quiz);
   $form->handleRequest($request);
   if ($form->isSubmitted() && $form->isValid()) 
      $em = $this->getDoctrine()->getManager();
      $em->persist($quiz);
      $em->persist($question);
      $em->flush();

问题实体的调用证据。

  $quiz->getQuestions()->add($question);
  $question->addQuiz($quiz);

我使用集合类型,我可以插入任意数量的问题。

更新 QuizType 表单。

class QuizType extends AbstractType

    public function buildForm(FormBuilderInterface $builder, array $options)
    
        $builder
            ->add('name', TextType::class, array('attr' => ['class' => 'form-control name-quiz'], 'label' => 'Name Quiz'))
            ->add('status', CheckboxType::class, array(
                'label'    => 'Status Quiz',
                'required' => false))
            ->add('save', SubmitType::class, ['label' => 'Add', 'attr' => ['class' => 'btn btn-primary']]);
        $builder
            ->add('questions', CollectionType::class, array(
                'entry_type' => QuestionType::class,
                'entry_options' => array('label' => false),
                'allow_add' => true,
            ));
    

    public function configureOptions(OptionsResolver $resolver)
    
        $resolver->setDefaults([
            'data_class' => Quiz::class
        ]);
    

如果我调试 addQuestion() 方法,我收到错误 INSERT INTO question (title) VALUES (?)' with params [null]。但是如果我调试方法 addQuiz 我得到 ​​p>

   Question #583 ▼
  -id: null
  -title: "123"
  -quiz: ArrayCollection #584 ▼
    -elements: array:1 [▼
      0 => Quiz #581 ▼
        -id: null
        -name: "123"
        -questions: ArrayCollection #580 ▶
        -status: false
      
    ]
  
  -answers: ArrayCollection #585 ▶

调试 $form->get('questions')->getData() 返回

ArrayCollection #733 ▼
  -elements: array:2 [▼
    0 => Question #736 ▶
    1 => Question #981 ▶
  ]

【问题讨论】:

请出示您的QuizType 好吗?从提交的数据到您的 Quiz 实体的映射看起来不像您预期​​的那样工作。 @xabbuhI 我更新了。 哦,您能否也显示您的 QuizQuestion 实体? @xabbuh,请看这里。 ***.com/questions/51954652/… 我想知道 $this->questions[] = $question; 是否被调用过,是否真的有效。 $this->questions 在您的 Quiz 实体中是一个 Doctrine 集合。所以我本来希望这条线是$this->questions->add($question); 【参考方案1】:

没有 QuizQuestion 实体 - 很难说。但是

我认为问题出在你的控制器上。

 $quiz = new Quiz();
 $question = new Question();
 //...
 $question->addQuiz($quiz);

所以你创建了一个新的测验,它应该有多个问题,对吧? 但是你只分配一次问题..

$question->addQuiz($quiz);

所以正确的方法是这样的(有点冗长,所以你可以明白这一点)

$quiz = new Quiz();
//Q 1
$question_1 = new Question('Title1'); 
$quiz->addQuestion( $question_1 ); 
//Q 2
$question_2 = new Question('Title2'); 
$quiz->addQuestion( $question_2 );
//Q 3
$question_2 = new Question('Title3'); 
$quiz->addQuestion( $question_3 );

$em->persist($question_1);
$em->persist($question_2);
$em->persist($question_2);
$em->persist($quiz);
$em->flush();

查看此示例 (https://gist.github.com/Ocramius/3121916),了解多对多关系,尤其是 addUSerGroup()addUser() 方法。

在控制器(不带表单)中尝试建议的方法,看看它是否有效,以及您是否在question_quiz 表中获得了多行

更新:

我认为您正在寻找的“魔法”是您的测验实体中的cascade="persist"。看看这篇文章(https://knpuniversity.com/screencast/collections/add-new-collection-cascade-persist)。虽然视频不是免费的,但文字很好。

1)在你的实体中设置cascade

2) 更新了 Quiz 类中的 addQuestion() 方法,如下所示

public function addQuestion(Question $question)

  // do not add same questions more than once. 
  if ($this->questions->contains($question)) 
    return;
  
  $this->questions[] = $question; // to add new question to that quiz
  $question->addQuiz($this); // this quiz now also belongs to a question

3) 让嵌入集合与 prototype 一起工作 => http://symfony.com/doc/current/form/form_collections.html。因此,您可以即时添加新问题

4)提交后,调用:

if ($form->isSubmitted() && $form->isValid()) 
   $quiz = $form->getData();
   $em->persist($quiz);
   $em->flush();

4.1)如果这不起作用,请尝试遍历提交的问题集合

if ($form->isSubmitted() && $form->isValid()) 
   $quiz = $form->getData();
   // since getQuestions() gives you an ArrayCollection, isEmpty is a built in method
   if(!$quiz->getQuestions()->isEmpty())
   
     // this is kinda bad, but you'll have to debug and play around
     foreach ($quiz->getQuestions() as $new_question)
     
       $em->persist($new_question);
     
   
   $em->persist($quiz);
   $em->flush();

【讨论】:

这对我不起作用,因为我想插入任意数量的问题。在您的示例中,我只能添加 3 个问题。我已经看到链接了。我尝试了连接表和连接列,但它对我不起作用。 这是不正确的。使用这种方法,您可以根据需要在测验中添加任意数量的问题。只是为了演示,我创建了三个问题并将每个问题添加到一个测验中。如果您将 cascade="persist" 添加到您的 Quiz manytomany 注释中,您将获得自动持久化,因此您不需要手动调用 $em->persist($quesion_X)【参考方案2】:

我解决了我的问题。我发布我的代码也许它会对某人有所帮助。

我的实体Question

class Question

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

/**
 * @ORM\Column(type="text")
 */
private $title;

/**
 * @ORM\ManyToMany(targetEntity="App\Entity\Quiz", inversedBy="questions")
 */
private $quiz;


public function __construct()

    $this->quiz = new ArrayCollection();


public function getId(): ?int

    return $this->id;


public function getTitle(): ?string

    return $this->title;


public function setTitle(string $title): self

    $this->title = $title;

    return $this;



/**
 * @return Collection|Quiz[]
 */
public function getQuiz(): Collection

    return $this->quiz;


public function addQuiz(Quiz $quiz): self

    if (!$this->quiz->contains($quiz)) 
        $this->quiz[] = $quiz;
    

    return $this;


public function removeQuiz(Quiz $quiz): self

    if ($this->quiz->contains($quiz)) 
        $this->quiz->removeElement($quiz);
    

    return $this;
    

我的实体Quiz

class Quiz

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;
/**
 * @ORM\Column(type="string", length=191)
 */
private $name;
/**
 * @ORM\ManyToMany(targetEntity="App\Entity\Question", mappedBy="quiz", cascade="persist")
 */
private $questions;

/**
 * @ORM\Column(type="boolean")
 */
private $status;

public function __construct()

    $this->questions = new ArrayCollection();


public function getId(): ?int

    return $this->id;


/**
 * @return string|null
 */
public function getName(): ?string

    return $this->name;


public function setName(string $name): self

    $this->name = $name;

    return $this;


/**
 * @return Collection|Question[]
 */
public function getQuestions(): Collection

    return $this->questions;


public function addQuestion(Question $question): self

    if (!$this->questions->contains($question)) 
        $this->questions[] = $question;
        $question->addQuiz($this);
    

    return $this;


public function removeQuestion(Question $question): self

    if ($this->questions->contains($question)) 
        $this->questions->removeElement($question);
        $question->removeQuiz($this);
    

    return $this;


public function getStatus(): ?bool

    return $this->status;


public function setStatus(bool $status): self

    $this->status = $status;

    return $this;
   

QuestionType 表格

 public function buildForm(FormBuilderInterface $builder, array $options)
    
        $builder
            ->add('title', TextareaType::class, array('attr' => array('class' => 'form-control'), 'label' => false));
    

    public function configureOptions(OptionsResolver $resolver)
    
        $resolver->setDefaults([
            'data_class' => Question::class
        ]);
    

QuizType 表格

   public function buildForm(FormBuilderInterface $builder, array $options)
    
        $builder
            ->add('name', TextType::class, array('attr' => ['class' => 'form-control name-quiz'], 'label' => 'Name quiz'))
            ->add('status', CheckboxType::class, array(
                'label'    => 'Status quiz',
                'required' => false))
            ->add('save', SubmitType::class, ['label' => 'Add', 'attr' => ['class' => 'btn btn-primary']]);
        $builder
            ->add('questions', CollectionType::class, array(
                'entry_type' => QuestionType::class,
                'entry_options' => array('label' => false),
                'allow_add' => true,
                'by_reference' => false,
            ));
    

    public function configureOptions(OptionsResolver $resolver)
    
        $resolver->setDefaults([
            'data_class' => Quiz::class
        ]);
    

最后是我的控制器

    $quiz = new Quiz();
    $form = $this->get('form.factory')->createNamed('quiz', QuizType::class, $quiz);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) 
        $em = $this->getDoctrine()->getManager();
        $em->persist($quiz);
        $em->flush();
        return $this->redirectToRoute('admin');

    

它的作品,是的 :) 谢谢大家。

【讨论】:

以上是关于将所有记录插入到 Symfony 中的联结表中的主要内容,如果未能解决你的问题,请参考以下文章

将记录插入到具有180列的表中

有啥方法可以自动将新行添加到 sql 中的联结表中?

将记录计数插入到不同的表中

PostgreSQL。在联结表中插入值

Symfony 2 - 从表中获取最后插入的行

mysql联表查询,使用phpStudy自带的