I have a query that looks like this:
My user entity has a one-to-one relation that looks like this:
/**
* #var UserProfile
*
* #ORM\OneToOne(targetEntity="UserProfile",mappedBy="user")
*/
private $userProfile;
Anytime I make a query to select multiple user objects, it creates an additional select statement per user to query for the UserProfile data even though I am not accessing it through a get method. I don't always need the UserProfile data, and I certainly don't want to load this data every single time I'm displaying a list of users.
Any idea why these queries are executed at run time?
Here is solutions explain with details :
https://groups.google.com/forum/#!topic/doctrine-user/fkIaKxifDqc
"fetch" in the mapping is a hint, that is, if it is possible
Doctrine does that, but if its not possible, obviously it does not.
Proxying for lazy-loading is simply not always possible, technically.
The situations where its not possible are:
1) one-to-one from inverse to owning side (appears only in
bidirectional one-to-one associations). Precondition a) above can not
be met. 2) one-to-one/many-to-one association to a hierarchy and the
targeted class has subclasses (is not a leaf in the class hierarchy).
Precondition b) above can not be met.
In these cases, proxying is technically not possible.
Your options to avoid this n+1 problem:
1) fetch-join via DQL: "select c,ca from Customer join c.cart ca".
Single query but join, however, joins on to-one associations are
relatively cheap.
2) force partial objects. No additional queries but
also no lazy-load: $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD,
true)
3) if an alternative result format (i.e. getArrayResult()) is
sufficient for a use-case, these also avoid this problem.
Benjamin had some ideas about automatic batching of these loads to
avoid n+1 queries but this does not change the fact that proxying is
not always possible.
I spent a lot of time searching for a solution. For me, none of the options were satisfying enough, but maybe I can save someone some time with this list of workarounds:
1) Change the owning side and inverse side http://developer.happyr.com/choose-owning-side-in-onetoone-relation - I don't think that's right from a DB design perspective every time.
2) In functions like find, findAll, etc, the inverse side in OneToOne is joined automatically (it's always like fetch EAGER). But in DQL, it's not working like fetch EAGER and that costs the additional queries. Possible solution is every time to join with the inverse entity
3) If an alternative result format (i.e. getArrayResult()) is sufficient for some use-cases, that could also avoid this problem.
4) Change inverse side to be OneToMany - just looks wrong, maybe could be a temporary workaround.
5) Force partial objects. No additional queries but also no lazy-loading: $query->setHint (Query::HINT_FORCE_PARTIAL_LOAD, true) - seams to me the only possible solution, but not without a price:
Partial Objects are a little bit risky, because your entity behavior is not normal. For example if you not specify in ->select() all associations that you will user you can have an error because your object will not be full, all not specifically selected associations will be null
6) Not mapping the inverse bi-directional OneToOne association and either use an explicit service or a more active record approach - https://github.com/doctrine/doctrine2/pull/970#issuecomment-38383961 - And it looks like Doctrine closed the issue
It seems that this is a open issue in Doctrine, see also
4.7.1. Why is an extra SQL query executed every time I fetch an entity with a one-to-one relation?
If Doctrine detects that you are fetching an inverse side one-to-one association it has to execute an additional query to load this object, because it cannot know if there is no such object (setting null) or if it should set a proxy and which id this proxy has.
To solve this problem currently a query has to be executed to find out this information.
Source
As #apfelbox explained... there is no fix for it now.
I went for a OneToMany solution in a combination with unique key:
User.php
/**
* #ORM\OneToMany(targetEntity="TB\UserBundle\Entity\Settings", fetch="EXTRA_LAZY", mappedBy="user", cascade={"all"})
*/
protected $settings;
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getSettings()
{
return $this->settings;
}
And
Settings.php
/**
* #ORM\ManyToOne(targetEntity="TB\UserBundle\Entity\User", fetch="EXTRA_LAZY", inversedBy="settings")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
protected $user;
And to ensure the uniqueness in Settings.php include:
use Doctrine\ORM\Mapping\UniqueConstraint;
And add unique index
/**
* #ORM\Entity
* #ORM\Table(name="user_settings", uniqueConstraints={#UniqueConstraint(name="user", columns={"user_id"})})
*/
class Settings
So when I want to access the user Settings I just need to this (which will fire ONE query ONLY in that specific moment)
$_settings = $user->getSettings()->current();
I think is the cleanest solution.
There is another option (which is the best IMHO) - you could use unidirectional OneToOne.
In your case - if you use UserProfile rarely - setup link in UserProfile
/**
* #var User
*
* #ORM\OneToOne(targetEntity="User")
*/
private $user;
And just dont map it in User. You could load it, when you will need it.
If you use UserProfile often - you could make it part of User entity.
According to the reference you can add the optional attribute fetch
/**
* #var UserProfile
*
* #ORM\OneToOne(targetEntity="UserProfile",mappedBy="user", fetch="LAZY")
*/
private $userProfile;
Related
Sorry for the vagueness, but I'm confused as to how to describe this database problem. Part of it is a database design problem, the other part is how to efficiently use the Doctrine ORM and Symfony's Form builder engine for this. Let me try to explain:
My database contains repairs for cell phones. Each repair is for one cellphone only. However, some cellphones come in different colours. While some repairs (like battery replacement) are applicable to all devices, some (like display replacement) have different prices depending on the colour of the device. It should be possible to, in the repair menu, specify the colour of each repair (choose one, many or all).
My current DB design looks like this:
But this does not allow me to access the colour of the device from the repair Entity, as here colour has a direct relationship to device, not to repair. So I need a more complex database which still treats the iphone as a single product, but can map various repairs to its many variants as well as map universal repairs to all variants.
Should I use a join table (repair_device_color) consisting of 3 ids? Seems not pretty. And what's the most efficient way to map this with the Doctrine ORM? Should I use data inheritance?
I wanted to give you a good answer, so it took me a bit of time to create my answer. This may not be perfect, but it will give you an idea. The digram you gave is not an ER Diagram by the way.
I created this in Excel, and copied and saved as a picture. The table names and ORM annotation are important.
For example you will create an "Device" Entity with the file name as "src/AppBundle/Entity/Device.php" in the standard way. For the arrays, you need to add to a __construct() function in the Entity class.
As an example, this is what the code would look like for the Device Entity:
<?php
// src/AppBundle/Entity/Device.php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="device")
*/
class Device
{
/**
* #ORM\Id
* #ORM\Column(name="device_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $device_id;
/**
* #ORM\Column(name="model", type="string", unique=true)
*/
protected $model;
/**
* #ORM\OneToMany(targetEntity="Part", mappedBy="part_device")
*/
protected $parts = null;
...
public function __construct(){
// This is an array of parts.
$this->parts = new ArrayCollection();
// Create the other arrays too.
$this->repairs = new ArrayCollection();
$this->customers = new ArrayCollection();
}
/*
* This should automatically be generated by the command.
* php bin/console doctrine:generate:entities AppBundle/Entity/Device
*/
public function addPart($part){
$this->parts[] = $part;
}
}
Hopefully you can figure out the rest. But I think this is a good start.
Depending on a real world repairing scenario, where one might change the color of the device by changing body, You might want to maintain additional 3 entity instead of one association devices_colous_repairs :
device_attributes (Many-To-One with device)
device_repair_attrbutes (Many-To-One with repair ids only if the attribute changed in repairing process.
Additional repair_billing to store bill cost as per standard / attrib change. (Many-To-One with repair ids with optional foreign key with device_repair_attrbutes where applicable.
Just a suggestion to normalize the data.
Typically when you implement a entity using Doctrine you map it to a table explicitly:
<?php
/**
* #Entity
* #Table(name="message")
*/
class Message
{
//...
}
Or you reply on doctrine to implicitly map your class name to a table...I have several tables which are identical in schema but I do not wish to re-create the class for each time...there fore at runtime (dynamically) I would like to change the table name accordingly.
Where do I start or what would I look into overriding to implement this odd requirement???
Surprisingly (to me), the solution is very simple. All you have to do is to get the ClassMetadata of your entity and change the name of the table it maps to:
/** #var EntityManager $em */
$class = $em->getClassMetadata('Message');
$class->setPrimaryTable(['name' => 'message_23']);
You need to be careful and do not change the table name after you have loaded some entities of type Message and changed them. It's a big chance it will either produce SQL errors on saving (because of table constraints, for example), if you are lucky or it will modify the wrong row (from the new table).
I suggest the following workflow:
set the desired table name;
load some entities;
modify them at will;
save them;
detach them from the entity manager (the method EntityManager::clear() is a quick way to start over);
go back to step 1 (i.e. repeat using another table).
The step #5 (detach the entities from the entity manager) is useful even if you don't change or don't save the entities. It allows the entity manager use less memory and work faster.
This is just one of the many methods you can use to dynamically set/change the mapping. Take a look at the documentation of class ClassMetadata for the rest of them. You can find more inspiration in the documentation page of the PHP mapping.
I would like to set up transitive persistence in the single table inheritance mapping I’ve developed with doctrine in zf2. I’ve set up the mapping similar to the example presented in doctrine’s documentation for single table inheritance:
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/**
* #Entity
*/
class Employee extends Person
{
// ...
}
If a record from the Employee table ever gets damaged or goes missing, I'd like for it to get replaced. However, the example above does not provide for that functionality on its own and doctrine won't allow me to write an INSERT dql in an error checker because they say entities and their relations have to be introduced into the persistence context through EntityManager#persist() to ensure consistency of your object model.
Examples in doctrine’s documentation for cascade operations show a neat way to place cascade settings in a #OneToMany mapping statement. However, none of the class table inheritance mapping statements (#InheritanceType, #DiscriminatorColumn, or #DiscriminatorMap) seem to want to accept cascade settings this way.
How and where can the cascade operations be configured for a single table inheritance strategy?
Association mapping in Doctrine 2
<?php
class User
{
//...
/**
* #ManyToMany(targetEntity="Group")
* #JoinTable(name="User_Group",
* joinColumns={#JoinColumn(name="User_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="Group_id", referencedColumnName="id")}
* )
*/
private $groups;
//...
}
In test, assuming in this example $groups initialized as ArrayCollection() in constructor, returns all groups associated with the user.
A programmer in my dev team brought up a good point about this scenario. It loads all groups associated with a user entity, we may not need all of them.
When doing DQL join statements, lets say we want to select the favorite groups, achievable by DQL joins, we join user, group, and favorites. Does Doctrine queries all the groups again? This is the overhead I'm trying to point out, there's already an array collection of all the groups.
How do we control the result from association mapping? Or if we remove association mappings, can we still use DQL to join entities that don't have association mappings setup? OR, does Doctrine actually use the data from the association mapping in DQL?
I'm pretty new to Doctrine and wondering how to efficiently calculate the number of related objects there are for a particular model object.
I read here that it's not a great idea to use the entity manager within models so I'm wondering how I would query the database to find out without lazy loading all of the related models and doing a count().
I haven't really found a great answer yet, but it seems like this is a pretty fundamental thing?
For example
class House
{
/**
* #var Room
*/
protected $rooms
public function getRoomCount()
{
// Cant use entity manager here?
}
}
class Room
{
// Shed loads of stuff in here
}
Doctrine 2 will get counts for you automatically as association properties are actually Doctrine Collection objects:
public function getRoomCount()
{
return $this->rooms->count();
}
If you mark the association as eager, Doctrine will load the rooms whenever you query for house entities. If you mark them as lazy (the default), Doctrine won't load the rooms until you actually access the $this->rooms property.
As of Doctrine 2.1 you can mark associations as extra lazy. This means that calling $this->rooms->count() won't load the rooms, it will just issue a COUNT query to the database.
You can read about extra lazy collections here: http://www.doctrine-project.org/docs/orm/2.1/en/tutorials/extra-lazy-associations.html