首页 > 解决方案 > Symfony 学说不会滋润整个链条

问题描述

我在下面尽可能简化了我的 3 个实体,它显示了一个简单的关系Currency <- 1:1 -> Balance <- 1:N -> BalanceLog

实体/货币.php

/**
 * @ORM\Entity(repositoryClass=CurrencyRepository::class)
 */
class Currency
{
    /**
     * @ORM\Id
     * @ORM\Column(type="string", length=3)
     */
    private ?string $code;


    /**
     * @ORM\OneToOne(targetEntity="Balance", mappedBy="currency")
     **/
    private ?Balance $balance;

    // ...
}

实体/余额.php

/**
 * @ORM\Entity(repositoryClass=BalanceRepository::class)
 */
class Balance
{
    /**
     * @ORM\Id
     * @ORM\OneToOne(targetEntity="Currency", inversedBy="balance")
     * @ORM\JoinColumn(name="currency", referencedColumnName="code", nullable=false)
     **/
    private ?Currency $currency;
    
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\BalanceLog", mappedBy="balance")
     */
    private Collection $balance_logs;

    // ...
}

实体/BalanceLog.php

/**
 * @ORM\Entity(repositoryClass=BalanceLogRepository::class)
 */
class BalanceLog
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private ?int $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Balance", inversedBy="balance_logs")
     * @ORM\JoinColumn(name="balance_currency", referencedColumnName="currency")
     **/
    private ?Balance $balance;

    // ...

}

当我打电话时会出现问题:

$balanceLog = $this->getDoctrine()
            ->getRepository('App:BalanceLog')->findAll();

这将水合BalanceLog::$balance到类型的正确实例Balance,但它不会水合BalanceLog::$balance->currencyCurrency实例。相反,它只想string使用

导致错误:

类型化属性 App\Entity\Balance::$currency 必须是 App\Entity\Currency 的实例或 null,使用的字符串

肮脏的解决方法是使 Balance::$currency 没有固定类型的?Currency. 然后它将接受字符串和代码“有效”。但这是不正确的。Balance::$currency应该是类型,Currency有时不是字符串,有时是货币。

我试图在 中制作自己的方法BalanceLogRepository,无论出于何种原因,这都很好:

public function findByBalance(Balance $balance) : iterable
{

    $query = $this->createQueryBuilder('bl');

    $query->andWhere('bl.balance = :balance')
        ->setParameter('balance', $balance);

    return $query->getQuery()->getResult();
}

所以我更加困惑为什么默认findAll或者findBy不做递归水化

经过进一步调查,我发现了一个非常奇怪的行为:

如果我添加此代码:

$balance = $this->getDoctrine()->getRepository('App:Balance')->find('USD');

在...前面

$balanceLog = $this->getDoctrine()->getRepository('App:BalanceLog')->findAll();

在我的控制器中,错误消失了。就好像在我尝试先验地直接获取对象之前,没有正确加载具有依赖关系的App:BalanceORM 模式。BalanceBalance

标签: phpdoctrine-ormdoctrine

解决方案


我做了一些调试,它看起来BalanceLog并没有创建一个完整的Balance实体实例,而是一个代理。解决方案是向BalanceLog类添加急切加载

class BalanceLog
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private ?int $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Balance", inversedBy="balance_logs", fetch="EAGER")
     * @ORM\JoinColumn(name="balance_currency", referencedColumnName="currency")
     **/
    private ?Balance $balance;

    // ...

}

UnitOfWork.php然后不使用代理,而是将实体作为一个整体加载。

如果有人想知道为什么事先查询 Balance 可以使代码工作,那是因为 Doctrine 复杂的缓存机制。它保存Balance了主键的实例USD,然后在填充 BalanceLog 时,它使用此实例而不是创建代理。

我仍然认为 Proxy 不应该从 Entity 强制执行严格类型的属性,但这是 Doctrine 开发人员决定的事情。


推荐阅读