在我的自定义PrestaShop 1.7模块中更新产品价格时,如何使Memcached值失效?

How to invalidate a Memcached value when updating a product's price in my custom PrestaShop 1.7 module?

提问人:Filip Albert 提问时间:10/10/2023 更新时间:10/30/2023 访问量:50

问:

我正在使用 Prestashop 1.7 多商店。其中一家商店启用了增值税,而另一家商店则没有。我创建了一个挂在钩子上的自定义模块。在本模块中,我更新了产品的价格,但仅适用于没有增值税的商店。这确保了两家商店的最终价格相同。updateproduct

为了提高性能,我使用了 Memcached。但是我很难正确地使缓存失效。一旦缓存了非增值税商店的值(我在模块中更改的值),在尝试在产品管理中再次设置它后,它永远不会更改。它在数据库中正确更改,但 Memcached 检索旧值。如何使 Memcached 中的存储值失效或更新?我也不知道如何获得正确的 Memcached 密钥,因为它是像 ps_288e7d9f7adabca7ca488166fced130a 这样的哈希值。

目前,我发现刷新 Memcached 的唯一方法是使整个缓存失效,就性能而言,这似乎是一个次优解决方案。

Cache::getInstance()->delete('*');

这是我简化的钩子函数来更新价格:

private function updateProductPrice($params)
    {
        $id_product = $params['id_product'];
        $price = $params['product']->price;
        $id_tax_rules_group = $params['product']->id_tax_rules_group;

        $shop_ids = $this->getShopIds();

        $tax_rate = $this->getTaxRate($id_tax_rules_group);

        foreach ($shop_ids as $shop_id) {
            $is_tax = Configuration::get('PS_TAX', null, null, $shop_id);
            $sql = 'SELECT `price`
                FROM `' . _DB_PREFIX_ . 'product`
                WHERE `id_product` = ' . (int) $id_product;
            $base_price = (float)Db::getInstance()->getValue($sql);
            
            if (!$is_tax) {
                $new_price = $base_price * (100 + $tax_rate) / 100;
                Db::getInstance()->update('product_shop', ['price' => $new_price], 'id_product = '.(int)$id_product . ' AND id_shop = '.(int)$shop_id);
            }
        }
    }

如果没有启用 Memcached,一切正常。

php prestashop memcached prestashop-1.7

评论

0赞 Markus Zeller 10/10/2023
Memcached 是一个键/值存储。您要使哪些内容无效?只需更换密钥并使用较短的过期时间即可。php.net/manual/de/memcached.set.php。要删除所有键,您需要使用flush。
0赞 Filip Albert 10/10/2023
我正在使用 Prestashop 的 CacheMemcached 核心类。我没有直接使用 Memcached。delete 方法实际上是调用 flush。我也不知道 Memcached 密钥,因为它由 Prestashop 处理。

答:

0赞 TheDrot 10/14/2023 #1

对象模型具有可在产品上使用的方法。clearCache()

调用更新查询后,使用以下代码

$params['product']->clearCache();

正如 OP 所评论的那样,上述内容不起作用,因为它仅清除 PHP 缓存。

尽管正确的方法是在产品对象上设置价格并调用其方法。直接修改数据库会跳过保存前后触发的所有钩子,并且根据您的问题,它也会跳过缓存失效。save()

上述操作也不起作用,因为 OP 正在更新产品钩子内更新产品。

在对核心进行一些挖掘后,缓存键似乎是根据 sql 查询计算的,因此您可以首先像在 Product 对象中一样构建价格查询。

构建前端价格查询:

$sql = new DbQuery();
$sql->select('product_shop.`price`, product_shop.`ecotax`');
$sql->from('product', 'p');
$sql->innerJoin('product_shop', 'product_shop', '(product_shop.id_product=p.id_product AND product_shop.id_shop = ' . (int) $id_shop . ')');
$sql->where('p.`id_product` = ' . (int) $id_product);
if (Combination::isFeatureActive()) {
    $sql->select('IFNULL(product_attribute_shop.id_product_attribute,0) id_product_attribute, product_attribute_shop.`price` AS attribute_price, product_attribute_shop.default_on, product_attribute_shop.`ecotax` AS attribute_ecotax');
    $sql->leftJoin('product_attribute_shop', 'product_attribute_shop', '(product_attribute_shop.id_product = p.id_product AND product_attribute_shop.id_shop = ' . (int) $id_shop . ')');
} else {
    $sql->select('0 as id_product_attribute');
}
$sql = $sql->build();

构建管理产品查询:

$entity_defs = ObjectModel::getDefinition(Product::class);
$sql = new DbQuery();
$sql->from($entity_defs['table'], 'a');
$sql->where('a.`' . bqSQL($entity_defs['primary']) . '` = ' . (int) $id);

// Get lang informations
if ($id_lang && isset($entity_defs['multilang']) && $entity_defs['multilang']) {
    $sql->leftJoin($entity_defs['table'] . '_lang', 'b', 'a.`' . bqSQL($entity_defs['primary']) . '` = b.`' . bqSQL($entity_defs['primary']) . '` AND b.`id_lang` = ' . (int) $id_lang);
    if ($id_shop && !empty($entity_defs['multilang_shop'])) {
        $sql->where('b.`id_shop` = ' . (int) $id_shop);
    }
}

// Get shop informations
if (Shop::isTableAssociated($entity_defs['table'])) {
    $sql->leftJoin($entity_defs['table'] . '_shop', 'c', 'a.`' . bqSQL($entity_defs['primary']) . '` = c.`' . bqSQL($entity_defs['primary']) . '` AND c.`id_shop` = ' . (int) $id_shop);
}
$adminSql = $sql->build();

从查询中计算缓存密钥。

$keyFrontend = Cache::getInstance()->getQueryHash($sql);
$keyAdmin = Cache::getInstance()->getQueryHash($adminSql);

删除密钥。

Cache::getInstance()->delete($keyFrontend);
Cache::getInstance()->delete($keyAdmin);

如果缓存处于活动状态,您应该首先进行一些检查,以便代码在有缓存和无缓存的情况下工作。

尽管所有这些都是/感觉很黑客,但我不知道这如何影响任何其他价格计算,例如特定价格、折扣等,因为他们的 sql 查询也可能需要缓存失效。我试图找出管理页面如何在保存时使产品缓存失效,以找到一种更简单的方法,但到目前为止还没有运气弄清楚。

评论

0赞 Filip Albert 10/28/2023
该方法仅适用于本地 PHP 缓存,而不适用于 Memcached。此外,我不能使用该方法,因为我已经在钩子中,这将创建一个无限循环。此外,我需要针对非增值税商店的特定shop_id,以便在增值税和非增值税商店中保持相同的价格。这与默认的 PrestaShop 行为背道而驰,该行为将增值税添加到基本价格中。这就是我跳过正确方式的原因。clearCache()save()updateproduct