Symfony 从外部实体存储库生成实体属性

Symfony 从外部实体存储库生成实体属性,symfony,doctrine-orm,repository,entity,Symfony,Doctrine Orm,Repository,Entity,我有一个用户实体,可以向他的客户收费。因此,用户可以创建发票实体。我想做的是,通过计算发票总额,轻松检索用户已收取的费用。通过将$user变量传递给汇总这些发票的方法,我可以在我的发票存储库中轻松做到这一点。但是我想把这个值存储在用户实体中,所以在我的小树枝模板中,我可以用类似这样的东西调用它:{{app.User.chargedAmount} 但据我所知,我无法在我的用户实体中创建getChargedAmount(),它将使用发票存储库并设置受保护的$chargedAmount属性 我应该如何

我有一个用户实体,可以向他的客户收费。因此,用户可以创建发票实体。我想做的是,通过计算发票总额,轻松检索用户已收取的费用。通过将$user变量传递给汇总这些发票的方法,我可以在我的发票存储库中轻松做到这一点。但是我想把这个值存储在用户实体中,所以在我的小树枝模板中,我可以用类似这样的东西调用它:{{app.User.chargedAmount}

但据我所知,我无法在我的用户实体中创建getChargedAmount(),它将使用发票存储库并设置受保护的$chargedAmount属性


我应该如何在我的用户实体中存储此自定义值?

在您的用户存储库中,您可以创建一个自定义查询(例如
findChargedAmount
)来检索已收费金额

public function findChargedAmount($user)
{
    $em = $this->getEntityManager();
    $query = $em->createQuery('SELECT SUM(i.total) AS amount FROM MyBundle:Invoice i WHERE i.user = :user');
    $query->setParameter('user', $user);
    $sum = $query->getResult();

    return $sum[0]['amount'];
}
假设发票实体作为用户的外键。您还可以使用一个


希望这会对您有所帮助。

有几种方法可以做到这一点,但这两种方法都不会涉及实体中的存储库。(老实说,可能还有很多方法可以做到这一点)

  • 假设您的用户模型和发票模型是使用
    oneToMany
    关联的,那么您的用户模型中可以有一个方法,该方法可以循环关联的发票,并动态获取金额。这意味着当前总数将不会持久化到数据库中

    $totalCharged = null;
    
    public function getTotalCharged()
    {
        if (null !== $totalCharged) {
            return $totalCharged;
        }
    
        $totalCharged = 0;
    
        foreach ($this->getInvoices() as $invoice) {
            $totalCharged += $invoice->getTotal();
        }
    
        return $totalCharged;
    }
    
    class AcmeExtension extends \Twig_Extension
    {
        protected $invoiceRepository;
    
        public function __construct(EntityRepository $invoiceRepository)
        {
            $this->invoiceRepository = $invoiceRepository;
        }
    
        public function getFunctions()
        {
            return array(
                new \Twig_SimpleFunction(
                    'acme_invoice_total_charged', 
                    array($this, 'getTotalChargedForUser')
                ),
            );
        }
    
        public function getTotalChargedForUser(UserInterface $user)
        {
            return $this->invoiceRepository->getTotalChargedForUser($user);
        }
    
        public function getName()
        {
            return 'acme_invoice_extension';
        }
    }
    
  • 您可以设置一个事件侦听器,该侦听器将在发票每次更改(创建、更新、删除)时侦听,然后使用最终结果更新您的用户模型。(假设您正在使用ORM)。这可能需要您更新控制器操作,以包括尚未使用的事件

    use Doctrine\ORM\EntityRepository;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\EventDispatcher\GenericEvent;
    
    class UserInvoiceTotalChargedSubscriber implements EventSubscriberInterface
    {
        protected $invoiceRepository;
    
        public function __construct(EntityRepository $invoiceRepository)
        {
            $this->invoiceRepository = $invoiceRepository;
        }
    
        public static function getSubscribedEvents()
        {
            return array(
                'acme.invoice.create' => 'updateTotalCharged',
                'acme.invoice.update' => 'updateTotalCharged',
                'acme.invoice.remove' => 'updateTotalCharged',
            );
        }
    
        public function updateTotalCharged(GenericEvent $event)
        {
            $invoice = $event->getSubject();
    
            if (!$invoice instanceof InvoiceInterface) {
            // Or Invoice if you are not using interfaces
                throw new \Exception(
                    sprintf('Invoice expected, %s provided', get_class($invoice)
                );
            }
    
            $user = $invoice->getUser();
    
            $totalCharged = $this->invoiceRepository
                ->getTotalChargedForUser($user);
    
            $user->setTotalCharged($totalCharged);
            // This could be moved to a separate class that performed the
            // calculation instead where you could call it like
            // $this->totalChargedCalculator->calculate($invoice->getUser());
            // Obviously this would need to be injected instead of the repository
        }
    }
    
  • 如果您只想在细枝模板中访问此属性,则可以创建自定义细枝扩展并在该模板中调用它。然后,您可以在模板中使用它,如
    {{acme\u invoice\u total\u charged(user)}}
    ,尽管这并不提供相同的可移植性。这(与选项1类似)意味着当前总数不会持久化到数据库中

    $totalCharged = null;
    
    public function getTotalCharged()
    {
        if (null !== $totalCharged) {
            return $totalCharged;
        }
    
        $totalCharged = 0;
    
        foreach ($this->getInvoices() as $invoice) {
            $totalCharged += $invoice->getTotal();
        }
    
        return $totalCharged;
    }
    
    class AcmeExtension extends \Twig_Extension
    {
        protected $invoiceRepository;
    
        public function __construct(EntityRepository $invoiceRepository)
        {
            $this->invoiceRepository = $invoiceRepository;
        }
    
        public function getFunctions()
        {
            return array(
                new \Twig_SimpleFunction(
                    'acme_invoice_total_charged', 
                    array($this, 'getTotalChargedForUser')
                ),
            );
        }
    
        public function getTotalChargedForUser(UserInterface $user)
        {
            return $this->invoiceRepository->getTotalChargedForUser($user);
        }
    
        public function getName()
        {
            return 'acme_invoice_extension';
        }
    }
    

  • 我想我会选择选项1。我不需要将此值存储在数据库中,因此选项2对我来说似乎有些过分。选项#3的可移植性不够,将来我可能需要在小枝模板之外的信息。我将在您的示例中添加一些测试#1认为,因为我只想对已付款发票求和(状态=3)。我唯一不喜欢您的解决方案的地方是我必须使用PHP(状态、年份等)应用我的过滤器,而不是在db查询中。看起来有点重。老实说,我会选择2号,但只有在您已经使用事件订阅服务器的情况下,它才有意义。总是可以选择使用条令事件订阅者来监听对实际实体的更新,而不是自定义事件。这正是我在原始帖子中提到的。问题是这个值不能存储在用户实体中(不一定存储在DB中,只存储在PHP对象中)。因此,每次我想使用它时,我都必须调用该方法,而不是只调用{{app.user.getChargedAmount}},并将其传递给我的模板。此外,我不能像Qoop的解决方案#1那样“缓存”它。