From 665e37b797f439c171a8b7d39c302f03b0324cb2 Mon Sep 17 00:00:00 2001 From: Romain Ruaud Date: Wed, 12 Sep 2018 15:45:21 +0200 Subject: [PATCH] Implement IdentityInterface for optimizers to ensure proper cache cleaning of related entities on save. --- .../Model/Optimizer.php | 39 +++- .../Model/Optimizer/Limitation/Identities.php | 190 ++++++++++++++++++ 2 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 src/module-elasticsuite-catalog-optimizer/Model/Optimizer/Limitation/Identities.php diff --git a/src/module-elasticsuite-catalog-optimizer/Model/Optimizer.php b/src/module-elasticsuite-catalog-optimizer/Model/Optimizer.php index 92d2d3dfa..044048fea 100644 --- a/src/module-elasticsuite-catalog-optimizer/Model/Optimizer.php +++ b/src/module-elasticsuite-catalog-optimizer/Model/Optimizer.php @@ -12,6 +12,7 @@ */ namespace Smile\ElasticsuiteCatalogOptimizer\Model; +use Magento\Framework\DataObject\IdentityInterface; use Smile\ElasticsuiteCatalogOptimizer\Api\Data\OptimizerInterface; /** @@ -23,7 +24,7 @@ * @package Smile\ElasticsuiteCatalogOptimizer * @author Fanny DECLERCK */ -class Optimizer extends \Magento\Framework\Model\AbstractModel implements OptimizerInterface +class Optimizer extends \Magento\Framework\Model\AbstractModel implements OptimizerInterface, IdentityInterface { /** * @var string @@ -40,6 +41,11 @@ class Optimizer extends \Magento\Framework\Model\AbstractModel implements Optimi */ private $dateFilter; + /** + * @var \Smile\ElasticsuiteCatalogOptimizer\Model\Optimizer\Limitation\Identities + */ + private $limitationIdentities; + /** * @var string */ @@ -48,26 +54,29 @@ class Optimizer extends \Magento\Framework\Model\AbstractModel implements Optimi /** * Class constructor * - * @param \Magento\Framework\Model\Context $context Context. - * @param \Magento\Framework\Registry $registry Registry. - * @param \Smile\ElasticsuiteCatalogRule\Model\RuleFactory $ruleFactory Rule factory. - * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter Date Filter. - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource Resource. - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection Resource collection. - * @param array $data Data. + * @param \Magento\Framework\Model\Context $context Context. + * @param \Magento\Framework\Registry $registry Registry. + * @param \Smile\ElasticsuiteCatalogRule\Model\RuleFactory $ruleFactory Rule factory. + * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter Date Filter. + * @param \Smile\ElasticsuiteCatalogOptimizer\Model\Optimizer\Limitation\Identities $limitationIdentities Limitation Identities. + * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource Resource. + * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection Resource collection. + * @param array $data Data. */ public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, \Smile\ElasticsuiteCatalogRule\Model\RuleFactory $ruleFactory, \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter, + \Smile\ElasticsuiteCatalogOptimizer\Model\Optimizer\Limitation\Identities $limitationIdentities, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [] ) { parent::__construct($context, $registry, $resource, $resourceCollection, $data); - $this->ruleFactory = $ruleFactory; - $this->dateFilter = $dateFilter; + $this->ruleFactory = $ruleFactory; + $this->dateFilter = $dateFilter; + $this->limitationIdentities = $limitationIdentities; } /** @@ -296,6 +305,16 @@ public function validateData(\Magento\Framework\DataObject $dataObject) return !empty($result) ? $result : true; } + /** + * {@inheritdoc} + */ + public function getIdentities() + { + $identities = array_merge($this->getCacheTags(), $this->limitationIdentities->get($this)); + + return $identities; + } + /** * @SuppressWarnings(PHPMD.CamelCaseMethodName) * diff --git a/src/module-elasticsuite-catalog-optimizer/Model/Optimizer/Limitation/Identities.php b/src/module-elasticsuite-catalog-optimizer/Model/Optimizer/Limitation/Identities.php new file mode 100644 index 000000000..e8430248f --- /dev/null +++ b/src/module-elasticsuite-catalog-optimizer/Model/Optimizer/Limitation/Identities.php @@ -0,0 +1,190 @@ + + * @copyright 2018 Smile + * @license Open Software License ("OSL") v. 3.0 + */ +namespace Smile\ElasticsuiteCatalogOptimizer\Model\Optimizer\Limitation; + +use Magento\Search\Model\PopularSearchTerms; +use Smile\ElasticsuiteCatalogOptimizer\Api\Data\OptimizerInterface; + +/** + * Identities Provider for optimizer limitations. + * + * @category Smile + * @package Smile\ElasticsuiteCatalogOptimizer + * @author Romain Ruaud + */ +class Identities +{ + /** + * Scope configuration + * + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Catalog search data + * + * @var \Magento\Search\Model\ResourceModel\Query\Collection + */ + private $queryCollection; + + /** + * Limitation Identities Constructor. + * + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig Scope Config + * @param \Magento\Search\Model\ResourceModel\Query\Collection $queryCollection Search Queries Collection + */ + public function __construct( + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Search\Model\ResourceModel\Query\Collection $queryCollection + ) { + $this->scopeConfig = $scopeConfig; + $this->queryCollection = $queryCollection; + } + + /** + * Get Limitation identities for a given optimizer. + * + * @param \Smile\ElasticsuiteCatalogOptimizer\Api\Data\OptimizerInterface $optimizer The optimizer + * + * @return array + */ + public function get(OptimizerInterface $optimizer) + { + $identities = []; + $origData = $optimizer->getOrigData(); + $containers = array_unique( + array_keys(array_merge($optimizer->getSearchContainers(), $origData['search_containers'] ?? [])) + ); + + if (in_array('quick_search_container', $containers)) { + $identities = array_merge($identities, $this->getSearchQueryIdentities($optimizer)); + } + + if (in_array('catalog_view_container', $containers)) { + $identities = array_merge($identities, $this->getCategoryIdentities($optimizer)); + } + + return $identities; + } + + /** + * Get search queries identities related to an optimizer. + * + * @param \Smile\ElasticsuiteCatalogOptimizer\Api\Data\OptimizerInterface $optimizer The optimizer + * + * @return array + */ + private function getSearchQueryIdentities(OptimizerInterface $optimizer) + { + $identities = []; + $queryIds = []; + $origData = $optimizer->getOrigData(); + $data = $optimizer->getData(); + + // If optimizer was previously assigned to all queries, or is now set to all queries. + $isAppliedToAllQueries = empty($data['quick_search_container']) + || (bool) $data['quick_search_container']['apply_to'] === false; + $wasAppliedToAllQueries = empty($origData['quick_search_container']['query_ids']); + + if (!empty($origData['quick_search_container']['query_ids'])) { + $queryIds = array_merge($queryIds, $origData['quick_search_container']['query_ids']); + } + + if (!empty($data['quick_search_container']['query_ids'])) { + foreach ($data['quick_search_container']['query_ids'] as $query) { + $queryIds[] = $query['id']; + } + } + + $queryIds = array_unique(array_filter($queryIds)); + + if (empty($queryIds) || $wasAppliedToAllQueries || $isAppliedToAllQueries) { + $identities[] = \Smile\ElasticsuiteCatalog\Block\CatalogSearch\Result\Cache::POPULAR_SEARCH_CACHE_TAG; + } + + if (!empty($queryIds)) { + $popularQueryIds = $this->queryCollection + ->setPopularQueryFilter($optimizer->getStoreId()) + ->setPageSize($this->getMaxCountCacheableSearchTerms($optimizer->getStoreId())) + ->load() + ->getColumnValues('query_id'); + + if (!empty(array_intersect($queryIds, $popularQueryIds))) { + $identities[] = \Smile\ElasticsuiteCatalog\Block\CatalogSearch\Result\Cache::POPULAR_SEARCH_CACHE_TAG; + } + } + + return $identities; + } + + /** + * Get category identities related to an optimizer. + * + * @param \Smile\ElasticsuiteCatalogOptimizer\Api\Data\OptimizerInterface $optimizer The optimizer + * + * @return array + */ + private function getCategoryIdentities(OptimizerInterface $optimizer) + { + $identities = []; + $categoryIds = []; + $origData = $optimizer->getOrigData(); + $data = $optimizer->getData(); + + // If optimizer was previously assigned to all categories, or is now set to all categories. + $isAppliedToAllCategories = empty($data['catalog_view_container']) + || (bool) $data['catalog_view_container']['apply_to'] === false; + + $wasAppliedToAllCategories = empty($origData['catalog_view_container']['category_ids']); + + if ($isAppliedToAllCategories || $wasAppliedToAllCategories) { + $identities[] = \Magento\Catalog\Model\Category::CACHE_TAG; + } + + if (!empty($data['catalog_view_container']['category_ids'])) { + $categoryIds = array_merge($categoryIds, $data['catalog_view_container']['category_ids']); + } + + if (!empty($origData['catalog_view_container']['category_ids'])) { + $categoryIds = array_merge($categoryIds, $origData['catalog_view_container']['category_ids']); + } + + $categoryIds = array_filter(array_unique($categoryIds)); + if (!empty($categoryIds)) { + $categoryTags = array_map(function ($categoryId) { + return \Magento\Catalog\Model\Category::CACHE_TAG . '_' . $categoryId; + }, $categoryIds); + + $identities = array_merge($identities, $categoryTags); + } + + return $identities; + } + + /** + * Retrieve maximum count cacheable search terms by Store. + * + * @param int $storeId Store Id + * + * @return int + */ + private function getMaxCountCacheableSearchTerms(int $storeId) + { + return $this->scopeConfig->getValue( + PopularSearchTerms::XML_PATH_MAX_COUNT_CACHEABLE_SEARCH_TERMS, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeId + ); + } +}