Accessing relations of a table with inheritance - php

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?

Related

Commit order issue when using OneToMany and ManyToOne relations on single inheritance table in Doctrine2

So I have a following single inheritance table defined:
/**
* #Entity
* #Table(name="listKeys")
* #InheritanceType("SINGLE_TABLE");
* #DiscriminatorColumn(name="parent", type="string")
* #DiscriminatorMap({"Type1" = "Something1", "Type2" = "Something2"})
*/
abstract class ListKey
{
/**
* #OneToMany(targetEntity="KeyListValue", mappedBy="listKey", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private Collection $listValues;
/**
* #ManyToOne(targetEntity="KeyListValue")
* #JoinColumn(name="defaultKeyListValueId")
*/
private ?KeyListValue $defaultKeyListValue = null;
}
With the list options defined as:
/**
* #Entity
* #Table(name="keyListValues")
*/
class KeyListValue
{
/**
* #Column(type="string")
*/
private $label;
/**
* #ManyToOne(targetEntity="ListKey", inversedBy="listValues", cascade={"persist"})
* #JoinColumn(name="caKeyId")
*/
private ListKey $listKey;
}
Before adding list values to a key, persisting and flushing worked ok, so the key would get inserted first after the commit order calculation. But once I added a new column default key list value, the commit order changes, and key list values try to get inserted first so the transaction fails.
Workaround is that I use flush() after creating new key and the adding the list values but this is problematic since ListKey also has association with some other entities which are not persisted yet at the time of flush so cascades would have to be declared and I don't want that. Any suggestion on how I can redefine this relationship better or a better workaround so the commit order would first insert keys?

Unidirectional many-to-one relation in Doctrine MongoDB ODM without MongoId

I'm trying to port the following Doctrine ORM example to Doctrine ODM.
<?php
/** #Entity */
class User
{
/**
* #ManyToOne(targetEntity="Address")
* #JoinColumn(name="address_id", referencedColumnName="address_id")
*/
private $address;
}
/** #Entity */
class Address
{
// ...
}
I'm looking for the counterpart of #JoinColumn(), which I couldn't find in the documentation. Basically, I want to set the referencing field name and the referenced field name myself. How can I do this?
In MongoDB you can only reference by id, but you're not limited to using MongoID's. In fact, you can use whatever you like, including objects as id's.
This is what you should do in MongoODM, to have a property of Address act as id and User will reference Address by the value of that field. You should also set simple=true for the reference.
/**
* #Document
*/
class User
{
/**
* #ReferenceOne(targetDocument="Address", simple=true)
*/
protected $address;
}
/**
* #Document
*/
class Address
{
/**
* #Id(strategy="NONE")
*/
protected $someProperty;
}
Remember that if you change the value of that property in any of the addresses that are referenced by one or more users, that reference will become corrupt and cause some painful errors in doctrine ODM.

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

Get subclass from repository using Class Table Inheritance

