原则ORM / Symfony-可以从父对象更新子对象吗?

时间:2018-10-12 13:09:50

标签: symfony doctrine-orm orm

我有两个实体,测验和题为OneToMany的关系(1个测验可以有很多问题)。

我正在尝试通过RestApi中的put操作更新测验对象(id = 19) 向数组问题中添加2个问题对象ID。 这些ID一直到该孤儿孤儿为止,他们的quiz_id为null。

测验ID 19更新前:

{
    "id": 19
    "alias": "Test Quiz",
    "questions": [],
    "hasFifty": false,
    "hasTip": false,
    "hasNext": false
}

Json放置数据操作(更新测验对象19):

 {
    "alias": "quiz-bill",
    "questions": [42,43],
    "hasFifty": true,
    "hasTip": true,
    "hasNext": true
}

放置请求的响应显示了更新测验对象:

 {
        "id": 19,
        "alias": "quiz-bill",
        "questions": [
            {
                "id": 42,
                "content": "test test test",
                "helpText": "dummy dummy dummy"                 
            },
            {
                "id": 43,
                "content": "test test",
                "helpText": "dummy"

            }
        ],
        "hasFifty": true,
        "hasTip": true,
        "hasNext": true
    }

但是这个对象是假的,当我从数据库中选择这些问题时,它们仍然具有quiz_id null。 我希望从parent(Quiz)更新中更新这些子对象的parent字段(quiz_id),但这似乎不可行。

有人在教义和Symfony框架上做了类似的事情吗?或者可以帮我吗?

测验实体:

/**
 * Quiz.
 *
 * @ORM\Table(name="quiz")
 * @ORM\Entity(repositoryClass="ApiBundle\Repository\QuizRepository")
 * @JMS\ExclusionPolicy("all")
 */
class Quiz
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @JMS\Groups({"task", "quiz"})
     * @JMS\Expose
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="alias", type="string", length=255)
     * @JMS\Groups({"quiz"})
     * @JMS\Expose
     */
    private $alias;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="Question", mappedBy="quiz")
     * @JMS\Groups({"quiz"})
     * @JMS\Expose
     */
    private $questions;

    /**
     * @var bool
     *
     * @ORM\Column(name="hasFifty", type="boolean", nullable=true)
     * @JMS\Groups({"quiz"})
     * @JMS\Expose
     */
    private $hasFifty;

    /**
     * @var bool
     *
     * @ORM\Column(name="hasTip", type="boolean", nullable=true)
     * @JMS\Groups({"quiz"})
     * @JMS\Expose
     */
    private $hasTip;

    /**
     * @var bool
     *
     * @ORM\Column(name="hasNext", type="boolean", nullable=true)
     * @JMS\Groups({"quiz"})
     * @JMS\Expose
     */
    private $hasNext;
/**
     * Get id.
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set alias.
     *
     * @param string $alias
     *
     * @return Quiz
     */
    public function setAlias($alias)
    {
        $this->alias = $alias;

        return $this;
    }

    /**
     * Get alias.
     *
     * @return string
     */
    public function getAlias()
    {
        return $this->alias;
    }

    /**
     * Set hasFifty.
     *
     * @param bool $hasFifty
     *
     * @return Quiz
     */
    public function setHasFifty($hasFifty)
    {
        $this->hasFifty = $hasFifty;

        return $this;
    }

    /**
     * Get hasFifty.
     *
     * @return bool
     */
    public function getHasFifty()
    {
        return $this->hasFifty;
    }

    /**
     * Set hasTip.
     *
     * @param bool $hasTip
     *
     * @return Quiz
     */
    public function setHasTip($hasTip)
    {
        $this->hasTip = $hasTip;

        return $this;
    }

    /**
     * Get hasTip.
     *
     * @return bool
     */
    public function getHasTip()
    {
        return $this->hasTip;
    }

    /**
     * Add question.
     *
     * @param \ApiBundle\Entity\Question $question
     *
     * @return Quiz
     */
    public function addQuestion(\ApiBundle\Entity\Question $question)
    {
        $this->questions[] = $question;

        return $this;
    }

    /**
     * Remove question.
     *
     * @param \ApiBundle\Entity\Question $question
     */
    public function removeQuestion(\ApiBundle\Entity\Question $question)
    {
        $this->questions->removeElement($question);
    }

    /**
     * Get questions.
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getQuestions()
    {
        return $this->questions;
    }

    /**
     * Set hasNext.
     *
     * @param bool $hasNext
     *
     * @return Quiz
     */
    public function setHasNext($hasNext)
    {
        $this->hasNext = $hasNext;

        return $this;
    }

    /**
     * Get hasNext.
     *
     * @return bool
     */
    public function getHasNext()
    {
        return $this->hasNext;
    }
}

问题实体:

/**
 * Question.
 *
 * @ORM\Table(name="question")
 * @ORM\Entity(repositoryClass="ApiBundle\Repository\QuestionRepository")
 * @JMS\ExclusionPolicy("all")
 */
