I have two entities, Quiz and Question with retlationship OneToMany (1 Quiz can have many questions).
I am trying to update the quiz object(id=19) through a put action in RestApi
adding to array questions 2 ids of question objects.
These ids are till that momment orphans, the quiz_id of them is null.
Quiz id 19 Before update:
{
"id": 19
"alias": "Test Quiz",
"questions": [],
"hasFifty": false,
"hasTip": false,
"hasNext": false
}
Json Data on Put action (Update Quiz object 19):
{
"alias": "quiz-bill",
"questions": [42,43],
"hasFifty": true,
"hasTip": true,
"hasNext": true
}
The response of put request shows me the update quiz object :
{
"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
}
But this object is fake , when i select these the questions from database, they still have quiz_id null.
I was hopping to update the parent field(quiz_id) of these child objects from parent(Quiz) update, but this seems not cappable.
Is there anyone who has done something like that with doctrine and Symfony framework? Or can help me in this ?
Quiz Entity:
/**
* 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 Entity:
/**
* 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 Put action:
/**
* 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 Form:
<?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';
}
}
In the controller action you only persist the Quiz entity but you need to persist the related Question entities too. In a many to one relation the connection is saved in the table of the entity that can only have one related entity.
foreach($quiz->getQuestions() as $question) {
// I don't know if you need this line
$question->setQuiz($quiz);
$em->persist($question);
}
$em->persist($quiz);
As you are adding already persisted questions to your quiz you do not need to cascade persist but you still need to set your quiz to the added question:
// inside Quiz entity
public function addQuestion(\ApiBundle\Entity\Question $question)
{
$question->setQuiz($this);
$this->questions[] = $question;
return $this;
}
This is because (quoting from Doctrine ORM Documentation)
Doctrine will only check the owning side of an association for
changes.
Changes made only to the inverse side of an association are ignored.
Make sure to update both sides of a bidirectional association (or at
least the owning side, from Doctrine's point of view)
In your case the inverse side of the association is Quiz entity and the owning side is the Question entity.
Related
First, I'm a french beginner in Symfony4,
Second, I already searched in Symfony Documentation, asked some friends, called my mom..
I'm working on EasyAdminBundle on the form Edit / New Entity.
I have to change the label of my entities but when I do it, my form type is changing.
Here's when picture of my view before editing:
I want to change ' id_equipe' to 'Domicile (home for english )' and id_equipe_equipes to 'exterieur (Visitors)'
So when I tried this :
fields:
- { property: 'id_equipe', label: equipe_domicile}
- { property: 'id_equipe_equipes', label: equipe_extérieur}
The type of the properties is changing to TextArea and I don't know why.
I tried to put a new type like this :
- { property: 'id_equipe', label: equipe_domicile, type: 'choice'}
but my select is blank, I cannot choose anything.
This is what I get:
Thanks you guys
Ps: sorry for ten years old english
So, look, if I understand correctly - id_equipe is a key to external entity. So, this way, to get it worked - you need to use type: 'entity' and also add type_options option, something like that:
- { property: 'id_equipe', label: 'Domicile', type: 'entity', type_options: { class: 'App\Entity\Equipe', multiple: true } }
UPDATE:
So, due to discussion found that the problem was in the property naming. Right to be property: 'idEquipe' not property: 'id_equipe'. So the property names must be same as in the entity, not as the name of the field in the database.
Here's my Matchs entity.
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Matchs
*
* #ORM\Table(name="matchs", indexes={#ORM\Index(name="MATCHS_EQUIPES1_FK", columns={"ID_EQUIPE_EQUIPES"}), #ORM\Index(name="MATCHS_EQUIPES_FK", columns={"ID_EQUIPE"}), #ORM\Index(name="MATCHS_JOURNEE0_FK", columns={"ID_JOURNEE"})})
* #ORM\Entity
*/
class Matchs
{
/**
* #var int
*
* #ORM\Column(name="ID_MATCHS", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idMatchs;
/**
* #var \DateTime
*
* #ORM\Column(name="DATE_MATCHS", type="datetime", nullable=false)
*/
private $dateMatchs;
/**
* #var string|null
*
* #ORM\Column(name="RESUME_MATCHS", type="text", length=65535, nullable=true)
*/
private $resumeMatchs;
/**
* #var string|null
*
* #ORM\Column(name="TITRE_MATCH", type="string", length=255, nullable=true)
*/
private $titreMatch;
/**
* #var int
*
* #ORM\Column(name="SCORE_EQUIPE1", type="integer", nullable=false)
*/
private $scoreEquipe1;
/**
* #var int
*
* #ORM\Column(name="SCORE_EQUIPE2", type="integer", nullable=false)
*/
private $scoreEquipe2;
/**
* #var \Equipes
*
* #ORM\ManyToOne(targetEntity="Equipes")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ID_EQUIPE_EQUIPES", referencedColumnName="ID_EQUIPE")
* })
*/
private $idEquipeEquipes;
/**
* #var \Equipes
*
* #ORM\ManyToOne(targetEntity="Equipes")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ID_EQUIPE", referencedColumnName="ID_EQUIPE")
* })
*/
private $idEquipe;
/**
* #var \Journee
*
* #ORM\ManyToOne(targetEntity="Journee")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ID_JOURNEE", referencedColumnName="ID_JOURNEE")
* })
*/
private $idJournee;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="JoueursEquipe", inversedBy="idMatchs")
* #ORM\JoinTable(name="jouer",
* joinColumns={
* #ORM\JoinColumn(name="ID_MATCHS", referencedColumnName="ID_MATCHS")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="ID_JOUEUR_EQUIPE", referencedColumnName="ID_JOUEUR_EQUIPE")
* }
* )
*/
private $idJoueurEquipe;
/**
* Constructor
*/
public function __construct()
{
$this->idJoueurEquipe = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getIdMatchs()
{
return $this->idMatchs;
}
public function setIdMatchs(int $idMatchs)
{
$this->idMatchs = $idMatchs;
return $this;
}
/**
* Get the value of dateMatchs
*
* #return \DateTime
*/
public function getDateMatchs()
{
return $this->dateMatchs;
}
/**
* Set the value of dateMatchs
*
* #param \DateTime $dateMatchs
*
* #return self
*/
public function setDateMatchs(\DateTime $dateMatchs)
{
$this->dateMatchs = $dateMatchs;
return $this;
}
/**
* Get the value of resumeMatchs
*
* #return string|null
*/
public function getResumeMatchs()
{
return $this->resumeMatchs;
}
/**
* Set the value of resumeMatchs
*
* #param string|null $resumeMatchs
*
* #return self
*/
public function setResumeMatchs($resumeMatchs)
{
$this->resumeMatchs = $resumeMatchs;
return $this;
}
/**
* Get the value of scoreEquipe1
*
* #return int
*/
public function getScoreEquipe1()
{
return $this->scoreEquipe1;
}
/**
* Set the value of scoreEquipe1
*
* #param int $scoreEquipe1
*
* #return self
*/
public function setScoreEquipe1(int $scoreEquipe1)
{
$this->scoreEquipe1 = $scoreEquipe1;
return $this;
}
/**
* Get the value of scoreEquipe2
*
* #return int
*/
public function getScoreEquipe2()
{
return $this->scoreEquipe2;
}
/**
* Set the value of scoreEquipe2
*
* #param int $scoreEquipe2
*
* #return self
*/
public function setScoreEquipe2(int $scoreEquipe2)
{
$this->scoreEquipe2 = $scoreEquipe2;
return $this;
}
/**
* Get the value of idEquipeEquipes
*
* #return \Equipes
*/
public function getIdEquipeEquipes()
{
return $this->idEquipeEquipes;
}
/**
* Set the value of idEquipeEquipes
*
* #param \Equipes $idEquipeEquipes
*
* #return self
*/
public function setIdEquipeEquipes(\Equipes $idEquipeEquipes)
{
$this->idEquipeEquipes = $idEquipeEquipes;
return $this;
}
/**
* Get the value of idEquipe
*
* #return \Equipes
*/
public function getIdEquipe()
{
return $this->idEquipe;
}
/**
* Set the value of idEquipe
*
* #param \Equipes $idEquipe
*
* #return self
*/
public function setIdEquipe(\Equipes $idEquipe)
{
$this->idEquipe = $idEquipe;
return $this;
}
public function __toString()
{
return $this->nomEquipe;
}
/**
* Get the value of idJournee
*
* #return \Journee
*/
public function getIdJournee()
{
return $this->idJournee;
}
/**
* Set the value of idJournee
*
* #param \Journee $idJournee
*
* #return self
*/
public function setIdJournee(\Journee $idJournee)
{
$this->idJournee = $idJournee;
return $this;
}
public function getTitreMatch(): ?string
{
return $this->titreMatch;
}
public function setTitreMatch(string $titreMatch): self
{
$this->titreMatch = $titreMatch;
return $this;
}
/**
* Get the value of idJoueurEquipe
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdJoueurEquipe()
{
return $this->idJoueurEquipe;
}
/**
* Set the value of idJoueurEquipe
*
* #param \Doctrine\Common\Collections\Collection $idJoueurEquipe
*
* #return self
*/
public function setIdJoueurEquipe(\Doctrine\Common\Collections\Collection $idJoueurEquipe)
{
$this->idJoueurEquipe = $idJoueurEquipe;
return $this;
}
public function addIdJoueurEquipe(JoueursEquipe $idJoueurEquipe): self
{
if (!$this->idJoueurEquipe->contains($idJoueurEquipe)) {
$this->idJoueurEquipe[] = $idJoueurEquipe;
}
return $this;
}
public function removeIdJoueurEquipe(JoueursEquipe $idJoueurEquipe): self
{
if ($this->idJoueurEquipe->contains($idJoueurEquipe)) {
$this->idJoueurEquipe->removeElement($idJoueurEquipe);
}
return $this;
}
}
I have a little problem i don't know why i can't edit my foreign key ($idProfile) but i can edit an other value from the database :
I can change the value enable for exemple but not the idProfile when i try to change it i have this error : "An instance of Symfony\Bundle\FrameworkBundle\Templating\EngineInterface must be injected in FOS\RestBundle\View\ViewHandler to render templates." don't know if that can help thx for help guys :p
my Profile controller :
/**
* Creates a new profile entity.
*
* #Route("/new/{id}", name="profile_new")
*/
public function newProfileAction(Request $request, User $user)
{
$loggedAs = $this->getUser();
$username = $loggedAs->getUsername();
$profile = new Profile();
$form = $this->createForm(ProfileType::class, $profile);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$profile->setLastConnexion(new \DateTime('now'));
$profile->setCreatedAccount(new \DateTime('now'));
$em = $this->getDoctrine()->getManager();
$em->persist($profile);
$em->flush();
$user->setEnabled('1');
$user->setIdLocation($profile->getId());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirectToRoute('user_list');
}
return $this->render('admin/user/new_profile.html.twig', array(
'profile' => $profile,
'form' => $form->createView(),
'username' => $username,
));
}
My userEntity :
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="user", uniqueConstraints=
{#ORM\UniqueConstraint(name="user_id_uindex", columns={"id"}),
#ORM\UniqueConstraint(name="user_username_uindex", columns=
{"username"})}, indexes={#ORM\Index(name="user_profile_id_fk",
columns={"id_profile"}), #ORM\Index(name="user_localisation_id_fk",
columns={"id_location"})})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* #UniqueEntity("username", groups={"Default", "Patch"})
*/
class User implements UserInterface
{
const ROLE_USER = 'ROLE_USER';
const ROLE_ADMIN = 'ROLE_ADMIN';
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Serializer\Groups({"Default", "Deserialize", "user_detail"})
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=32)
* #Serializer\Groups({"Default", "Deserialize", "user_detail"})
*/
protected $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
* #Serializer\Groups({"Deserialize", "user_detail"})
* #Assert\Regex(
* pattern="/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{7,}/",
* message="Password must be seven characters long and contain at least one digit code, upper case, and lower case letter!",
* groups={"Default", "Patch"}
* )
*/
protected $password;
/**
* #var boolean
*
* #ORM\Column(name="enabled", type="boolean", nullable=false)
*/
protected $enabled = '1';
/**
* #var \AppBundle\Entity\Localisation
*
* #ORM\ManyToOne(targetEntity="Localisation")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_location", referencedColumnName="id")
* })
*/
protected $idLocation;
/**
* #var \AppBundle\Entity\Profile
*
* #ORM\ManyToOne(targetEntity="Profile")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_profile", referencedColumnName="id")
* })
*/
protected $idProfile;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="idUserOne")
* #ORM\JoinTable(name="friend",
* joinColumns={
* #ORM\JoinColumn(name="id_user_one", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_user_two", referencedColumnName="id")
* }
* )
*/
protected $idUserTwo;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", inversedBy="idUserInvited")
* #ORM\JoinTable(name="list_invited",
* joinColumns={
* #ORM\JoinColumn(name="id_user_invited", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_place_invited", referencedColumnName="id")
* }
* )
*/
protected $idPlaceInvited;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", mappedBy="idUserPresent")
*/
protected $idPlacePresent;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", inversedBy="idUserPlace")
* #ORM\JoinTable(name="user_place",
* joinColumns={
* #ORM\JoinColumn(name="id_user_place", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_place_place", referencedColumnName="id")
* }
* )
*/
protected $idPlacePlace;
/**
* #var array
* #ORM\Column(type="simple_array", length=200)
* #Serializer\Exclude()
*/
protected $roles;
/**
* Constructor
*/
public function __construct()
{
$this->idUserTwo = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlaceInvited = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlacePresent = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlacePlace = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id)
{
$this->id = $id;
}
/**
* #return string|null
*/
public function getUsername()
{
return $this->username;
}
/**
* #param string $username
*/
public function setUsername(string $username)
{
$this->username = $username;
}
/**
* #return string|null
*/
public function getPassword()
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password)
{
$this->password = $password;
}
/**
* #return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* #param bool $enabled
*/
public function setEnabled(bool $enabled)
{
$this->enabled = $enabled;
}
/**
* #return mixed
*/
public function getIdLocation()
{
return $this->idLocation;
}
/**
* #param mixed $idLocation
*/
public function setIdLocation($idLocation)
{
$this->idLocation = $idLocation;
}
/**
* #return mixed
*/
public function getIdProfile()
{
return $this->idProfile;
}
/**
* #param mixed $idProfile
*/
public function setIdProfile($idProfile)
{
$this->idProfile = $idProfile;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdUserTwo()
{
return $this->idUserTwo;
}
/**
* #param \Doctrine\Common\Collections\Collection $idUserTwo
*/
public function setIdUserTwo(\Doctrine\Common\Collections\Collection $idUserTwo)
{
$this->idUserTwo = $idUserTwo;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlaceInvited()
{
return $this->idPlaceInvited;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlaceInvited
*/
public function setIdPlaceInvited(\Doctrine\Common\Collections\Collection $idPlaceInvited)
{
$this->idPlaceInvited = $idPlaceInvited;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlacePresent()
{
return $this->idPlacePresent;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlacePresent
*/
public function setIdPlacePresent(\Doctrine\Common\Collections\Collection $idPlacePresent)
{
$this->idPlacePresent = $idPlacePresent;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlacePlace()
{
return $this->idPlacePlace;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlacePlace
*/
public function setIdPlacePlace(\Doctrine\Common\Collections\Collection $idPlacePlace)
{
$this->idPlacePlace = $idPlacePlace;
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return (Role|string)[] The user roles
*/
public function getRoles()
{
return $this->roles;
}
/**
* #param array $roles
*/
public function setRoles(array $roles)
{
$this->roles = $roles;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
// TODO: Implement getSalt() method.
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
}
some screen of data base : [https://ibb.co/iCSnWy
p
[https://ibb.co/cZZedd
p
In this case you'll have to do it this way:
$loggedAs = $this->getUser(); // get the user
$profile = new Profile();
// set up profile variables
// [...]
$profile->setUser($loggedAs); // case 1
// or, depending on your implementation
$loggedAs->addProfile($profile); // case 2
// persist, flush etc
With doctrine the foreign key handling is behind the scenes,
you don't have to worry about them if your mappings are correct
So instead of fkeys, you just attach entities.
Depending on your implementation (the relation can be uni- or bi-directional) you only have to chose one of the 2 cases.
I hope it helps, more here and here.
I have a little problem i don't know why i can't edit my foreign key ($idProfile) but i can edit an other value from the database :
I can change the value enable for exemple but not the idProfile when i try to change it i have this error : "An instance of Symfony\Bundle\FrameworkBundle\Templating\EngineInterface must be injected in FOS\RestBundle\View\ViewHandler to render templates." don't know if that can help thx for help guys :p
In general : I just want to know how can i edit my $id_profile to link it with my profile entity
my Profile controller :
/**
* Creates a new profile entity.
*
* #Route("/new/{id}", name="profile_new")
*/
public function newProfileAction(Request $request, User $user)
{
$loggedAs = $this->getUser();
$username = $loggedAs->getUsername();
$profile = new Profile();
$form = $this->createForm(ProfileType::class, $profile);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$profile->setLastConnexion(new \DateTime('now'));
$profile->setCreatedAccount(new \DateTime('now'));
$em = $this->getDoctrine()->getManager();
$em->persist($profile);
$em->flush();
$user->setEnabled('1');
$user->setIdLocation($profile->getId());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirectToRoute('user_list');
}
return $this->render('admin/user/new_profile.html.twig', array(
'profile' => $profile,
'form' => $form->createView(),
'username' => $username,
));
}
My userEntity :
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="user", uniqueConstraints=
{#ORM\UniqueConstraint(name="user_id_uindex", columns={"id"}),
#ORM\UniqueConstraint(name="user_username_uindex", columns=
{"username"})}, indexes={#ORM\Index(name="user_profile_id_fk",
columns={"id_profile"}), #ORM\Index(name="user_localisation_id_fk",
columns={"id_location"})})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* #UniqueEntity("username", groups={"Default", "Patch"})
*/
class User implements UserInterface
{
const ROLE_USER = 'ROLE_USER';
const ROLE_ADMIN = 'ROLE_ADMIN';
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Serializer\Groups({"Default", "Deserialize", "user_detail"})
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=32)
* #Serializer\Groups({"Default", "Deserialize", "user_detail"})
*/
protected $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
* #Serializer\Groups({"Deserialize", "user_detail"})
* #Assert\Regex(
* pattern="/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{7,}/",
* message="Password must be seven characters long and contain at least one digit code, upper case, and lower case letter!",
* groups={"Default", "Patch"}
* )
*/
protected $password;
/**
* #var boolean
*
* #ORM\Column(name="enabled", type="boolean", nullable=false)
*/
protected $enabled = '1';
/**
* #var \AppBundle\Entity\Localisation
*
* #ORM\ManyToOne(targetEntity="Localisation")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_location", referencedColumnName="id")
* })
*/
protected $idLocation;
/**
* #var \AppBundle\Entity\Profile
*
* #ORM\ManyToOne(targetEntity="Profile")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_profile", referencedColumnName="id")
* })
*/
protected $idProfile;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="idUserOne")
* #ORM\JoinTable(name="friend",
* joinColumns={
* #ORM\JoinColumn(name="id_user_one", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_user_two", referencedColumnName="id")
* }
* )
*/
protected $idUserTwo;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", inversedBy="idUserInvited")
* #ORM\JoinTable(name="list_invited",
* joinColumns={
* #ORM\JoinColumn(name="id_user_invited", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_place_invited", referencedColumnName="id")
* }
* )
*/
protected $idPlaceInvited;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", mappedBy="idUserPresent")
*/
protected $idPlacePresent;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Place", inversedBy="idUserPlace")
* #ORM\JoinTable(name="user_place",
* joinColumns={
* #ORM\JoinColumn(name="id_user_place", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_place_place", referencedColumnName="id")
* }
* )
*/
protected $idPlacePlace;
/**
* #var array
* #ORM\Column(type="simple_array", length=200)
* #Serializer\Exclude()
*/
protected $roles;
/**
* Constructor
*/
public function __construct()
{
$this->idUserTwo = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlaceInvited = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlacePresent = new \Doctrine\Common\Collections\ArrayCollection();
$this->idPlacePlace = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id)
{
$this->id = $id;
}
/**
* #return string|null
*/
public function getUsername()
{
return $this->username;
}
/**
* #param string $username
*/
public function setUsername(string $username)
{
$this->username = $username;
}
/**
* #return string|null
*/
public function getPassword()
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password)
{
$this->password = $password;
}
/**
* #return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* #param bool $enabled
*/
public function setEnabled(bool $enabled)
{
$this->enabled = $enabled;
}
/**
* #return mixed
*/
public function getIdLocation()
{
return $this->idLocation;
}
/**
* #param mixed $idLocation
*/
public function setIdLocation($idLocation)
{
$this->idLocation = $idLocation;
}
/**
* #return mixed
*/
public function getIdProfile()
{
return $this->idProfile;
}
/**
* #param mixed $idProfile
*/
public function setIdProfile($idProfile)
{
$this->idProfile = $idProfile;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdUserTwo()
{
return $this->idUserTwo;
}
/**
* #param \Doctrine\Common\Collections\Collection $idUserTwo
*/
public function setIdUserTwo(\Doctrine\Common\Collections\Collection $idUserTwo)
{
$this->idUserTwo = $idUserTwo;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlaceInvited()
{
return $this->idPlaceInvited;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlaceInvited
*/
public function setIdPlaceInvited(\Doctrine\Common\Collections\Collection $idPlaceInvited)
{
$this->idPlaceInvited = $idPlaceInvited;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlacePresent()
{
return $this->idPlacePresent;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlacePresent
*/
public function setIdPlacePresent(\Doctrine\Common\Collections\Collection $idPlacePresent)
{
$this->idPlacePresent = $idPlacePresent;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdPlacePlace()
{
return $this->idPlacePlace;
}
/**
* #param \Doctrine\Common\Collections\Collection $idPlacePlace
*/
public function setIdPlacePlace(\Doctrine\Common\Collections\Collection $idPlacePlace)
{
$this->idPlacePlace = $idPlacePlace;
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return (Role|string)[] The user roles
*/
public function getRoles()
{
return $this->roles;
}
/**
* #param array $roles
*/
public function setRoles(array $roles)
{
$this->roles = $roles;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
// TODO: Implement getSalt() method.
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
}
some screen of data base : [https://ibb.co/iCSnWy
p
[https://ibb.co/cZZedd
p
you defined profile as type of \AppBundle\Entity\Profile this is the right way to work with doctrine. No to understand what is the difference in your entities and your database...
In the entities you need to set the entity not the id. Doctrine will then passt the id in the database, and when you get the entity from doctrine it will parse the id to an entity.
This means:
$user->setIdLocation($profile); //<-- set the entity
$profil = $user->getIdLocation(); // get the profile not the id
I have a form with 4 realtionships but have a problem with 2 of them.
Here is code for my form:
<?php
namespace Psw\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MeatType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name_pl')
->add('name_en')
->add('jm')
->add('vat')
->add('net_price')
->add('min')
->add('new')
->add('promotion', 'entity', array(
'class' => 'PswAdminBundle:Promotions',
'empty_value' => 'No promotion',
'required' => false
))
->add('promotion_price')
->add('producer', 'entity', array(
'class' => 'PswAdminBundle:MeatProducers'
))
->add('animals')
->add('categories')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Psw\AdminBundle\Entity\Meat'
));
}
public function getName()
{
return 'psw_adminbundle_meattype';
}
}
And entities
Meat:
<?php
namespace Psw\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Meat
*
* #ORM\Table()
* #ORM\Entity
*/
class Meat
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name_pl", type="string", length=255)
*/
private $name_pl;
/**
* #var string
*
* #ORM\Column(name="name_en", type="string", length=255)
*/
private $name_en;
/**
* #var string
*
* #ORM\Column(name="jm", type="string", length=10)
*/
private $jm;
/**
* #var integer
*
* #ORM\Column(name="vat", type="integer")
*/
private $vat;
/**
* #var float
*
* #ORM\Column(name="net_price", type="float")
*/
private $net_price;
/**
* #var float
*
* #ORM\Column(name="min", type="float")
*/
private $min;
/**
* #var boolean
*
* #ORM\Column(name="new", type="boolean")
*/
private $new;
/**
* #var integer
*
* #ORM\Column(name="promotion", type="integer", nullable=true)
* #ORM\ManyToOne(targetEntity="Promotions")
*/
private $promotion;
/**
* #var float
*
* #ORM\Column(name="promotion_price", type="float")
*
*/
private $promotion_price;
/**
* #ORM\ManyToMany(targetEntity="Animals")
* #ORM\JoinTable(name="meat_animals",
* joinColumns={#ORM\JoinColumn(name="meat_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="animal_id", referencedColumnName="id")}
* )
**/
private $animals;
/**
* #ORM\ManyToMany(targetEntity="MeatCategories")
* #ORM\JoinTable(name="meat_meat_categories",
* joinColumns={#ORM\JoinColumn(name="meat_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")}
* )
**/
private $categories;
/**
* #ORM\Column(name="producer", type="integer")
*
* #ORM\ManyToOne(targetEntity="MeatProducers", inversedBy="products")
* #ORM\JoinColumn(name="producer", referencedColumnName="id")
**/
private $producer;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name_pl
*
* #param string $namePl
* #return Meat
*/
public function setNamePl($namePl)
{
$this->name_pl = $namePl;
return $this;
}
/**
* Get name_pl
*
* #return string
*/
public function getNamePl()
{
return $this->name_pl;
}
/**
* Set name_en
*
* #param string $nameEn
* #return Meat
*/
public function setNameEn($nameEn)
{
$this->name_en = $nameEn;
return $this;
}
/**
* Get name_en
*
* #return string
*/
public function getNameEn()
{
return $this->name_en;
}
/**
* Set jm
*
* #param string $jm
* #return Meat
*/
public function setJm($jm)
{
$this->jm = $jm;
return $this;
}
/**
* Get jm
*
* #return string
*/
public function getJm()
{
return $this->jm;
}
/**
* Set vat
*
* #param integer $vat
* #return Meat
*/
public function setVat($vat)
{
$this->vat = $vat;
return $this;
}
/**
* Get vat
*
* #return integer
*/
public function getVat()
{
return $this->vat;
}
/**
* Set net_price
*
* #param float $netPrice
* #return Meat
*/
public function setNetPrice($netPrice)
{
$this->net_price = $netPrice;
return $this;
}
/**
* Get net_price
*
* #return float
*/
public function getNetPrice()
{
return $this->net_price;
}
/**
* Set min
*
* #param float $min
* #return Meat
*/
public function setMin($min)
{
$this->min = $min;
return $this;
}
/**
* Get min
*
* #return float
*/
public function getMin()
{
return $this->min;
}
/**
* Set new
*
* #param boolean $new
* #return Meat
*/
public function setNew($new)
{
$this->new = $new;
return $this;
}
/**
* Get new
*
* #return boolean
*/
public function getNew()
{
return $this->new;
}
/**
* Set promotion
*
* #param integer $promotion
* #return Meat
*/
public function setPromotion($promotion)
{
$this->promotion = $promotion;
return $this;
}
/**
* Get promotion
*
* #return integer
*/
public function getPromotion()
{
return $this->promotion;
}
/**
* Constructor
*/
public function __construct()
{
$this->animals = new ArrayCollection();
$this->categories = new ArrayCollection();
}
/**
* Add animals
*
* #param \Psw\AdminBundle\Entity\Animals $animals
* #return Meat
*/
public function addAnimal(\Psw\AdminBundle\Entity\Animals $animals)
{
$this->animals[] = $animals;
return $this;
}
/**
* Remove animals
*
* #param \Psw\AdminBundle\Entity\Animals $animals
*/
public function removeAnimal(\Psw\AdminBundle\Entity\Animals $animals)
{
$this->animals->removeElement($animals);
}
/**
* Get animals
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAnimals()
{
return $this->animals;
}
/**
* Add categories
*
* #param \Psw\AdminBundle\Entity\Meat_categories $categories
* #return Meat
*/
public function addCategorie(\Psw\AdminBundle\Entity\Meat_categories $categories)
{
$this->categories[] = $categories;
return $this;
}
/**
* Remove categories
*
* #param \Psw\AdminBundle\Entity\Meat_categories $categories
*/
public function removeCategorie(\Psw\AdminBundle\Entity\Meat_categories $categories)
{
$this->categories->removeElement($categories);
}
/**
* Get categories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
/**
* Set producer
*
* #param \Psw\AdminBundle\Entity\MeatProducers $producer
* #return Meat
*/
public function setProducer(\Psw\AdminBundle\Entity\MeatProducers $producer = null)
{
$this->producer = $producer;
return $this;
}
/**
* Get producer
*
* #return \Psw\AdminBundle\Entity\MeatProducers
*/
public function getProducer()
{
return $this->producer;
}
/**
* Set promotion_price
*
* #param float $promotionPrice
* #return Meat
*/
public function setPromotionPrice($promotionPrice)
{
$this->promotion_price = $promotionPrice;
return $this;
}
/**
* Get promotion_price
*
* #return float
*/
public function getPromotionPrice()
{
return $this->promotion_price;
}
}
MeatProducers:
<?php
namespace Psw\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MeatProducers
*
* #ORM\Table()
* #ORM\Entity
*/
class MeatProducers
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="Meat", mappedBy="producer")
**/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return MeatProducers
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function __toString()
{
return $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add products
*
* #param \Psw\AdminBundle\Entity\Meat $products
* #return MeatProducers
*/
public function addProduct(\Psw\AdminBundle\Entity\Meat $products)
{
$this->products[] = $products;
return $this;
}
/**
* Remove products
*
* #param \Psw\AdminBundle\Entity\Meat $products
*/
public function removeProduct(\Psw\AdminBundle\Entity\Meat $products)
{
$this->products->removeElement($products);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
There are no problems with many to many raltions on Animals and Categories.
But Promotions and Producers where i have many to one cause some issues.
Problem is that when trying to insert/update record doctrine calls __toString method instead of getId to obtain value to put into database.
I have working example like this with other entities, they are almost the same but in this case it just does not want to work.
Controller code is from crud generator, I didn't change anything there if necessary I may post it.
Question is how to make it use getId method?
The issue here is not with the __toString, it lies in your mapping annotation. You should not use #Column for association mappings. Instead, use #JoinColumn:
/**
* #ORM\ManyToOne(targetEntity="Promotions")
* #ORM\JoinColumn(name="promotion_id", referencedColumnName="id")
*/
private $promotion;
Reading this page I've setup a form to handle PATCH requests.
I've a Player entity:
<?php
namespace Acme\PlayerBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\PlayerBundle\Entity\Player
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\PlayerBundle\Entity\PlayerRepository")
*/
class Player
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="players")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #ORM\ManyToOne(targetEntity="Acme\TeamBundle\Entity\Team", inversedBy="players")
* #ORM\JoinColumn(nullable=false)
* #Assert\NotBlank()
*/
private $team;
/**
* #var integer $shirtNumber
*
* #ORM\Column(name="shirtNumber", type="smallint")
* #Assert\NotBlank()
*/
private $shirtNumber;
/**
* #var integer $vsid
*
* #ORM\Column(name="vsid", type="integer", nullable=true)
*/
private $vsid;
/**
* #var string $firstname
*
* #ORM\Column(name="firstname", type="string", length=255, nullable=true)
*/
private $firstname;
/**
* #var string $lastname
*
* #ORM\Column(name="lastname", type="string", length=255, nullable=true)
*/
private $lastname;
/**
* #var boolean $deleted
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted = false;
/**
* #var integer $role
*
* #ORM\Column(type="integer", nullable=true)
*/
private $role;
/**
* Create the user salt
*/
public function __construct()
{
//TODO: just for test
$this->uniqueId = substr(uniqid(), 0, 14);
}
/* MANAGED BY DOCTRINE, DON'T EDIT */
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Player
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set shirtNumber
*
* #param integer $shirtNumber
* #return Player
*/
public function setShirtNumber($shirtNumber)
{
$this->shirtNumber = $shirtNumber;
return $this;
}
/**
* Get shirtNumber
*
* #return integer
*/
public function getShirtNumber()
{
return $this->shirtNumber;
}
/**
* Set vsid
*
* #param integer $vsid
* #return Player
*/
public function setVsid($vsid)
{
$this->vsid = $vsid;
return $this;
}
/**
* Get vsid
*
* #return integer
*/
public function getVsid()
{
return $this->vsid;
}
/**
* Set firstname
*
* #param string $firstname
* #return Player
*/
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
/**
* Get firstname
*
* #return string
*/
public function getFirstname()
{
return $this->firstname;
}
/**
* Set lastname
*
* #param string $lastname
* #return Player
*/
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
/**
* Get lastname
*
* #return string
*/
public function getLastname()
{
return $this->lastname;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Player
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Set role
*
* #param integer $role
* #return Player
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role
*
* #return integer
*/
public function getRole()
{
return $this->role;
}
/**
* Set owner
*
* #param Acme\UserBundle\Entity\User $owner
* #return Player
*/
public function setOwner(\Acme\UserBundle\Entity\User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
/**
* Set team
*
* #param Acme\TeamBundle\Entity\Team $team
* #return Player
*/
public function setTeam(\Acme\TeamBundle\Entity\Team $team)
{
$this->team = $team;
return $this;
}
/**
* Get team
*
* #return Acme\TeamBundle\Entity\Team
*/
public function getTeam()
{
return $this->team;
}
}
and a Team entity:
<?php
namespace Acme\TeamBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\TeamBundle\Entity\Team
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\TeamBundle\Entity\TeamRepository")
*/
class Team
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $uniqueId
*
* #ORM\Column(name="uniqueId", type="string", length=15)
*/
private $uniqueId;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="teams")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=50)
* #Assert\NotBlank()
*/
private $name;
/**
* #var string $homeColor
*
* #ORM\Column(name="homeColor", type="string", length=7, nullable=true)
*/
private $homeColor;
/**
* #var string $awayColor
*
* #ORM\Column(name="awayColor", type="string", length=7, nullable=true)
*/
private $awayColor;
/**
* #var string $homeShirt
*
* #ORM\Column(name="homeShirt", type="string", length=50, nullable=true)
*/
private $homeShirt;
/**
* #var string $awayShirt
*
* #ORM\Column(name="awayShirt", type="string", length=50, nullable=true)
*/
private $awayShirt;
/**
* #var string $teamLogo
*
* #ORM\Column(name="teamLogo", type="string", length=50, nullable=true)
*/
private $teamLogo;
/**
* #var boolean $deleted
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted = false;
/**
* #ORM\OneToMany(targetEntity="Acme\PlayerBundle\Entity\Player", mappedBy="team", cascade={"persist", "remove"})
*/
private $players;
/**
* #ORM\OneToMany(targetEntity="Acme\MatchBundle\Entity\Match", mappedBy="homeTeam")
*/
private $homeMatches;
/**
* #ORM\OneToMany(targetEntity="Acme\MatchBundle\Entity\Match", mappedBy="awayTeam")
*/
private $awayMatches;
/**
* Create the user salt
*/
public function __construct()
{
$this->players = new ArrayCollection();
$this->homeMatches = new ArrayCollection();
$this->awayMatches = new ArrayCollection();
//TODO: just for test
$this->uniqueId = substr(uniqid(), 0, 14);
}
public function getMatches()
{
return array_merge($this->awayMatches->toArray(), $this->homeMatches->toArray());
}
/* MANAGED BY DOCTRINE, DON'T EDIT */
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set uniqueId
*
* #param string $uniqueId
* #return Team
*/
public function setUniqueId($uniqueId)
{
$this->uniqueId = $uniqueId;
return $this;
}
/**
* Get uniqueId
*
* #return string
*/
public function getUniqueId()
{
return $this->uniqueId;
}
/**
* Set name
*
* #param string $name
* #return Team
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set homeColor
*
* #param string $homeColor
* #return Team
*/
public function setHomeColor($homeColor)
{
$this->homeColor = $homeColor;
return $this;
}
/**
* Get homeColor
*
* #return string
*/
public function getHomeColor()
{
return $this->homeColor;
}
/**
* Set awayColor
*
* #param string $awayColor
* #return Team
*/
public function setAwayColor($awayColor)
{
$this->awayColor = $awayColor;
return $this;
}
/**
* Get awayColor
*
* #return string
*/
public function getAwayColor()
{
return $this->awayColor;
}
/**
* Set homeShirt
*
* #param string $homeShirt
* #return Team
*/
public function setHomeShirt($homeShirt)
{
$this->homeShirt = $homeShirt;
return $this;
}
/**
* Get homeShirt
*
* #return string
*/
public function getHomeShirt()
{
return $this->homeShirt;
}
/**
* Set awayShirt
*
* #param string $awayShirt
* #return Team
*/
public function setAwayShirt($awayShirt)
{
$this->awayShirt = $awayShirt;
return $this;
}
/**
* Get awayShirt
*
* #return string
*/
public function getAwayShirt()
{
return $this->awayShirt;
}
/**
* Set teamLogo
*
* #param string $teamLogo
* #return Team
*/
public function setTeamLogo($teamLogo)
{
$this->teamLogo = $teamLogo;
return $this;
}
/**
* Get teamLogo
*
* #return string
*/
public function getTeamLogo()
{
return $this->teamLogo;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Team
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Add players
*
* #param Acme\PlayerBundle\Entity\Player $players
* #return Team
*/
public function addPlayer(\Acme\PlayerBundle\Entity\Player $players)
{
$this->players[] = $players;
return $this;
}
/**
* Remove players
*
* #param Acme\PlayerBundle\Entity\Player $players
*/
public function removePlayer(\Acme\PlayerBundle\Entity\Player $players)
{
$this->players->removeElement($players);
}
/**
* Get players
*
* #return Doctrine\Common\Collections\Collection
*/
public function getPlayers()
{
return $this->players;
}
/**
* Add homeMatches
*
* #param Acme\MatchBundle\Entity\Match $homeMatches
* #return Team
*/
public function addHomeMatche(\Acme\MatchBundle\Entity\Match $homeMatches)
{
$this->homeMatches[] = $homeMatches;
return $this;
}
/**
* Remove homeMatches
*
* #param Acme\MatchBundle\Entity\Match $homeMatches
*/
public function removeHomeMatche(\Acme\MatchBundle\Entity\Match $homeMatches)
{
$this->homeMatches->removeElement($homeMatches);
}
/**
* Get homeMatches
*
* #return Doctrine\Common\Collections\Collection
*/
public function getHomeMatches()
{
return $this->homeMatches;
}
/**
* Add awayMatches
*
* #param Acme\MatchBundle\Entity\Match $awayMatches
* #return Team
*/
public function addAwayMatche(\Acme\MatchBundle\Entity\Match $awayMatches)
{
$this->awayMatches[] = $awayMatches;
return $this;
}
/**
* Remove awayMatches
*
* #param Acme\MatchBundle\Entity\Match $awayMatches
*/
public function removeAwayMatche(\Acme\MatchBundle\Entity\Match $awayMatches)
{
$this->awayMatches->removeElement($awayMatches);
}
/**
* Get awayMatches
*
* #return Doctrine\Common\Collections\Collection
*/
public function getAwayMatches()
{
return $this->awayMatches;
}
/**
* Set owner
*
* #param Acme\UserBundle\Entity\User $owner
* #return Team
*/
public function setOwner(\Acme\UserBundle\Entity\User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
}
Now I've created a player form class with the app/console and I've edited the team field to be an instance of the Team entity, this way:
<?php
namespace Acme\PlayerBundle\Form;
use Acme\TeamBundle\Entity\TeamRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PlayerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('shirtNumber')
->add('firstname')
->add('lastname')
->add('role')
->add('team', 'entity', array(
'class' => 'AcmeTeamBundle:Team',
'query_builder' => function(TeamRepository $er) {
$query = $er->createQueryBuilder('t');
return $query;
}
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\PlayerBundle\Entity\Player',
'csrf_protection' => false
));
}
public function getName()
{
return 'player';
}
}
And this is the relevant part of my controller:
/**
* Create a new player
*
* #Route(".{_format}", name="api_player_create")
* #Method("POST")
* #ApiDoc(
* description="Create a new player",
* statusCodes={
* 201="Player created and informations are returned",
* 400="Missing informations",
* 403="The user isn't authorized"
* },
* input="Acme\PlayerBundle\Form\PlayerType",
* return="Acme\PlayerBundle\Entity\Player"
* )
*
* #return Renders the player just created
*/
public function createPlayerAction()
{
return $this->processForm(new Player());
}
/**
* Edit a player
*
* #param integer $id The id of the player to be created
*
* #Route("/{id}.{_format}", name="api_player_patch", requirements={ "id": "\d+" })
* #Method("PATCH")
* #ApiDoc(
* description="Edit a player",
* statusCodes={
* 200="Player is updated",
* 400="Missing informations",
* 403="The user isn't authorized"
* },
* input="Acme\PlayerBundle\Form\PlayerType",
* return="Acme\PlayerBundle\Entity\Player"
* )
*
* #return Renders the player just edited
*/
public function editPlayerAction(Player $player)
{
if ($player->getOwner() != $this->getUser()) {
throw new ApiException\PermissionDeniedException;
}
return $this->processForm($player);
}
/**
* Function to handle a form to create/edit a player
*
* #param Player $player The player to be created or edited
*
* #return Api Response
*/
private function processForm(Player $player)
{
/**
* Check if the player is new (to be created) or we're editing a player
*/
$statusCode = is_null($player->getId()) ? 201 : 200;
$form = $this->createForm(new PlayerType(), $player);
$form->bind($this->getRequest());
if ($form->isValid()) {
if($player->getTeam()->getOwner() != $this->getUser()) {
throw new ApiException\PermissionDeniedException;
}
$player->setOwner($this->getUser());
$this->entityManager->persist($player);
$this->entityManager->flush();
return $this->apiResponse->getResponse($player, $statusCode);
}
return $this->apiResponse->getResponse($form, 400, 'Missing arguments');
}
The player creation works fine, the player edit doesn't, when the user makes the api request, passing the ID in the url and the name of the player I get:
Catchable Fatal Error: Argument 1 passed to Acme\PlayerBundle\Entity\Player::setTeam() must be an instance of Acme\TeamBundle\Entity\Team, null given, called in /Volumes/Dati/Users/alessandro/Sites/acme-api/vendor/symfony/symfony/src/Symfony/Component/Form/Util/PropertyPath.php on line 538 and defined in /Volumes/Dati/Users/alessandro/Sites/acme-api/src/Acme/PlayerBundle/Entity/Player.php line 278
Seems that form is trying to set the Team to null, why?
I've tried both sending and not the team as the form parameters but it doesn't work.
Any clue?
Figured it out that null unsent fields in the form is the default behaviour in symfony.
There is an open request for partial form binding in symfony. For now a guy created a form event subscriber that adds the missing fields with the actual values:
https://gist.github.com/3720535
This is his code:
<?php
namespace Foo;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
/**
* Changes Form->bind() behavior so that it treats not set values as if they
* were sent unchanged.
*
* Use when you don't want fields to be set to NULL when they are not displayed
* on the page (or to implement PUT/PATCH requests).
*/
class PatchSubscriber implements EventSubscriberInterface
{
public function onPreBind(FormEvent $event)
{
$form = $event->getForm();
$clientData = $event->getData();
$clientData = array_replace($this->unbind($form), $clientData ?: array());
$event->setData($clientData);
}
/**
* Returns the form's data like $form->bind() expects it
*/
protected function unbind($form)
{
if ($form->hasChildren()) {
$ary = array();
foreach ($form->getChildren() as $name => $child) {
$ary[$name] = $this->unbind($child);
}
return $ary;
} else {
return $form->getClientData();
}
}
static public function getSubscribedEvents()
{
return array(
FormEvents::PRE_BIND => 'onPreBind',
);
}
}
to add to the form you have to edit the buildForm method in the form class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$subscriber = new PatchSubscriber();
$builder->addEventSubscriber($subscriber);
$builder->add('name');
....
}
In this case you're ok to use the PATCH REST requests to edit an entity just by the sent fields