DOCTRINE :: OneToOne Relation crashes on persisting - php

getting deeper into doctrine. But actually I get stucked with this.
Got this to persist:
$f = new Field();
$f->setValue(123);
$em = $this->getEntityManager();
$em->persist($f);
$em->flush();
In my Entity called Field I will relate to an Entity called Integer
/**
* #ORM\OneToOne(targetEntity="Integer", mappedBy="field", cascade="persist")
**/
private $integer;
In my Entity Integer I need to relate to Field like this
/**
* #ORM\OneToOne(targetEntity="Field", inversedBy="integer")
* #ORM\JoinColumn(name="fid", referencedColumnName="id")
**/
private $field;
it works, but how can I read the integer? I can see the field ID in the integer table for the new entry. But shouldn't be there a column in Fields pointing to integers?
I tried to set a column for relating to integers in fields entity like this:
* #ORM\JoinColumn(name="int", referencedColumnName="id")
But it did not work
cu n00n

Related

Doctrine2 join column

I have defined the follow entity in doctrine2 (with symfony).
/**
*
* #ORM\Table(name="order")
* #ORM\Entity
*/
class Order
/**
* #var integer
*
* #ORM\Column(name="personid", type="integer", nullable=false)
*/
private $personid;
/**
* #ORM\OneToOne(targetEntity="People")
* #ORM\JoinColumn(name="personid", referencedColumnName="personid")
*/
private $person;
public function getPersonId()
{
return $this->personid;
}
public function getPerson()
{
return $this->person;
}
}
I realize that if I call $order->getPersonId() it return always an empty value and I have to call the getPerson()->getId() method to get the correct personid.
Could anyone explain me why the variable $personid is not filled?
Should I to delete the column id used for the join if I defined one?
Thanks
Gisella
You should remove private $personid;, it's better to work with objects only in an ORM.
It's not a problem if you get the ID with $order->getPerson()->getId(), because Doctrine won't load the complete entity. The People entity will only be loaded if you call an other field than the join key.
You can still have a getter shortcut like this :
public function getPersonId()
{
return $this->getPerson()->getId();
}
Edit :
You can also still work with "ID" if you use Doctrine references, like this :
$order->setPerson($em->getReference('YourBundle:People', $personId));
With this way, Doctrine won't perform a SELECT query to load data of the person.
You don't need to have the $personid field when you already have the $person field.
$people contains the People object (with all People's attributes including the id).
Moreover, when doctrine translate your object into sql tables, he knows that he have to join with th id so it will create a field (in database) named personid. (It's the name that you defined in your ORM)
/**
* #ORM\OneToOne(targetEntity="People")
* #ORM\JoinColumn(name="personid", referencedColumnName="personid")
*/
private $person;
Sorry for bad english :p

Symfony Doctrine One to Many does not insert foreign key

I am having annoying problems with persisting an entity with one or more OneToMany-Childs.
I have a "Buchung" entity which can have multiple "Einsatztage" (could be translated to an event with many days)
In the "Buchung entity I have
/**
* #param \Doctrine\Common\Collections\Collection $property
* #ORM\OneToMany(targetEntity="Einsatztag", mappedBy="buchung", cascade={"all"})
*/
private $einsatztage;
$einsatztage is set to an ArrayCollection() in the __constructor().
Then there is the "Einsatztag" Entity which has a $Buchung_id variable to reference the "Buchung"
/**
* #ORM\ManyToOne(targetEntity="Buchung", inversedBy="einsatztage", cascade={"all"})
* #ORM\JoinColumn(name="buchung_id", referencedColumnName="id")
*/
private $Buchung_id;
Now If I try to persist an object to the database the foreign key of the "Einsatztag" Table is always left empty.
$buchung = new Buchung();
$buchung->setEvent( $r->request->get("event_basis"));
$buchung->setStartDate(new \DateTime($r->request->get("date_from")));
$buchung->setEndDate(new \DateTime($r->request->get("date_to")));
$von = $r->request->get("einsatz_von");
$bis = $r->request->get("einsatz_bis");
$i = 0;
foreach($von as $tag){
$einsatztag = new Einsatztag();
$einsatztag->setNum($i);
$einsatztag->setVon($von[$i]);
$einsatztag->setBis($bis[$i]);
$buchung->addEinsatztage($einsatztag);
$i++;
}
$em = $this->getDoctrine()->getManager();
$em->persist($buchung);
foreach($buchung->getEinsatztage() as $e){
$em->persist($e);
}
$em->flush();
Firstly, you have to understand that Doctrine and Symfony does not work with id's within your entities.In Einsatztag entity, your property should not be called $Buchung_id since it's an instance of buchung and not an id you will find out there.
Moreover, in your loop, you add the Einsatztag to Buchung. But do you process the reverse set ?
I do it this way to always reverse the set/add of entities.
Einsatztag
public function setBuchung(Buchung $pBuchung, $recurs = true){
$this->buchung = $pBuchung;
if($recurs){
$buchung->addEinsatztag($this, false);
}
}
Buchung
public function addEinsatztag(Einsatztag $pEinsatztag, $recurs = true){
$this->einsatztages[] = $pEinsatztag;
if($recurs){
$pEinsatztag->setBuchung($this, false);
}
}
Then, when you will call
$buchung->addEinsatztag($einsatztag);
Or
$einsatztag->set($buchung);
The relation will be set on both side making your FK to be set. Take care of this, you'll have some behavior like double entries if you do not use them properly.
SImplier , you can use default getter/setters and call them on both sides of your relation, using what you already have, like following:
$einsatztag->set($buchung);
$buchung->addEinsatztag($einsatztag);
Hope it helped ;)
First of all, don't use _id properties in your code. Let it be $buchung. If you want it in the database, do it in the annotation. And this also the reason, why it's not working. Your are mapping to buchung, but your property is $Buchung_id
<?php
/** #ORM\Entity **/
class Buchung
{
// ...
/**
* #ORM\OneToMany(targetEntity="Einsatztag", mappedBy="buchung")
**/
private $einsatztage;
// ...
}
/** #ORM\Entity **/
class Einsatztag
{
// ...
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="einsatztage")
* #JoinColumn(name="buchung_id", referencedColumnName="id")
**/
private $buchung;
// ...
}
You don't have to write the #JoinColumn, because <propertyname>_id would the default column name.
I'm going to ignore the naming issue and add a fix to the actual problem.
You need to have in the adder method a call to set the owner.
//Buchung entity
public function addEinsatztage($einsatztag)
{
$this->einsatztags->add($einsatztag);
$ein->setBuchung($this);
}
And to have this adder called when the form is submitted you need to add to the form collection field the by_reference property set to false.
Here is the documentation:
Similarly, if you're using the CollectionType field where your underlying collection data is an object (like with Doctrine's ArrayCollection), then by_reference must be set to false if you need the adder and remover (e.g. addAuthor() and removeAuthor()) to be called.
http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference

