A
A
Alexander Popov2020-03-10 10:12:45
symfony
Alexander Popov, 2020-03-10 10:12:45

How to disable lazy initialization of entities in Doctrine for the current request?

Good day to all.

Today I encountered the following very strange problem.

What is available:

PHP on Windows 7 x64, version 5.3.17, Apache version 2.4 (at work 2.2), PHP is included as a module. Symfony version 2.0.16, Doctrine version 2.1.
Three tables were created for the test - а, b, and a_b, storing many-to-many relationships. Test entities Doctrine MyUsers and MyComments were created, binding was made through metadata to these tables. At this stage, everything is fine. Here is the code that is contained in the entity classes:

<?php

namespace TTCom\BillingBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * TTCom\BillingBundle\Entity\MyUsers
 *
 * @ORM\Table(name="a")
 * @ORM\Entity
 */
class MyUsers
{
    /**
     *  @ORM\Id
     *  @ORM\GeneratedValue
     *  @ORM\Column(name="id", type="integer", nullable=false)
     */
    private $id;
    
    /**
     * @var string $name
     *
     * @ORM\Column(name="name", type="varchar(255)", nullable=false)
     */
    private $name;

    /**
     * Bidirectional - Many users have Many favorite comments (OWNING SIDE)
     *
     * @ORM\ManyToMany(targetEntity="MyComments", inversedBy="userFavorites")
     * @ORM\JoinTable(name="a_b",
     *   joinColumns={@ORM\JoinColumn(name="a_id", referencedColumnName="id")},
     *   inverseJoinColumns={@ORM\JoinColumn(name="b_id", referencedColumnName="id")}
     * )
     */
    private $favorites;
    
    
    public function getName() {
    return $this->name;
    }
    
    public function getFavoriteComments() {
        return $this->favorites;
    }
    
    public function addFavorite(Comment $comment) {
        $this->favorites->add($comment);
        $comment->addUserFavorite($this);
    }

    public function removeFavorite(Comment $comment) {
        $this->favorites->removeElement($comment);
        $comment->removeUserFavorite($this);
    }

}

<?php

namespace TTCom\BillingBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * TTCom\BillingBundle\Entity\MyComments
 *
 * @ORM\Table(name="b")
 * @ORM\Entity
 */
class MyComments
{
    /**
     *  @ORM\Id
     *  @ORM\GeneratedValue
     *  @ORM\Column(name="id", type="integer", nullable=false)
     */
    private $id;
    
    /**
     * @var string $text
     *
     * @ORM\Column(name="text", type="varchar(255)", nullable=false)
     */
    private $text;

    /**
     * Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
     *
     * @ORM\ManyToMany(targetEntity="MyUsers", mappedBy="favorites")
     */
    private $userFavorites;
    
    
    public function getCommentText() {
        return $this->text;
    }
    
    public function getUserFavorites() {
        return $this->userFavorites;
    }

}


Here is the code I wrote for a test in one of the controllers:

public function testAction(Request $request) {
    $em = $this->getDoctrine()->getEntityManager();
    $user = $em->getRepository('TTComBillingBundle:MyUsers')->findOneById(1);
    $fav = $user->getFavoriteComments();
    //return new Response(print_r(\Doctrine\Common\Util\Debug::dump($fav[0]), true)); //1
    //return new Response(count($fav)); //2
    $a = array();
    //count($fav); //3
    //for ($i = 0; $i < count($fav); $i++) {
      //print_r(\Doctrine\Common\Util\Debug::dump($fav[$i])); //4
      //$a[] = $fav[$i]->getCommentText(); //5
    //}
    //array_walk($fav, function($el, $i) use ($a, $fav) { print_r(\Doctrine\Common\Util\Debug::dump($el)); $a[] = $fav[$i]->getCommentText(); }); //6
    print_r($a);
    //return new Response(print_r($f[0], true)); //7
    //return new Response(print_r(\Doctrine\Common\Util\Debug::dump($user->getFavoriteComments()), true)); //8
    return new Response($user->getName().": ".implode(", ", $a)); //9
    $objects = $em->getRepository('TTComBillingBundle:AlarmObject')->getList();
    $events = $em->getRepository('TTComBillingBundle:AlarmLogEntry')->getList();
    return $this->render('TTComBillingBundle:Default:alarm.html.twig',array('objects' => $objects, 'events' => $events));
  }


What is happening in my code:

  • An array of Doctrine objects with a bunch of internal data is obtained absolutely correctly in the line with comment 1
  • Its length is displayed correctly in the comment line 2
  • If now I uncomment the arrray_walk call on line 6, I get array(0) {}, as well as an error in the log like
    PHP Fatal error:  Call to a member function getCommentText() on a non-object

  • But it is worth uncommenting line 3 with the count() call - and everything starts working fine
  • If you use iteration through the for loop instead of array_walk, everything works correctly
  • If you use not an element in the callback function, but its index, then everything is fine too


"Normal" means output
array(2) {
  [0]=>
  object(stdClass)#548 (4) {
    ["__CLASS__"]=>
    string(37) "TTCom\BillingBundle\Entity\MyComments"
    ["id"]=>
    int(1)
    ["text"]=>
    string(2) "b1"
    ["userFavorites"]=>
    string(8) "Array(2)"
  }
  [1]=>
  object(stdClass)#540 (4) {
    ["__CLASS__"]=>
    string(37) "TTCom\BillingBundle\Entity\MyComments"
    ["id"]=>
    int(2)
    ["text"]=>
    string(2) "b2"
    ["userFavorites"]=>
    string(8) "Array(1)"
  }
}

as a result of a call print_r(\Doctrine\Common\Util\Debug::dump($el));inside the callback function, as well as a non-empty array $a containing two elements: "b1" and "b2".

I so understand, a problem in lazy initialization of objects. Is it possible to turn it off somehow? Or is using array_walk and array_map with arrays of Doctrine objects bad form and should be avoided? Because calling count($fav) or idly running through for before using array_walk/array_filter/array_map for the first time looks like a very dirty hack.

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question