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

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.

Related

Symfony Doctrine Count ManyToMany Relationship

I have an User entity with a many-to-many entity (Wish) linked to it, I now need the top 10 wishes now using the Symfony QueryBuilder.
The problem here is that i do not have direct access to the ManyToMany Table/Entity/Repository as its automatically created and managed by Doctrine ORM.
Making a manual OneToMany, ManyToOne Entity is not really an option as it will break existing code (mostly the automatic collection population and the add / remove functions of it)
Doing a manual SQL is kinda dodgy since the table name is generated and might change some update (even if not I would prefer to keep it clean)
some code:
class User {
// .........
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Wish")
*/
private $wish;
public function __construct()
{
$this->wish = new ArrayCollection(); // with get/set/add/remove etc
}
}
class Wish {
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
// ... get set etc
}

Doctrine 2 Mapping of 2 tables, error

Zend Framework 3, Doctrine 2. I created some mapping of 2 tables MySQL
/**
* #ORM\Entity
* #ORM\Table(name="object")
*/
class Object
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(name="id")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="..\Host", inversedBy="object")
* #ORM\JoinColumn(name="ip", referencedColumnName="IP")
*/
protected $host;
}
/**
* #ORM\Entity
* #ORM\Table(name="host")
*/
class Host
{
/**
* #ORM\Id
* #ORM\Column(name="IP")
*/
protected $ip;
/**
* #ORM\OneToOne(targetEntity="..\Object", mappedBy="host")
* #ORM\JoinColumn(name="IP", referencedColumnName="ip")
*/
protected $object;
}
I have this error:
Error "Missing value for primary key id on ..\Object".
Why? It seems to be done according to the example -> 5.3. One-To-One, Bidirectional from the site Doctrine
The parameters you pass to JoinColumn are incorrect. You can check the doc for JoinColumn here.
Try like this:
// For Object class:
// #ORM\JoinColumn(name="host_ip", referencedColumnName="ip")
// For Host class
// #ORM\JoinColumn(name="object_id", referencedColumnName="id")
In addition to this: mappedBy is not supported in OneToOne annotations. You can use targetEntity and inversedBy but not mappedBy.
On the other hand, making the ip your primary key seems wrong. You should have an auto-generated primary id for Host and use that to connect the entities. You can still set you ip field as unique, so you won't have duplicates. Plus you don't have any other fields, you can even just put the ip within Object class.
Also, I don't recommend using Object as your class name. It is too general.

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.

Can Doctrine2 map an object property to the entities' table?

i'm using Doctrine2 for a project with accounting for the user.
So the user object has a collection of account objects.
These account objects have an accounting period with Datetimes for the start and end of the period.
/** #Entity */
class Account
{
/** #Column(type="datetime") */
private periodStart;
/** #Column(type="datetime") */
private periodEnd;
}
Since I have to add some methods to the account class, that should handle the period, I think it would be good practice to refactor the period properties and put them in their own class with the new methods. something like:
/** #Entity */
class Account
{
/** ??? */
private accountPeriod;
}
/** #Entity */
class AccountPeriod
{
/** #Column(type="datetime") */
private periodStart;
/** #Column(type="datetime") */
private periodEnd;
public function doSomething(){...}
}
But then Doctrine2 would create a new table for AccountPeriod.
What I want is to keep the periodStart and periodEnd columns in the Account table. I searched for it but couldn't find anything - also I looked at custom types but as I understand they only map simple types but not aggregates.
I also want to use these columns in queries so the doctrine column type 'object' is no option.
Have somebody done this before, maybe with some trick?
Or is it just impossible with Doctrine2?
The answer for the above case is Embeddables:
Embeddables are classes which are not entities themselves, but are embedded in entities and can also be queried in DQL. You'll mostly want to use them to reduce duplication or separating concerns. Value objects such as date range or address are the primary use case for this feature.
The solution would be something like:
/** #Entity */
class Account
{
/** #Embedded(class = "AccountPeriod") */
private $accountPeriod;
}
/** #Embeddable */
class AccountPeriod
{
/** #Column(type="datetime") */
private periodStart;
/** #Column(type="datetime") */
private periodEnd;
public function doSomething(){...}
}

Accessing relations of a table with inheritance

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?

Categories