0

Why are Proxy classes created instead of entity objects?

DoctrineORMModule\Proxy\__CG__\App\Entity\FormType vs \App\Entity\FormType

Setup: Laminas, Doctrine ORM

    Class Project{}
    
    Class ProjectForm
    {
    /**
         * @ORM\Id
         * @ORM\Column(name="id")
         * @ORM\GeneratedValue
         */
        protected $id;
    
        /**
         * @ORM\OneToOne(targetEntity="FormType")
         * @ORM\JoinColumn(name="form_type_id", referencedColumnName="id")
         */
        protected $formType;
        
        /** 
         * @ORM\OneToOne(targetEntity="Project")
         * @ORM\JoinColumn(name="project_id", referencedColumnName="id")
         */
        protected $project;
    
        /** 
         * @ORM\Column(name="label")  
         */
        protected $label;
    
        /**
         * @ORM\Column(name="status")  
         */
        protected $status;
           
        /**
         * @ORM\Column(name="order_nr")
         */
        protected $order;
    }    
Class FormType
{/**
     * @ORM\Id
     * @ORM\Column(name="id")
     * @ORM\GeneratedValue
     */
    protected $id;
    
    /**
     * @ORM\Column(name="code")
     */
    protected $code;
    
    /**
     * @ORM\Column(name="label_txt")
     */
    protected $label;
:
:
}

Then, using a custom extended Repository use the query builder to get data from storage

 $qb->select('pc','ft')
        ->from(\App\Entity\ProjectForm::class,'pc')
        ->join(\App\Entity\FormType::class,'ft')   
        ->where($qb->expr()->eq('pc.project',$projectId))
        ->andWhere('pc.formType=ft.id');

SQL Generated is/seems correct (when passing project ID 1) and returns the correct number of rows

SELECT p0_.id AS id_0, p0_.label AS label_1, p0_.status AS status_2, p0_.order_nr AS order_nr_3, f1_.id AS id_4, f1_.code AS code_5, f1_.label_txt AS label_txt_6, p0_.form_type_id AS form_type_id_7, p0_.project_id AS project_id_8 FROM project_forms p0_ INNER JOIN form_types f1_ WHERE p0_.project_id = 1 AND p0_.form_type_id = f1_.id

The same resultset is visible via

$this->getEntityManager()->getRepository(\Eho\Core\Entity\ProjectForm::class)->findBy(['project'=>$projectId],[]);

With $result[0]->getProject()->getTitle() returning the correct string but $result[0]->getFormType()->getName() throwing and error due to class \DoctrineORMModule\Proxy\__CG__\Entity\FormType being returned from getFormType()...???

Project::getTite() -> string
ProjectForm->getName() -> string

EDIT: Also tried different join for giggles

$joinOn = \Doctrine\ORM\Query\Expr\Join::ON;
        $joinWith = \Doctrine\ORM\Query\Expr\Join::WITH;
        $qb->select('pf','ft')
        ->from(\App\Entity\ProjectForm::class,'pf')
        ->innerJoin(\App\Entity\FormType::class,'ft',$joinWith, 'pf.formType=ft.id')
        ->where($qb->expr()->eq('pf.project',$projectId));

With SQL generated:

SELECT ...fields... FROM project_forms p0_ INNER JOIN form_types f1_ ON (p0_.form_type_id = f1_.id) WHERE p0_.project_id = 1

But the same resultsets (with Proxy classes are returned)...

Why is ONE entity (project) populated correctly and another (formType) as a proxy...?

JI-Web
  • 481
  • 6
  • 27
  • I think I've found a 'workaround' but it does not explain why the proxy is created. Using the last $qb method (innerJoin) I swap the from() and innerJoin() entities and it works....? Why? It seems that one has to 'select' from the referenced/associated entity. Why was projects fine then...? Any explainations/insights much appreciated – JI-Web May 20 '21 at 07:24
  • Does this answer your question? [What is a Proxy in Doctrine 2?](https://stackoverflow.com/questions/4923817/what-is-a-proxy-in-doctrine-2) – Ermenegildo May 20 '21 at 07:59
  • Proxies are used to support lazy loading of associated entities. The Doctrine manual has a section which discusses this. Load all your entities via a custom query and no proxies will be used. In theory it should not make any difference to the rest of your code. – Cerad May 20 '21 at 12:14
  • Thanks @cerad So is the Query Builder not a 'customer query'? Also, the 'What is a proxy' link I've read but still not clear why Lazy loading is used IF I've joined the tables to populate the correct entity... I.e. I understand that you can use $e1 = $em->find() to get the base entity, then if that has associations ($e1->getProject() ) will be affected by EAGER vs LAZY loading. BUT I've already requested the data I need via a join. No lazy or eager needed... – JI-Web May 21 '21 at 05:28
  • Also, does not address why 1 associated entity (Project) was populated without a proxy and another (FormType) was not and had a proxy. – JI-Web May 21 '21 at 05:30
  • Too much information! First off it does not matter if a proxy is being returned or not. So if the method FormType::getName() exists then the method can be used with either the original class or a proxy. If you take a peak under the var directory where the proxy classes live then you will see that all the methods are duplicated. In your original query, ->andWhere('pc.formType=ft.id'); is not needed. Doctrine takes care of doing the actual join. So that could be the problem right there. I also tend to use leftJoin instead of join. – Cerad May 21 '21 at 11:32
  • I can see mine is a replication of https://stackoverflow.com/questions/28925792/doctrines-querybuilder-returns-unexpected-proxies-cg-objects . The problem is this: I create a query and want to loop over the resultset (foreach...) an since the proxies are in the resultset then you have to add code to 'skip' them... Maybe I'm just doing 'it' wrong... – JI-Web May 28 '21 at 02:19

0 Answers0