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
Related
I have a many-to-many-relation, and when I load an entity that is on one side this relation, I expect to see as its property the ArrayCollection of related entities on another side. However, this does not happen - the ArrayCollection loaded has no elements in it, while in the database I can see the related entries. What could be the reason?
Here is my code:
One side of the relation, ConsolidatedReport class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
Another side of the relation, Response class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\ConsolidatedReport\ConsolidatedReport", mappedBy="responses")
*/
private $consolidatedReports;
Here is the function I run to get an instance of ConsolidatedReport. This function sits inside a service that is being called from container:
/**
* Picks the consolidated report with given id.
*
* #param string $id
*
* #return ConsolidatedReport
*
* #throws NonExistentConsolidatedReportException if the survey doesn't exist
*/
public function pick($id)
{
$report = $this->repository->findOneBy(array('id' => $id));
if (!$report) {
throw new NonExistentConsolidatedReportException($id);
}
return $report;
}'
In the database, there is "con_rprt_responses" table with two columns "consolidated_reports_id" and "response_id". However, in profiler I do not see any queries to that table.
What could go wrong here?
UPDATE:
Please see my answer to this question below, that worked for me.
I added fetch="EAGER" to the $responses property of ConsolidatedReport class, and it worked.
The code now looks like this:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports", fetch="EAGER")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
More info here:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
Still if someone knows why the collection of related entity would not load without explicitly specifying EAGER fetching - please share your knowledge, it is highly appreciated!
If you specify the joinColumns, does this solve your problem?
/**
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses",
* joinColumns={#ORM\JoinColumn(name="consolidated_reports_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="response_id", referencedColumnName="id")}
* )
*/
The *toMany properties have to be initialized with an ArrayCollection.
public function __construct() {
$this->responses = new \Doctrine\Common\Collections\ArrayCollection();
$this-> consolidatedReports = new \Doctrine\Common\Collections\ArrayCollection();
}
In case you have more then single query to fetch the same objects using Doctrine try to use:
$entityManager->clear();
in between, to fix "missing" entities. It isn't solution "as is", however can give you an idea something wrong in chain of your queries.
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
So let's say we use a User and a Ticket class. They are normal entities, nothing fancy.
The User class contains this lines:
/**
* #ORM\ManyToMany(targetEntity="Ticket", mappedBy="collaborateurs")
**/
private $tickets;
The Ticket class contains this:
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="tickets")
* #ORM\JoinTable(name="users_to_tickets")
**/
private $collaborateurs;
To get all ticket's a user has I can just call the getTickets() function created by Symfony. As far as good. The Ticket class has a few additional fields like updated which is a DateTime field or status which is an integer. I would like to sort those tickets by status DESC and updated DESC
I know I could just make a function in the repository like findTicketsByUserOrderedByFooBar($user), but I'm wondering if there isn't a better way.
If you always want your tickets to be in that order you can set and orderBy on the association.
/**
* #ORM\ManyToMany(targetEntity="Ticket", mappedBy="collaborateurs")
* #ORM\OrderBy({"status" = "DESC", "updated" = "DESC"})
**/
private $tickets;
You can add an Helper method to your User entity and sort/filter DIRECTLY on the ArrayCollection with doctrine2 criteria. Something like this:
/**
* this return a ArrayCollection
*/
public function getTicketsByUserOrderedByFooBar()
{
$criteria = Criteria::create()
->orderBy(array('foo'=>'DESC','bar' =>'ASC'))
return $this->tickets->matching($criteria);
}
/**
* this return a ArrayCollection
*/
public function getTicketsByUserOrderedBytitle()
{
$criteria = Criteria::create()
->orderBy(array('title'=>'DESC'))
return $this->tickets->matching($criteria);
}
See also this
Hope this help.
Creating a function the way you suggested would be the suggested approach.
I am trying to display a "CategorieActivite" in an activite.html twig page.
I have 2 entities. The first one is Activite. The second one is CategorieActivite.
I have put annotations in the Activite.
I have prepared my query with join in ActiviteRepository.
It's ok in my database (foreign key).
BUT i get this error :
Method "CategorieActivite" for object "AssoFranceRussie\MainBundle\Entity\Activite" does not exist in (in my twig page)
Do I have to do something else to get some entity data from another entity?
Thanks
EB
The code :
class Activite
{
// $categorieActiviteId lié à l'entité CategorieActivite
// ManyToOne
/**
* #ORM\ManyToOne(targetEntity="AssoFranceRussie\MainBundle\Entity\CategorieActivite")
* #ORM\JoinColumn(name="categorie_activite_id", referencedColumnName="id")
*/
private $categorieActiviteId;
...
}
ActiviteRepository :
public function getAllActivites()
{
$query = $this->getEntityManager()->createQuery(
'SELECT a,c,n
FROM AssoFranceRussieMainBundle:Activite a
JOIN a.categorieActiviteId c
JOIN a.niveauActiviteId n
ORDER BY a.nom ASC '
);
return $query->getResult();
}
And in the twig html:
<p><strong>{{activite.CategorieActivite.libelle}}</strong></p>
You should create getter for $categorieActiviteId property.
So in Activite class you should have
public function getCategorieActivite() {
return $this->categorieActiviteId;
}
and in twig you should have:
<p><strong>{{activite.getCategorieActivite.libelle}}</strong></p>
Dont forget libelle have to be public method or property
Thank you for your help. It works now.
Actually i had already the getter with annotation in the class Activite. So, I should have write in twig {{activite.categorieActiviteId.libelle}} instead of {{activite.categorieActivite.libelle}}. The 2 ways to access the data work.
the getter in Activite class:
/**
* Get categorieActiviteId
*
* #return \AssoFranceRussie\MainBundle\Entity\CategorieActivite
*/
public function getCategorieActiviteId()
{
return $this->categorieActiviteId;
}
I have an Inheritance class as shown here:
As you can easily see users, buildings and hotels have addresses (more than one) and address table keeps the id of the owner in whose column.
Is my logic correct?
Let's say I want to get the address of user (or buildings or hotels) whose id is 2; must I run a DQL statement (and how?) or can I get it with find() function without DQL?
And I'll be happy if you give example since Doctrine documentation doesn't help much.
Thanks.
Edit: users, buildings and hotels are just symbolic names that is why they can have multiple addresses otherwise buildings and hotels would have only one address.
Edit 2:I think I couldn't make myself clear, when I talk about the Class Table Inheritance I mean entity class has the Discriminator column as
/**
* ...
*
* #DiscriminatorColumn(name="classname", type="string")
* #DiscriminatorMap({"Entities\users" = "Entities\users",
* "Entities\buildings" = "Entities\buildings"}) ... etc
*/
Each and every subclass is related to parent (Entity) with the foreign key relation as "id". But of course doctrine creates this relation already for me.
Usually an Address is a typical value object. Value objects are usually stored with the entity compositing the value object so it is neither about relations nor about class table inheritance. If your domain indicates otherwise (e.g. you can do something with your address, meaning), they might be an entity, than entity Hotel holds an entity Address (persisted in a n:m relation table) and entity Building holds and Address too (in a different n:m relation table).
If you go the value object route, things are different. You would store the address with the Building entity as well as with the Hotel entity (as you would do it with other value objects may it be Moneyor Email or Password). So you don’t need relations at all, just a few more fields. The issue with Doctrine 2 is, that it does not support Component mapping. Component mapping would be used to nicely store value objects. T accomplish the same thing with Doctrine 2, you would implement a #prePersist and a #postLoad handler like that:
class Hotel
{
private ;
/** These fields are persisted */
/** #Column(type=string) */
private $addressStreet;
/** #Column(type=string) */
private $addressCity;
/** #Column(type=string) */
private $addressZip;
/** #Column(type=string) */
private $addressCountry;
/** #prePersist */
public function serializeValueObjects()
{
$this->addressStreet = ->address->getStreet();
$this->addressCity = ->address->getCity();
$this->addressZip = ->address->getZip();
$this->addressCountry = ->address->getCountry();
}
public function unserializeValueObjects()
{
$this->address = new Address(->addressStreet, ->addressCity, ->addressZip, ->addressCountry);
}
}
As you need to serialize/unserialize Address value objects in various places, you might want to extract the serializing code into a separated class.
/**
*
* #Entity
* #Table(name="proposaltemplate")
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="entitytype", type="string")
* #DiscriminatorMap({"proposal" = "ProposalTemplate","page" = "PageTemplate"})
*
*/
abstract class AbstractProposalTemplate
{
/**
*
* #var integer
* #Id
* #Column(type="integer")
* #generatedValue(strategy="AUTO")
*
*/
private $id;
}
next
#Entity
class ProposalTemplate extends AbstractProposalTemplate
{
#Id
#Column(type="integer")
#generatedValue(strategy="AUTO")
private $id;
}
next another class
#Entity
class PageTemplate extends AbstractProposalTemplate
{
/**
*
* #var integer
* #Id
* #Column(type="integer")
* #generatedValue(strategy="AUTO")
*
*/
private $id;
}
So you've got a superclass called "Entity", which has subclasses "User", "Building", and "Hotel".
Your "Entity" entity should have a OneToMany relation to Address. Let's imagine it looks like this, in your Entity definition:
/**
* #OneToMany(targetEntity="Address", mappedBy="whose"
*/
protected $addresses;
This is a more-or-less fine approach, though the use of inheritance is a little smelly.
Then if you want to iterate over the addresses, from inside User, Building, or Hotel:
foreach($this->addresses as $address){
//do something with adderess
}
Does that answer your question?