3

I have on my Symfony 5 project, 2 entities : Client and Template. Each Client has one or more templates (OneToMany), each templates relates to only one Client (ManyToOne). I have a getTemplates() function in Client.php which returns a Collection of templates.

The problem I am getting is that when I call the getTemplates() function I run it from :

$client = $entityManager->getRepository(Client::class)->find($id);
$templates = $client->getTemplates();

I am getting an initialized = false Collection.

From what I understand, to avoid unnecessary requests, Symfony creates a Template proxy object which is null. To make this work, I can add fetch="EAGER"

@ORM\OneToMany(targetEntity=Template::class, mappedBy="Client", orphanRemoval=true, fetch="EAGER")

Adding this basically makes request the "real" Template object (more information here).

What I would like to do is make my getTemplates work without having to request all the templates when I retrieve all my clients like here (sometimes I only need to display all the clients, and request the templates from a certain client afterwards):

$templateRepository->findAll()

I know how to solve my problem, I just would like to have the best way to do it while having the smallest amount of requests to my database (basically only calling it when I need it).

To sum up, I would like to :

  • findAll() of my clients without retrieving the templates associated to the clients
  • findAll() of my templates and get the relating client - this currently works when I add a fetch="EAGER" since I get an "owner" which corresponds to the correct client
HectorB
  • 113
  • 6
  • to retrieve some clients with their templates in 1 request, you need to do a method in the client repository –  Oct 25 '21 at 09:48
  • How are you using `$templates = $client->getTemplates();` ? is your application a REST API, are you using your clients (with templates) inside twig ? – Dylan KAS Oct 25 '21 at 09:49
  • @DylanKas No it's not a REST API. What I am doing in basically getting on a page a list of all clients (with their names and other info - except their templates). And then getting all templates (here with their according Client) and also a page where I only show Templates from a certain client. – HectorB Oct 25 '21 at 09:52
  • Basically, to solve my problem I can add the fetch="EAGER" everywhere where I have a relation, for my application it's fine since the DB is not very big, however I am just curious on how I could optimize it. – HectorB Oct 25 '21 at 09:53
  • @yassinefikri so I would need a function to retrieve all all my clients and another one getting all my clients WITH the templates linked ? Since I am displaying all of this via twig, I want to get the templates "in" my Client object not separately. – HectorB Oct 25 '21 at 09:54
  • to retrieve clients without their templates you have findAll / find / findby ..., but to retrieve them with their templates in 1 request, yes you need to do a method in the repository where you select client joining the templates, then if you dump you will see that each client select you have it's collection template instead of the default behaviour –  Oct 25 '21 at 09:57
  • yes, because that's the default doctrine behaviour for performance, that's why you need do a custom method in the repository to retreive the relations from the database in 1 request –  Oct 25 '21 at 10:01
  • @yassinefikri Okay I understand ! So I need like a findAllWithTemplates() method in my repository. Thank you, seems in fact very logic ! – HectorB Oct 25 '21 at 10:03
  • exactly, and avoid changing the default doctrine behaviour because it will decrease the performance, because it will load all the enttites relations (even if you won't use them) in rendering, ... –  Oct 25 '21 at 10:05
  • A bit lost as to why the lazy proxy collection is an issue, are you not using `foreach($client->getTemplates() as $template)` or `{% for template in client.templates %}`? Which will query the templates on demand. Using `dump($client->getTemplates())` will always be empty, since you are not iterating over the collection to execute the query. – Will B. Oct 26 '21 at 01:48

1 Answers1

1

As Yassinefikri explained, the default Symfony behavior is not to request the linked Entities to avoid bad performance.

To solve the problem, you need a proper function in the repository where you will fetch the Clients and join them with their Templates.

This allows to fetch all the Clients without their Template but also be able to fetch all the Clients with their Templates, regarding of if you need it or not.

Changing the default behavior of Symfony by adding a fetch="EAGER" is not good practice, you should always create a function for a desired result instead of changing default behavior (which would decrease performance, specially if you are dealing with a big DB).

- EDIT, Better (and more optimized) way of solving the problem

As Will B said, the behavior comes from Doctrine ORM and not not Symfony (Symfony frequently is used with Doctrine ORM but this applies to ORM in general).

Another (and better) way of achieving the desired result is to initilialize the object (which will fetch linked entities without actually making JOIN requests to the DB - initializeObject()).

TO SUM UP

NEVER use fetch="EAGER" if you can since this is really the work way of dealing with linked entities, it is ay better to use Will B's or Yassinefikri's solution to the problem.

HectorB
  • 113
  • 6
  • Also note, this is not a Symfony behavior but rather the general usage of Doctrine ORM, which is typically recommended to install with Symfony. – Will B. Oct 26 '21 at 01:53
  • 1
    Another workaround instead of writing a custom query would be to force the collection to execute the query by using [initializeObject()](https://github.com/doctrine/persistence/blob/2.2.x/lib/Doctrine/Persistence/ObjectManager.php#L157). `$templates = $client->getTemplates(); $em->initializeObject($templates);`. Using a custom query will more than likely reduce permeance due to the JOIN and the hydrator execution. – Will B. Oct 26 '21 at 02:08
  • @WillB. okay well thank you, this really helped me ! – HectorB Oct 26 '21 at 09:17