Symfony 4:调用 getter 后,Doctrine2 LAZY Fetch Collection 仍然为空

Posted

技术标签:

【中文标题】Symfony 4:调用 getter 后,Doctrine2 LAZY Fetch Collection 仍然为空【英文标题】:Symfony 4 : Doctrine2 LAZY Fetch Collection still empty after calling the getter 【发布时间】:2020-06-04 15:20:47 【问题描述】:

我对某些实体有疑问:根据the doctrine documentation,延迟加载应该只允许我在调用 getter 时加载我的集合,但它没有。

这是关系描述:

一个位置有许多场地,由位置“externalId”字段映射。

这是我的实体及其关系的简化代码示例:

// Location.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="App\Repository\LocationRepository")
 * @ORM\Table(name="Location")
 */
class Location extends Entity

    /**
     * @var integer
     * @ORM\Column(type="integer", unique=true, nullable=true)
     */
    protected $locationId;

    /**
     * @var integer
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer", nullable=false)
     */
    protected $externalId;

    /**
     * @var Venue[]|Collection
     * @ORM\OneToMany(targetEntity="App\Entity\Venue", mappedBy="locationExternalId", orphanRemoval=true, fetch="LAZY")
     */
    protected $venues;

    /**
     * Location constructor.
     * @param array $data
     */
    public function __construct(array $data)
    
        $this->venues = new ArrayCollection;
        $this->hydrate($data);
    

    /**
     * @return int
     */
    public function getLocationId()
    
        return $this->locationId;
    

    /**
     * @param int $locationId
     * @return $this
     */
    public function setLocationId($locationId)
    
        $this->locationId = $locationId;
        return $this;
    

    /**
     * @return Venue[]|Collection
     */
    public function getVenues()
    
        return $this->venues;
    

    /**
     * @param Venue[]|Collection $venues
     * @return $this
     */
    public function setVenues($venues)
    
        $this->venues = $venues;
        return $this;
    


// Venue.php
namespace App\Entity;

use App\Entity\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="App\Repository\VenueRepository")
 * @ORM\Table(name="Venue", indexes=@ORM\Index(name="IDX_Venue", columns="location_id"))
 */
class Venue extends Entity

    /**
     * @var integer
     * @ORM\Column(type="integer", unique=true, nullable=true)
     */
    protected $venueId;

    /**
     * @var integer
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer", nullable=false)
     */
    protected $externalId;

    /**
     * @var Location
     * @ORM\ManyToOne(targetEntity="App\Entity\Location", inversedBy="venues")
     * @ORM\JoinColumn(name="location_external_id", referencedColumnName="external_id", nullable=true)
     */
    protected $locationExternalId;

    /**
     * Venue constructor.
     * @param array $data
     */
    public function __construct(array $data)
    
        $this->hydrate($data);
    

    /**
     * @return int
     */
    public function getVenueId()
    
        return $this->venueId;
    

    /**
     * @param int $venueId
     * @return $this
     */
    public function setVenueId($venueId)
    
        $this->venueId = $venueId;
        return $this;
    

    /**
     * @return int
     */
    public function getExternalId()
    
        return $this->externalId;
    

    /**
     * @param int $externalId
     * @return $this
     */
    public function setExternalId($externalId)
    
        $this->externalId = (int)$externalId;
        return $this;
    

    /**
     * @return Location
     */
    public function getLocationExternalId()
    
        return $this->locationExternalId;
    

    /**
     * @param Location $locationExternalId
     * @return $this
     */
    public function setLocationExternalId($locationExternalId)
    
        $this->locationExternalId = $locationExternalId;
        return $this;
    

如果我加载一个位置,场地没有初始化,这是预期的:

// Controller.php
use App\Entity\Basketball\Location;
use App\Entity\Basketball\Venue;

$location = $this->getDoctrine()->getRepository(Location::class)->findOneBy(['externalId' => 1]);
dump($location);

// Dump results
App\Entity\Location #1438 ▼   #locationId: 1644  
#externalId: 1
#venues: Doctrine\ORM\PersistentCollection #1450 ▼
    -snapshot: []
    -owner: App\Entity\Location #1438
    -association: array:15 [ …15]
    -em: Doctrine\ORM\EntityManager #75 …11
    -backRefFieldName: "locationExternalId"
    -typeClass: Doctrine\ORM\Mapping\ClassMetadata #1441 …
    -isDirty: false
    #collection: Doctrine\Common\Collections\ArrayCollection #1700 ▶
    #initialized: false

但它应该通过调用 getter,它没有按预期工作

// Controller.php
dump($location->getVenues());

// Dump results
Doctrine\ORM\PersistentCollection #1450 ▼
  -snapshot: []
  -owner: App\Entity\Location #1438 ▶
  -association: array:15 [ …15]
  -em: Doctrine\ORM\EntityManager #75 …11
  -backRefFieldName: "locationExternalId"
  -typeClass: Doctrine\ORM\Mapping\ClassMetadata #1441 …
  -isDirty: false
  #collection: Doctrine\Common\Collections\ArrayCollection #1700 ▼
    -elements: []
  
  #initialized: false

环境

Symfony v4.4.7 Doctrine v2.7 ("doctrine/doctrine-fixtures-bundle": "^3.3") 通过指定“fetch=EAGER”,它可以工作,但我不想...

有什么想法吗?我应该怎么做才能初始化我的收藏?

编辑

感谢this Github issue 我找到了一种解决方法,方法是在 getter 中初始化集合,但仍然如此;应该有一种方法可以自动完成:

public function getVenues()

    $this->venues->initialize();
    return $this->venues;

【问题讨论】:

【参考方案1】:

真的需要你吗?要初始化集合,您需要调用此集合的某些方法。如果您使用JMSSerializerSymfony Serializer,您可以只返回序列化的数据,序列化程序会为您完成所有工作,因为它们调用toArray 方法来初始化您的集合。

【讨论】:

嗯,非常感谢,你是对的。文档不清楚,但“首次访问”意味着调用集合的方法。我不使用 JMSSerializer 或 Symfony Serializer,但我可以手动调用 toArray 方法,它工作正常:$location->getVenues()->toArray();

以上是关于Symfony 4:调用 getter 后,Doctrine2 LAZY Fetch Collection 仍然为空的主要内容,如果未能解决你的问题,请参考以下文章

如何防止不需要的学说在symfony 4中持续存在?

Symfony、nelmio/api-doc-bundle 和 @SWG\SecurityScheme

Nelmio API 文档区域和带有 symfony 4 的不记名令牌

使用 Symfony 2 CLI 工具,如何为子类生成具有正确类型提示的 getter 和 setter?

如何在 Symfony 中调用其他服务?

Symfony Upload doc, docx file using sfValidatorFile Mime type issue