【问题标题】:Twig extension: database queryTwig 扩展:数据库查询
【发布时间】:2016-05-14 21:33:30
【问题描述】:

我需要做一个自定义过滤器,将价格转换为用户的货币偏好。 所以我每天都会更新一张表格,上面有最新的汇率。

我打算这样调用过滤器 {{ '200'|price }} 或 {{ '200'|price('USD') }}

过滤器会首先从 cookie 中获取用户偏好。

然后会根据币码查询最新汇率并换算实际价格。

我的问题是,是否可以在 twig 扩展中进行数据库查询(在此处过滤)。

我可能必须在一个页面中调用此过滤器 10 次。这是否意味着还有 10 个查询?

你有什么更好的建议?

【问题讨论】:

    标签: symfony twig-extension


    【解决方案1】:

    是的,如果你没有为Doctrine定义缓存配置,那就意味着你每次都会向数据库发送请求来获取数据。

    您可以选择查询汇率、自己存储在缓存中或使用 Doctrine 的缓存功能来完成。

    Symfony 2 cache Doctrine query results

    这两种方式都比每次都直接从数据库中查询数据要好。

    【讨论】:

    • 谢谢你,这是我需要听到的。
    • 在扩展中创建静态属性就足够了,以保持整个请求的汇率。
    • 嗨@malcolm,我完全同意静态属性是一个很好的解决方案。但是,正如您还指出的那样,它的生命周期只是一个请求,所以我认为除非您的应用程序中的汇率很重要并且需要实时更新(在这种情况下,您甚至可能需要一个比每次请求都更新更好的解决方案) ,我还是觉得把它们放入缓存是一个很好的解决方案。
    • @ThaiDuongTran 它取决于您使用的数据库,但问题不在于存储/检索数据,而是如何在 twig 中进行操作。想知道为什么它被接受。
    • @malcoml,问题不在于如何访问 twig 中的数据,当然当 Brieuc 在这里提到扩展时,他知道该怎么做。他只是在考虑它的效率,而且您的评论是关于使用 PHP 类的静态属性,我确信您使用的数据库类型不会影响 PHP 静态属性的生命周期。
    【解决方案2】:

    使用教义事件系统,在这种情况下postLoad 事件将是合适的。您可以将价格添加到实体中未映射的属性,然后在树枝中调用:{{ entity.prices.usd }}

    services:
        app.postload.listener:
            class: AppBundle\EventListener\PostLoadListener
            tags:
                - { name: doctrine.event_listener, event: postLoad }
    

    监听类:

    namespace AppBundle\EventListener;
    
    use Doctrine\ORM\Event\LifecycleEventArgs;
    use AppBundle\Entity\Product;
    
    
    
    class PostLoadListener
    {
    
    protected static $currencies = [];
    
    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
    
    
        $entityManager = $args->getEntityManager();
    
    
        if ($entity instanceof Product) {
            $price = $entity->getPrice();
            if (empty($this->currencies) {
                // get currencies from db and calculate prices
                $this->currencies = $entityManager->getConnection()->prepare('SELECT.....')->execute()->fetchAll();
            }
    
            // Calculate prices and make them as array: $prices = ['usd' => 34, 'eur` => 30 .....]
    
        $entity->setPrices($prices);
    }
    

    }

    在 Twig 中调用它们:

    {{ entity.prices[app.request.cookies.get('currency')] }}
    

    【讨论】:

    • 这也是个好主意。我可能无法以 entity.prices.usd 的方式访问该属性,因为我获得了像 {{ app.request.cookies.get('currency') }} 这样的货币。但无论如何,提及教义事件系统是个好主意。谢谢!
    • 您可以:{{ entity.prices[app.request.cookies.get('currency')] }}
    【解决方案3】:

    您可以创建类CurrencyConverter,它只会查询数据库一次,并假设在用户请求期间速率没有改变,使用它的结果。一些简单的服务(你可以用 DI 将它注入到你的 Twig 扩展中),比如

    class CurrencyConverter
    {
        /** @var EntityManager */
        private $em;
    
        private $rates = [];
    
        public function __construct(EntityManager $em)
        {
            $this->em = $em;
        }
    
        public function convert($amount, $currency)
        {
            return $amount * $this->getRate($currency);
        }
    
        private function getRate($currency)
        {
            if (!isset($this->rates[$currency])) {
                $repository = $this->em->getRepository('AppBundle:Rate');
                $this->rates[$currency] = $repository->getLatestRate($currency);
            }
    
            return $this->rates[$currency];
        }
    }
    

    这样,每种货币的每个请求都会有 1 个数据库查询。如果您没有很多用户,那可能就足够了。 MySQL 很可能会将整个汇率表加载到内存中,并且速度会非常快。

    Doctrine 缓存可能会加快速度,但任何缓存都会导致额外的问题和问题:

    • 查询结果应该在缓存中保存多长时间?
    • 是否需要在速率更新时手动清除缓存?
    • 我是从缓存还是数据库中获得结果?

    等等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-08
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2018-02-16
      • 1970-01-01
      • 2015-04-09
      相关资源
      最近更新 更多