I have one entity, say Person, which contains a list of $pets:
protected $pets;
public function getPets()
{
return $this->pets;
}
Standard Doctrine. Unfortunately, these pets may be of different types, such as cats or dogs, or a mix. So I used Class Table Inheritance:
/**
* #ORM\Entity
* #ORM\Table(name="pets")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="pettype", type="string")
* #ORM\DiscriminatorMap({"cat_animal" = "CatAnimal", "dog_animal" = "DogAnimal"})
*/
class Pet
{
/**
* #ORM\Column(name="eventid", type="integer")
* #ORM\Id
*/
private $id; // protected did not work either
/**
* Get id
*/
public function getId()
{
return $this->id;
}
}
/**
* #ORM\Entity
* #ORM\Table(name="cat_animal")
*/
class CatAnimal extends Pet
{
/**
* #ORM\Column(type="float")
*/
protected $height;
// etc.
}
// DogAnimal class omitted.
This was relatively straightforward using Doctrine's docs.
If I want to get all cats for an individual person, I have discovered I can do this:
public function getCats($person)
{
return $this->getEntityManager()->getRepository('MyBundle:CatAnimal')
->findByPerson($person);
}
However, how do I access the subclasses using a query builder? If I have the Person repository ($repos here), I want to do something like the following:
$repos->createQueryBuilder('person')
->select('pet.height')
->join('person.pets', 'pet')
->where('person = :person')
->setParameter('person', $person);
Except Pet doesn't have height, so this throws an exception. The DQL generated automagically joins to DogAnimal and CatAnimal, so I should be able to access these properties, but I don't know how. I have tried:
$repos->createQueryBuilder('person')
->select('cat.height')
->from('MyBundle:CatAnimal', 'cat)
->join('person.pets', 'pet')
->where('person = :person')
->setParameter('person', $person);
But this seems to do the cartesian product. I can solve that by adding:
->andWhere('person.id = cat.person')
This seems overly complicated for what I want. I have tried looking for the correct way to do this, but resources are limited.
This builds on a previous question, with a similar structure. The names of the tables were changed for clarity and generalisability.
You need to join correctly to Person, adding a field to the Pet class. In my example I named it owner:
$catRepo->createQueryBuilder('cat')
->select('cat.height')
->from('MyBundle:CatAnimal', 'cat')
->join('cat.owner', 'person')
->where('person = :person')
->setParameter('person', $person);

How to handle Primary/Secondary/Normal statuses for entity in Symfony2

I am developing an application and I came across the following: Lets say I have an entity called Contact, that Contact belongs to a Company and the Company has a Primary Contact and a Secondary Contact and also has the remaining Contacts which I've named Normal.
My question is, what is the best approach for this when talking about entities properties and also form handling. I've though about two things:
Having 2 fields on the Company entity called PrimaryContact and SecondaryContact and also have a one-to-many relationship to a property called contacts.
What I don't like (or I'm not 100% how to do) about this option is that on the Contact entity I would need an inversedBy field for each of the 2 one-to-one properties and also 1 for the one-to-many relationship and my personal thought is that this is kind of messy for the purpose.
Having a property on the Contact entity called Type which would hold if it's primary, secondary or normal and in the Company methods that has to do with Contacts I would modify it and add the getPrimaryContact, getSecondaryContact, etc.
What I don't like about this option is that I would need to have 2 unmapped properties for the Company and I would need to do a lot on the form types in order to get this to work smoothly.
My question is what is the best approach for this structure and how to deal with forms and these dependencies. Let me know if this is not clear enough and I will take time and preparate an example with code and images.
I'm not yet a Symfony expert but i'm currently learning entites manipulation and relations !
And there is not simple way to do relations with attributes.
You have to create an entity that represent your relation.
Let's suppose you have an entity Company and and entity Contact
Then you will have an entity named CompanyContact whick will represent the relation between your objects. (you can have as many attributes as you wish in your relation entity). (Not sure for the Many-to-One for your case but the idea is the same)
<?php
namespace My\Namespace\Entity
use Doctrine\ORM\Mapping as ORM
/**
* #ORM\Entity(repositoryClass="My\Namespace\Entity\CompanyContactRepository")
*/
class CompanyContact
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="contact_type", type="string", length=255)
*/
private $contactType;
/**
* #ORM\ManyToOne(targetEntity="My\Namespace\Entity\Company")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
/**
* #ORM\ManyToOne(targetEntity="My\Namespace\Entity\Contact")
* #ORM\JoinColumn(nullable=false)
*/
private $contact;
}
And in your controller you can do this:
$em = $this->getDoctrine()->getManager();
$company = $em->getRepository('YourBundle:Company')->find($yourCompanyId);
$yourType = "primary";
$companyContacts = $em->getRepository('YourBundle:CompanyContact')
->findBy(array('company' => $company, 'type' => $yourType));
What do you think about this approach ?
If i learn more soon i will get you posted ;)
Thanks to #Cerad this is the following approach I took:
I have a OneToMany property on the Company to hold all the contacts.
Implemented the getPrimaryContact/setPrimaryContact methods and looped through all the contacts and retrieving the one of the type I want. Did the same for the secondary.
On the Form type of the company my issue was that I had the 'mapped' => 'false' option, I removed this since I implemented the getters and setters SF2 knows it has to go to these methods.
`
<?php
namespace XYZ\Entity;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Company
{
...
/**
* #ORM\OneToMany(targetEntity="\XYZ\Entity\Contact", mappedBy="company", cascade={"persist", "remove"})
*/
private $contacts;
public function getPrimaryContact() { ... }
public function setPrimaryContact(Contact $contact) { //Set the type of $contact and add it $this->addContact($contact) }
public function getSecondaryContact() { ... }
public function setSecondaryContact(Contact $contact) { //Set the type of $contact and add it $this->addContact($contact) }
}`
And for the Form Type I have:
`
class CompanyType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
...
->add('primaryContact', new ContactType())
->add('secondaryContact', new ContactType())
}
...
}`
With this set everything runs smoothly and I can CRUD without much struggle.

Categories