Unable to persist complex entity graph in Symfony2/Doctrine2

I'm a bit limited in the details I can provide due to a NDA, so please bear with me.
I have a complex entity graph. It consists of:
A 1-to-1 relationship between a Parent and Child.
The Child contains an ArrayCollection of FooChild entities. Cascade all.
FooChild represents a many-to-many join table between Foo and Child, but also contains some metadata that Child needs to track. Cascade persist on each side (Foo and Child)
Parents aren't required to have a Child.
To be 100% clear regarding FooChild, the relationship is many-to-many, but because of the metadata, it contains many-to-one relationship definitions:
/**
* #ORM\Entity
* #ORM\Table(name="foo_children", indexes={
* #ORM\Index(name="fooid_idx", columns={"foo_id"}),
* #ORM\Index(name="childid_idx", columns={"child_id"}),
* })
*/
class FooChild
{
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Foo", cascade={"persist"})
* #ORM\JoinColumn(name="foo_id", referencedColumnName="id", nullable=false)
*/
protected $foo;
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Child", inversedBy="fooChildren", cascade={"persist"})
* #ORM\JoinColumn(name="child_id", referencedColumnName="id", nullable=false)
*/
protected $child;
/**
* #ORM\Column(type="smallint")
*/
private $count;
// methods
}
Okay, so with that structure, on the Parent edit page, I created the option for someone to add a Child to it and populate it with FooChilds with the Symfony prototype mechanism seen here. When I attempt to submit the rather large form, I get the following exception:
Entity of type MyBundle\Entity\FooChild has identity through a foreign entity MyBundle\Entity\Child, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'MyBundle\Entity\FooChild'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.
The thing is, I've attempted to persist the various parts of this graph in different orders, and the exception still remains. My current attempt is:
$form = $this->createForm(new ParentType(), $parent);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->has('child')) {
$data = $form->getData();
$child = $data->getChild();
$fooChildren = $child->getFooChildren();
foreach ($fooChildren as $fc) {
$em->persist($fc);
$em->flush();
}
$em->persist($child);
$em->flush();
}
$em->persist($parent);
$em->flush();
}
The exception is thrown at the first attempt to persist, in the foreach. Like I said before, I've swapped the order of what gets persisted when several times, but it hasn't made a difference. I'm not sure what else to try.
I had an initial solution of removing the Child's form type and having the Parent's form type handle unmapped (very important) FooChild entries. Then, in the controller, I had:
$em->persist($parent);
$em->flush();
if ($form->has('fooChildren')) {
$child = new Child();
$child->setParent($parent);
$em->persist($child);
$em->flush();
// run through the FooChild entites and add them to the child
}
It worked, but I ran into some other non-related issues, so I'm currently reorganizing my schema.

Doctrine 2.2/Symfony 2.2: Persisting an entity with a composite foreign key