class Question
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @JMS\Groups({"quiz" ,"question"})
     * @JMS\Expose
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     * @JMS\Groups({"quiz" ,"question"})
     * @JMS\Expose
     */
    private $content;

    /**
     * @var string
     *
     * @ORM\Column(name="help", type="text", nullable=true)
     * @JMS\Groups({"quiz" ,"question"})
     * @JMS\Expose
     */
    private $helpText;

    /**
     * @var \ApiBundle\Entity\Quiz
     *
     * @ORM\ManyToOne(targetEntity="Quiz", inversedBy="questions")
     * @ORM\JoinColumn(name="quiz_id", referencedColumnName="id")
     */
    protected $quiz;

    /**
     * @var \DateTime
     *
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(name="createdAt", type="datetime")
     * @JMS\Groups({"quiz" ,"question"})
     * @JMS\Expose
     */
    private $createdAt;

    /**
     * @var \DateTime
     *
     * @Gedmo\Timestampable(on="update")
     * @ORM\Column(name="updatedAt", type="datetime", nullable=true)
     * @JMS\Groups({"quiz" ,"question"})
     * @JMS\Expose
     */
    private $updatedAt;

    public function __construct()
    {
        $this->answers = new ArrayCollection();
    }

    /**
     * Get id.
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set content.
     *
     * @param string $content
     *
     * @return Question
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content.
     *
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Set createdAt.
     *
     * @param \DateTime $createdAt
     *
     * @return Question
     */
    public function setCreatedAt($createdAt)
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    /**
     * Get createdAt.
     *
     * @return \DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * Set updatedAt.
     *
     * @param \DateTime $updatedAt
     *
     * @return Question
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * Get updatedAt.
     *
     * @return \DateTime
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }

    /**
     * Set helpText.
     *
     * @param string $helpText
     *
     * @return Question
     */
    public function setHelpText($helpText)
    {
        $this->helpText = $helpText;

        return $this;
    }

    /**
     * Get helpText.
     *
     * @return string
     */
    public function getHelpText()
    {
        return $this->helpText;
    }

    /**
     * Set quiz.
     *
     * @param \ApiBundle\Entity\Quiz $quiz
     *
     * @return Question
     */
    public function setQuiz(\ApiBundle\Entity\Quiz $quiz = null)
    {
        $this->quiz = $quiz;

        return $this;
    }

    /**
     * Get quiz.
     *
     * @return \ApiBundle\Entity\Quiz
     */
    public function getQuiz()
    {
        return $this->quiz;
    }


}

QuizController放置动作:

/**
     * Update an existing Quiz.
     *
     * @param Request $request
     * @param int     $id
     *
     * @return mixed
     *
     * @Operation(
     *     tags={"Quiz"},
     *     summary="Update an existing Quiz.",
     *     @SWG\Response(
     *         response="204",
     *         description="Returned when an existing Quiz has been successful updated"
     *     ),
     *     @SWG\Response(
     *         response="400",
     *         description="Return when errors"
     *     ),
     *     @SWG\Response(
     *         response="401",
     *         description="Returned when access is not authorized"
     *     ),
     *     @SWG\Response(
     *         response="404",
     *         description="Return when not found"
     *     )
     * )
     *
     *
     * @Rest\View(serializerGroups={"quiz"})
     */
    public function putAction(Request $request, $id)
    {
        $quiz = $this->getDoctrine()->getRepository('ApiBundle:Quiz')->find($id);
        if (null === $quiz || empty($quiz)) {
            return new View(null, Response::HTTP_NOT_FOUND);
        }

        $form = $this->createForm(QuizType::class, $quiz, [
             'method' => 'PUT',
             'csrf_protection' => false,
         ]);
        $form->submit($request->request->all(), false);
        if (!$form->isValid()) {
            return $form;
        }

        $em = $this->getDoctrine()->getManager();
        $em->persist($quiz);
        $em->flush();

        return $quiz;
    }

QuizType表单:

<?php

namespace ApiBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class QuizType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
        ->add('alias')
        ->add('hasFifty')
        ->add('hasTip')
        ->add('hasNext')
        ->add('videoUrl')
        ->add('questions')
        ->add('task');
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'ApiBundle\Entity\Quiz',
            'csrf_protection' => false,
            'allow_extra_fields' => true,
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'apibundle_quiz';
    }
}

2 个答案:

答案 0 :(得分:0)

在控制器操作中,您仅保留Quiz实体,但也需要保留相关的Question实体。在多对一关系中,连接保存在只能有一个相关实体的实体表中。

foreach($quiz->getQuestions() as $question) {
    // I don't know if you need this line
    $question->setQuiz($quiz);
    $em->persist($question);
}
$em->persist($quiz);

答案 1 :(得分:0)

在将已经存在的问题添加到测验中时,您无需级联持久化,但仍需要将测验设置为添加的问题:

// inside Quiz entity
public function addQuestion(\ApiBundle\Entity\Question $question)
{
    $question->setQuiz($this);
    $this->questions[] = $question;

    return $this;
}

这是因为(引自Doctrine ORM Documentation

  

Doctrine只会检查关联的拥有方   变化。

     

仅对关联的反面所做的更改将被忽略。   确保更新双向关联的两端(或在   从教义的角度来看,至少是拥有方)

在您的情况下,关联的相反方面是Quiz实体,而拥有方面是Question实体。