Search code examples
phppostgresqlsymfonydoctrine-ormfosrestbundle

OneToMany relationship NULL as the foreign key?


Preamble


I'm trying to POST (insert in a Postgresql database) a JSON formatted entity converted into a PHP object thanks to a FOSRestBundle route with JMSSerializerBundle. This entity looks like this:

**Vote** : OneToOne Bidirectional : **Question** : OneToMany Bidirectional : Answer

The JSON payload here :

{
  "title": "string",
  "description": "string",
  "question": {
    "id": 0,
    "title": "string",
    "description": "string",
    "answers": [
      {
        "title": "string",
        "description": "string"
      },
      {
        "title": "First answer ?",
        "description": "string"
      }
    ]
  }
}

Problem


When it inserts the vote, the vote_id in the question field is null as well as the question_id in answers.

When I get the payload from my route it get transformed into an object with the fos_rest.request_body here is the action :

    public function postVoteAction(Vote $vote, ConstraintViolationList $violations)
    {
        if (count($violations)) {
            return $this->view($violations, Response::HTTP_BAD_REQUEST);
        }
        $em = $this->getDoctrine()->getManager();
        $vote->setOwner($this->getUser());
        $em->persist($vote);
        $em->flush();
        return $vote;
    }

I do get a Vote object with my question and answers but when it get inserted in the database as said earlier foreign key fields are NULL.

What I have already done


I looked into relations & looked if persist was there in entities cascade={"persist"}

// in vote
@ORM\OneToOne(targetEntity="Question", mappedBy="vote", cascade={"persist", "remove"})
private $question;

// in question
@ORM\OneToOne(targetEntity="Vote", inversedBy="question", cascade={"persist"})
@ORM\JoinColumn(name="vote_id", referencedColumnName="id")
private $vote;

@ORM\OneToMany(targetEntity="Answer", mappedBy="question", cascade={"persist", "remove"})
private $answers;

// in answer
@ORM\ManyToOne(targetEntity="Question", inversedBy="answers", cascade={"persist"})
@ORM\JoinColumn(name="question_id", referencedColumnName="id")
private $question;

I used php bin\console make:entity --regenerate to get all getters/setters. I cleared the database & regenerated it.

Answer


As said by @yTko I forgot to put the references back to my object in my controller I thought that it was made by Doctrine with the persist so here is now my working code :

public function postVoteAction(Vote $vote, ConstraintViolationList $violations)
{
    if (count($violations)) {
        return $this->view($violations, Response::HTTP_BAD_REQUEST);
    }

    $em = $this->getDoctrine()->getManager();

    $vote->setOwner($this->getUser());
    $question = $vote->getQuestion();
    $question->setVote($vote);
    foreach ($question->getAnswers() as $answer) {
        $answer->setQuestion($question);
    }
    $em->persist($vote);
    $em->flush();

    return $vote;
}

Solution

  • I think that you've just forgotten to set related instances of vote and questions.

    In your controller action you have the vote object that converted by jms with from your json example.

    So, you need to set them manually through calling certain setters, like this:

    $question = $vote->getQuestion();
    $question->setVote($vote);
    

    or modify your setters this way:

    public function setQuestion(Question $question)
    {
        $this->question = $question;
        $this->question->setVote($this);
    
        return $this;
    }
    

    I prefer the first way because setters is just for setting concrete values, not for modifying other objects.