Doctrine Entity with fetch="EAGER" causing N+1 queries in dropdown - symfony

I have two entities, Client and Contact. There is a one-to-many relationship between them, Clients have many Contacts. The association is annotated with fetch="EAGER", and it's reasonable to say that Contact information will always be needed when viewing a Contact (not true in all cases, but a fair generalisation).
However, rather than just always ensuring that only one query is run by automatically joining to Contacts when viewing Clients, I'm observing some behaviour that seems strange to me, specifically when displaying a list of Clients in a Choice field on a form. Reviewing Doctrine log output, rather than doing a single query and using the results to display a list of Client names, instead a query gets all the Clients and then multiple queries get the data for all the Contacts, one per Contact. In this case the Contact data is not in fact needed, and I've successfully prevented the behaviour by simply removing `fetch="EAGER". This seems contrary to the desired behaviour of eager fetching, it's actually having the opposite effect.
When used to populate a Choice field, why is Contact data not being fetched along with the Client info using a single query as expected?
I've followed the code through the framework to watch the queries get fired one by one in the Doctrine hydration code, but this hasn't made it clear to me why it's happening. My pet theory involves something complicated to do with the Choice field "interrupting" what Doctrine would typically do and forcing it to fetch the additional data at a later stage than normal, but I've nothing much to back that up.
Client form field
$form->add(
'client',
'entity',
[
'class' => 'AppBundle:Client',
'choice_label' => 'name',
]
);
Client Contact assoc
/**
* #ORM\OneToMany(targetEntity="Contact", mappedBy="client", fetch="EAGER", cascade={"persist", "remove"})
* */
private $contacts;

Have you tried to add query_builder to your EntityType filed?
Example
$form
->add('client', EntityType::class, [
'class' => Client::class,
'choice_label' => 'name',
'query_builder' => function (EntityRepository $entityRepository) {
return $entityRepository->createQueryBuilder('this');
},
]);

Related

setQueryBuilder sort by name easy admin

I am developping a Symfony 5.4 application with easy admin (v3.5). I have an entity Article and an entity Category. I have a ManyToMany relationship between my two entities. In my easy admin Article crud controller, I can create new Article entities and add a Category to my entity. Everything works fine. Now what I would like to do is to sort the result given by the select tag for my Category association by name.
In my code i have tried this :
//App\Controller\Admin\ArticleCrudController.php
AssociationField::new('categories')->setQueryBuilder(
function (QueryBuilder $queryBuilder) {
return $queryBuilder->getEntityManager()->getRepository(Category::class)->findBy([], ['name' => 'ASC']);
}
)->setFormTypeOptions([
"by_reference" => false,
]),
based on easy admin association field documention.
When i dump my $queryBuilder line, i do have all my entites sorted by name. But when I open the select tag, they do not appear sorted. (I would expect "Espace métiers" to appear first, then "Gourvernance et pilotage" and so on).
I have noticed that the documentation I am using is for version 4.x and that mine is 3.5 but as I have a result when I dump I am still asking just in case.

Symfony - Discard association field during hydration process of an EntityType querybuilder

My Symfony (3.3) Form EntityType is displayed as a select input and lists all the clients we have in database. The client entity is associated to several other entities using lazy mode.
When the select box is rendered, 204 DB queries are issued. I suspect the form component to call setters against each query result, resulting in the loading of many additional database queries.
We could set association mapping as "EAGER" I guess, or use join('…')->addSelect('…') methods inside the form's querybuilder option to force the datas to be part of the results, but the hydration process still costly when several entities are involved.
As you can see, I tried to use the Doctrine Query HINT, hoping it would solve the problem but It did not change anything.
Then, what is the way to go for such a use case ?
What should I do in order to only get the fields I need to populate the dropdown input ?
Here is what I tried so far:
$builder->add('parent', EntityType::class, [
'class' => Client::class,
,'required' => false
,'multiple' => false
,'query_builder' => function (EntityRepository $er) {
$qb = $er ->createQueryBuilder('c')
// All I want doctrine to fetch are the following fields
->select('PARTIAL c.{id,uuid,name,shortName}');
// I expected this flag to help but it does not change the total amount of queries executed
$qb->getQuery()->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
return $qb;
}
])…
Thank you.
Solved
One of the association is a oneToOne and is the only one which has its mapping "fetch" key set to 'EAGER'.
I expected Doctrine to automatically join and select such an association when using the default EntityType's QueryBuilder, but it does not and I had to explicitly tell the querybuilder to do so (once again, despite the fetch flag set to 'EAGER').
return $qb->select('c, p')->leftJoin('c.param', 'p');
I don't really get the point here on what's going on underneath, still the number of database requests dropped down back to 4 queries.

symfony2 relation between two entities

i am started to learn symfony2, Here i have some basic doubts on entity relations. Totally i have two entities 1.Admission.php and 2.Mstcity.php , just i wanna make relation between these two entities.
mysql Table structure:1.admission= id, name , mst_city_id 2. mst_city = id,city_name .. just i am having simple admission form. in that form i need to load the city_name select box . the relation id is in admission table mst_city_id is foreign key of mst_city table .
admission.mst_city_id=mst_city.id ...... > need city_name by this matching
just help me to understand this process
There's no point for me to repost here the symfony docs, so go ahead and read them HERE There are examples in the docs that show exactly what you want to do.
Assuming you have proper Associations set in place, you can just add the field with entity as widget type
$builder->add('mst_city_id', 'entity', array(
'class' => 'BundleName:mst_city',
'property' => 'city_name',
));
You should check which type of relations you use -One2One, One2Many, Many2Many
Define relations in mappers/annotations and check for owned side
Create form types and bind data from it to entities
Persist and flush entities

Add a search form in a manyToMany Relation interface

How to implement a search form in a many to many relation between entities.
I want to search items from an entity before to add them to my other entity. I am using a long list of items (product) that i need to link to Shops and i can't use a simple listbox to select my items.
I need you to point me to a tutorial or any explaination to deal with this interface problem.
The goal is to use a minimum of javascript
I would suggest to create an view where you could select a category or define your search condition. And a second View where you only display products by the previously selected condition. In your second view you could use an entity Field Type ( http://symfony.com/doc/current/reference/forms/types/entity.html#query-builder ) and provide a custom query for the entities like:
use Doctrine\ORM\EntityRepository;
// ...
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:Product',
'query_builder' => function(ProductRepository $er) {
return $er->createQueryBuilder('p')
->where('p.category = 1);
},
));
This solution doesn't require JavaScript at all.
I spent lot's of time trying to figure out the best solution to make the compromise between re-usability, performance and ergonomy and i found a nice solution
I did this way :
I created a custom form field that show a collection like entity field type but i pass field names that I want to show in a nice table :
->add('products','reflist',array(
'columns'=>array('name','cost','description'),
'actions'=>array('select'=>true,'remove'=>true),
'entityName'=>'VendorProductBundle:Product',
'searchForm'=> 'Vendor\ProductBundle\Form\ProductSearchType'
));
Then I created a generic searching service that takes in input the entity to search on. The result is sent in a popup paginated.
Finally, I created a controller related to my new field to manage actions like add, remove
Thats's it for the logic.
I can't really share the work since it is really dependent of my framework (depend of search service, layout,etc...)

Combining databases in Drupal

So here's the scenario:
I am building a series of event websites that need to be separate Drupal-6 installations on the same server. Each uses the same architecture but different URLs and themes.
What I'd like the sites to be able to share is content in 2 content types, teachers and sponsors. So that there would be one database with all the teachers and sponsors that each individual event site could then pull from by means of nodequeues.
So that new teacher nodes and sponsor nodes can be created and edited from within any of the Drupal installations.
It would also be convenient to share a user table as well, but not absolutely necessary.
Any ideas?
Does this help you?
The comments in settings.txt explain
how to use $db_prefix to share tables:
* To provide prefixes for specific tables, set $db_prefix as an array.
* The array's keys are the table names and the values are the prefixes.
* The 'default' element holds the prefix for any tables not specified
* elsewhere in the array. Example:
*
* $db_prefix = array(
* 'default' => 'main_',
* 'users' => 'shared_',
* 'sessions' => 'shared_',
* 'role' => 'shared_',
* 'authmap' => 'shared_',
* );
Another way, although I don't know if that would work, is to create one database per Drupal installation and create in each database a VIEW eg. install1_users, install2_users that refers to one shared table shared_users in a shared database. A carefully constructed view should be updatable just like a normal table.
Sounds like a job for the Domain Access project, a suite of modules that provide tools for running a group of affiliated sites from one Drupal installation and a single shared database. The module allows you to share users, content, and configurations across a number of sites. By default, these sites share all tables in your Drupal installation but the Domain Prefix module allows for selective, dynamic table prefixing for advanced users.
IMHO this is much better than rolling a custom solution which poses considerable complexity and risk of not being able to upgrade your site. See Share tables across instances (not recommended).
There are a couple of ways of doing this.
If it's okay to share a single database, you can use prefixing as follows:
$db_prefix = array(
'default' => 'main_',
'users' => 'shared_',
'sessions' => 'shared_',
'role' => 'shared_',
'authmap' => 'shared_',
);
However, keep in mind that there is a hard limit to the number of tables that a MySQL database can hold. According to this thread, it's 1792. If you suspect that you will reach this limit, you can use this hack/bug.
$db_prefix = array(
'default' => '',
'users' => 'maindb.',
'sessions' => 'maindb.',
'role' => 'maindb.',
'authmap' => 'maindb.',
);
where maindb is another shared database that contains the data that you need. This is not best practice, but it works in Drupal 6 (haven't tested in D7).

Resources