symfony2 doctrine manytoone relationship - php

I believe it's that there must be a simple solution for my issue but I can't get that to work and I'm not sure why.
Scenario:
I've got 2 entities: Bike and Review, relationship is OneToMany:
/**
* Bike
*
* #ORM\Entity(repositoryClass="BikeRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="bike")
*/
class Bike
{
...
/**
* #var string
*
* #ORM\OneToMany(targetEntity="Review", mappedBy="bike", cascade={"persist"}, fetch="EXTRA_LAZY")
*/
private $reviews;
...
}
/**
* Review
*
* #ORM\Entity(repositoryClass="ReviewRepository")
* #ORM\Table(name="review")
* #ORM\HasLifecycleCallbacks()
*/
class Review
{
...
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="Bike", inversedBy="reviews", fetch="EXTRA_LAZY")
* #ORM\JoinColumn(name="bike_id", referencedColumnName="id")
*/
private $bike;
....
}
Getters and setters has been generated by doctrine.
On the frontend I've got a form where bike is as a hidden field because there is an autocomplete field with ajax request. So in the controller if $form->isValid() I'm getting bike id from the hidden field, search database for that bike and set bike as below:
if ($form->isValid()) {
if ($bikeId) {
$bike = $this->em->getRepository('BikeBundle:Bike')->findOneBy(array('id' => $bikeId));
$review->setBike($bike);
}
$this->em->persist($review);
$this->em->flush();
}
And this is always giving me NULL in the database for bike_id. Any idea what could be wrong? I have tried to dump $review before persist and I can get the bike details so I don't know what's going wrong. In the entity setBike() method I can get any value of that Bike object except getId() is returning NULL.
Thanks for any help.

you don't need to persist an existing entity, so you can just save your entity like
$review->setBike($bike);
$this->em->flush();
try to check if doctrine really returns any entities..I'm afraid if it doesn't return Bike entity
UPD: Also try to check the namespace of Bike Entity class. It seems like it is wrong in your case, because usually it looks like "AcmeBikeBundle:Bike"

Related

Symfony2: Doctrine does not load related entities in many-to-many relation

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.

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.

Doctrine2 - add to product entity parameter with value

Please, could you help me?
I´m looking for the best way, how to add to entity (Product) some parametrs with values.
For example:
Product T-Shirt would have parametrs: size: XXL, color: red, material: cotton. How to make tables to have the best result - easy adding parametrs to product and easy filtering products by parametrs.
Thank you for your opinions.
You have two options:
A OneToMany relationship with another entity (recommended)
You may create a new entity called ProductProperty and declare a OneToMany relationship from Product to ProductProperty, like this:
The Product entity
/**
* #ORM\Entity
*/
class Product
{
/**
* #ORM\OneToMany(targetEntity="ProductProperty", mappedBy="product", cascade={"remove"})
*/
public $properties;
}
The ProductProperty entity
/**
* #ORM\Entity
*/
class ProductProperty
{
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="properties")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
public $product;
}
Create an array property in entity Product
Doctrine 2 supports arrays (it serializes the array into a TEXT column). Create a property that behaves like an array:
/**
* #ORM\Entity
*/
class Product
{
/** #ORM\Column(type="array") */
public $properties;
public function __construct()
{
$this->properties = []; //new PHP array notation, if using older PHP use array()
}
}

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?

Many To Many Mapping Issue

I'm currently trying to map music related data using Doctrine2's POPO Annotations.
I haven't had problems mapping any other many-to-many relations, but one specific relation is giving me trouble. It does not throw an error, but the mapping does not get inserted into the mapping table (artist_album)
Artist:
<?php
/**
* #orm:Entity
* #orm:Table(name="artist")
*/
class Artist
{
...
/**
* #orm:ManyToMany(targetEntity="Company\MusicBundle\Entity\Album", inversedBy="artists", cascade={"persist"})
* #orm:JoinTable(name="artist_album",
* joinColumns={#orm:JoinColumn(name="artist_id", referencedColumnName="id")},
* inverseJoinColumns={#orm:JoinColumn(name="album_id", referencedColumnName="id")})
*
* #var ArrayCollection
*/
private $albums;
...
}
Album
....
/**
* #orm:ManyToMany(targetEntity="Company\MusicBundle\Entity\Artist", mappedBy="albums", cascade={"persist"})
*
* #var ArrayCollection
*/
private $artists;
...
}
I'm sure it's just something in I've done wrong in the mapping, but I just can't put my proverbial finger on it.
My problem was that I was setting the artist on the inverse side of the relationship. It appears you must set the relationship on the owning side (in this case, Artist).

Categories