I must first establish that I'm a total newcomer to Doctrine, even though I know enough about SQL and PHP/Symfony 2.
So, I created this IssueType entity associated to a SQL table:
/**
* IssueType
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Blog\Bundle\CoreBundle\Entity\IssueTypeRepository")
*/
class IssueType
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
// Getters, setters...
}
I populated it, so the content of said table is now:
id | name
1 | Bande dessinée
2 | Livre
3 | Film
4 | Disque
Now I have this other entity, Role, which uses a composite key, made up of a regular string (name) and a foreign key (id from IssueType):
/**
* Role
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Blog\Bundle\CoreBundle\Entity\RoleRepository")
*/
class Role
{
/**
* #var IssueType
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\IssueType")
* #ORM\JoinColumn(nullable=false)
* #ORM\Id
*/
private $issueType;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #ORM\JoinColumn(nullable=false)
* #ORM\Id
*/
private $name;
// Getters, setters...
}
Both tables are correctly generated by doctrine in the database. However, although it should be trivial, I can't for the life of me find a single example of a correct and successful persist operation in such a case.
What I try to do is the following:
$manager = $this->getDoctrine()->getManager();
$issueType = new IssueType();
$issueType->setId(1);
$role = new Role();
$role->setIssueType($issueType);
$role->setName('Dessinateur');
$manager->persist($role);
$manager->flush();
I thus try to persist the following:
Role: {
IssueType: {id: 1, name: ''},
name: 'Dessinateur',
}
And what I get is this nasty exception:
Entity of type Blog\Bundle\CoreBundle\Entity\Role has identity through a foreign entity Blog\Bundle\CoreBundle\Entity\IssueType, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'Blog\Bundle\CoreBundle\Entity\Role'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.
I understand it wants me to persist first the foreign entity, but I don't want to do that, since the foreign issue type of ID#1 already exists in the database and thus don't need persisting. How can it ask me that when I did not specify any 'cascade' attribute in the annotations?
BTY I tried anyway to do as it says, and it expectedly ended up with a duplicate entry error.
So, what should I do to make Doctrine understand that the foreign issue type should not be persisted?
EDIT
artmees came up with the following solution, which works fine:
$manager = $this->getDoctrine()->getManager();
$issueType = $manager->getRepository('BlogCoreBundle:IssueType')->find(1);
$role = new Role();
$role->setIssueType($issueType);
$role->setName('Dessinateur');
$manager->persist($role);
$manager->flush();
However this implies making an additional request to the database which could have been avoided if not using Doctrine. Since I already know the foreign Id to use, is there any way to use it directly with the persist(), without going to such lengths as actually retrieving the full object from the database?
try this
$manager = $this->getDoctrine()->getManager();
$issueType = $manager->find('IssueTypeRepository', 1);
$role = new Role();
$role->setIssueType($issueType);
$role->setName('Dessinateur');
$manager->persist($role);
$manager->flush();
I know this is an old article, but...
Just to add to artmees answer, if you know the ID and you just want to insert that, you don't need to load the entity, simply use a reference.
$manager = $this->getDoctrine()->getManager();
$issueType = $manager->getReference('BlogCoreBundle:IssueType',1);
$role = new Role();
$role->setIssueType($issueType);
$role->setName('Dessinateur');
$manager->persist($role);
$manager->flush();
That will create a proxy with that ID (1), which is all you need in order to save the role entity.

Problem saving from inversed side of many-to-many relationship

I have two entities: AudioPlaylist and AudioTrack.
AudioPlaylist.php:
/**
* #ORM\ManyToMany(targetEntity = "AudioTrack", inversedBy = "audioPlaylists")
* #ORM\JoinTable(name = "audioplaylist_audiotrack")
*
* #var ArrayCollection
*/
protected $audioTracks;
AudioTrack.php:
/**
* #ORM\ManyToMany(targetEntity = "AudioPlaylist", mappedBy = "audioTracks")
*
* #var ArrayCollection
*/
protected $audioPlaylists;
My problem is that when I call $audioTrack->addAudioPlaylist($audioPlaylist), the audioplaylist_audiotrack table doesn't get updated. I'm expecting a new row to be added to the table signifying the relationship between the two entities. Everything works fine for the inverse though $audioPlaylist->addAudioTrack($audioTrack) adds a new row.
I'm making sure to persist $audioTrack and flush the entity manager, but no luck, so I assume there must be something wrong with my annotations (I'm using this example from the Doctrine docs). Any ideas?
This is probably because you have not set the cascade property for your inverse side. You must define cascading explicitly for Doctrine2 to persist any related entities.
/**
* #ORM\ManyToMany(targetEntity = "AudioPlaylist",
* mappedBy = "audioTracks",
* cascade = {"persist", "remove"})
*
* #var ArrayCollection
*/
protected $audioPlaylists;
Make sure you also add your AudioTrack to AudioPlaylist as well, when calling AudioTrack::addAudioPlaylist():
public function addAudioPlaylist(AudioPlaylist $playlist)
{
$this->getAudioPlaylists()->add($playlist);
$playlist->getAudioTracks()->add($this);
}

Categories