├── .gitignore ├── EcomDev ├── ConfigurableDisableIdentities │ ├── composer.json │ ├── etc │ │ ├── di.xml │ │ └── module.xml │ └── registration.php ├── ConfigurableProductPreloader │ ├── Loader │ │ ├── ConfigurablePrice.php │ │ ├── ConfigurableSalable.php │ │ └── ConfigurableTierPrice.php │ ├── Plugin │ │ ├── ConfigurablePriceResolverPlugin.php │ │ ├── ConfigurableSalableCheckPlugin.php │ │ ├── ConfigurableTierPriceCalculatorPlugin.php │ │ └── ConfigurableTypeSalableCheckPlugin.php │ ├── composer.json │ ├── etc │ │ ├── di.xml │ │ └── module.xml │ └── registration.php └── NoPriceCache │ ├── Plugin │ └── AvoidCachingProductPrice.php │ ├── composer.json │ ├── etc │ ├── di.xml │ └── module.xml │ └── registration.php ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /var/* 2 | !/var/.htaccess 3 | 4 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableDisableIdentities/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecom-dev/configurable-disable-identities", 3 | "version": "1.0.0", 4 | "description": "N/A", 5 | "type": "magento2-module", 6 | "require": { 7 | "magento/framework": "*", 8 | "ecomdev/magento2-configurable-preloader": "1.0.*" 9 | }, 10 | "license": [ 11 | "Massachusetts Institute of Technology License (MITL)" 12 | ], 13 | "autoload": { 14 | "files": [ 15 | "registration.php" 16 | ], 17 | "psr-4": { 18 | "EcomDev\\ConfigurableDisableIdentities\\": "" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableDisableIdentities/etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableDisableIdentities/etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableDisableIdentities/registration.php: -------------------------------------------------------------------------------- 1 | resourceConnection = $resourceConnection; 24 | } 25 | 26 | /** @inheritDoc */ 27 | public function load(ScopeFilter $filter, array $products): array 28 | { 29 | $configurableProductIds = []; 30 | 31 | foreach ($products as $product) { 32 | if ($product->isType(Configurable::TYPE_CODE)) { 33 | $configurableProductIds[] = $product->getId(); 34 | } 35 | } 36 | 37 | if (!$configurableProductIds) { 38 | return []; 39 | } 40 | 41 | $connection = $this->resourceConnection->getConnection('catalog'); 42 | $priceIndexTable = $this->resourceConnection->getTableName('catalog_product_index_price','catalog'); 43 | $relationTable = $this->resourceConnection->getTableName('catalog_product_super_link','catalog'); 44 | $select = $connection->select(); 45 | 46 | $select 47 | ->from(['index' => $priceIndexTable], []) 48 | ->join(['relation' => $relationTable], 'relation.product_id = index.entity_id', []) 49 | ->columns([ 50 | 'entity_id' => 'relation.parent_id', 51 | 'price' => 'MIN(index.price)', 52 | 'final_price' => 'MIN(index.final_price)', 53 | ]) 54 | ->group('relation.parent_id') 55 | ->where('relation.parent_id IN(?)', $configurableProductIds) 56 | ->where('index.customer_group_id = ?', $filter->getCustomerGroupId()) 57 | ->where('index.website_id = ?', $filter->getWebsiteId()) 58 | ; 59 | 60 | return $connection->fetchAssoc($select); 61 | } 62 | 63 | public function isApplicable(string $type): bool 64 | { 65 | return $type === self::TYPE_LIST; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/Loader/ConfigurableSalable.php: -------------------------------------------------------------------------------- 1 | stockResolver = $stockResolver; 36 | $this->indexTableNameResolver = $indexTableNameResolver; 37 | $this->storeManager = $storeManager; 38 | $this->resourceConnection = $resourceConnection; 39 | } 40 | 41 | public function load(ScopeFilter $filter, array $products): array 42 | { 43 | $configurableProductIds = []; 44 | 45 | foreach ($products as $product) { 46 | if ($product->isType(Configurable::TYPE_CODE)) { 47 | $configurableProductIds[] = $product->getId(); 48 | } 49 | } 50 | 51 | if (!$configurableProductIds) { 52 | return []; 53 | } 54 | 55 | $websiteCode = $this->storeManager->getWebsite($filter->getWebsiteId())->getCode(); 56 | $stock = $this->stockResolver->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode); 57 | $connection = $this->resourceConnection->getConnection('catalog'); 58 | $relationTable = $this->resourceConnection->getTableName('catalog_product_super_link','catalog'); 59 | $productTable = $this->resourceConnection->getTableName('catalog_product_entity','catalog'); 60 | $indexTableName = $this->indexTableNameResolver->execute((int)$stock->getStockId()); 61 | $select = $connection->select(); 62 | 63 | $select 64 | ->from(['relation' => $relationTable], []) 65 | ->join(['product' => $productTable], 'relation.product_id = product.entity_id', []) 66 | ->join(['index' => $indexTableName], 'product.sku = index.sku', []) 67 | ->columns([ 68 | 'entity_id' => 'relation.parent_id', 69 | 'is_salable' => 'MAX(index.is_salable)', 70 | ]) 71 | ->group('relation.parent_id') 72 | ->where('relation.parent_id IN(?)', $configurableProductIds); 73 | ; 74 | 75 | return $connection->fetchAssoc($select); 76 | } 77 | 78 | public function isApplicable(string $type): bool 79 | { 80 | return true; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/Loader/ConfigurableTierPrice.php: -------------------------------------------------------------------------------- 1 | resourceConnection = $resourceConnection; 27 | } 28 | 29 | public function load(ScopeFilter $filter, array $products): array 30 | { 31 | $configurableProductIds = []; 32 | 33 | foreach ($products as $product) { 34 | if ($product->isType(Configurable::TYPE_CODE)) { 35 | $configurableProductIds[] = $product->getId(); 36 | } 37 | } 38 | 39 | if (!$configurableProductIds) { 40 | return []; 41 | } 42 | 43 | $connection = $this->resourceConnection->getConnection('catalog'); 44 | $tierPriceTable = $this->resourceConnection->getTableName('catalog_product_entity_tier_price','catalog'); 45 | $relationTable = $this->resourceConnection->getTableName('catalog_product_super_link','catalog'); 46 | $select = $connection->select(); 47 | 48 | $select 49 | ->from(['tier_price' => $tierPriceTable], []) 50 | ->join(['relation' => $relationTable], 'relation.product_id = tier_price.entity_id', []) 51 | ->columns([ 52 | 'entity_id' => 'relation.parent_id', 53 | 'tier_price' => 'MIN(tier_price.value)' 54 | ]) 55 | ->group('relation.parent_id') 56 | ->where('relation.parent_id IN(?)', $configurableProductIds) 57 | ->where('tier_price.customer_group_id = ? OR tier_price.all_groups = 1', $filter->getCustomerGroupId()) 58 | ->where('tier_price.website_id = ?', $filter->getWebsiteId()) 59 | ; 60 | 61 | return $connection->fetchAssoc($select); 62 | } 63 | 64 | public function isApplicable(string $type): bool 65 | { 66 | return $type === self::TYPE_LIST; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/Plugin/ConfigurablePriceResolverPlugin.php: -------------------------------------------------------------------------------- 1 | loadService = $loadService; 22 | } 23 | 24 | public function aroundResolvePrice(ConfigurablePriceResolver $subject, callable $proceed, SaleableInterface $product) 25 | { 26 | $productId = (int)$product->getId(); 27 | 28 | if ($this->loadService->has($productId, ConfigurablePrice::DATA_KEY)) { 29 | return (float)($this->loadService->get($productId, ConfigurablePrice::DATA_KEY)['final_price'] ?? 0); 30 | } 31 | 32 | return $proceed($product); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/Plugin/ConfigurableSalableCheckPlugin.php: -------------------------------------------------------------------------------- 1 | loadService = $loadService; 23 | } 24 | 25 | public function afterIsSalable(SalableResolver $subject, $result, SaleableInterface $salableItem) 26 | { 27 | $productId = (int)$salableItem->getId(); 28 | $canProcess = $salableItem->getTypeId() === Configurable::TYPE_CODE && $result; 29 | if ($canProcess && $this->loadService->has($productId, ConfigurableSalable::DATA_KEY)) { 30 | return (bool)($this->loadService->get($productId, ConfigurableSalable::DATA_KEY)['is_salable'] ?? false); 31 | } 32 | return $result; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/Plugin/ConfigurableTierPriceCalculatorPlugin.php: -------------------------------------------------------------------------------- 1 | loadService = $loadService; 23 | } 24 | 25 | public function aroundGetValue(MinimalTierPriceCalculator $subject, callable $proceed, SaleableInterface $saleableItem) 26 | { 27 | $productId = (int)$saleableItem->getId(); 28 | 29 | if ($saleableItem->getTypeId() === Configurable::TYPE_CODE 30 | && $this->loadService->has($productId, ConfigurableTierPrice::DATA_KEY)) { 31 | $tierPrice = ($this->loadService->get($productId, ConfigurableTierPrice::DATA_KEY)['tier_price'] ?? null); 32 | 33 | return $tierPrice ? (float)$tierPrice : null; 34 | } 35 | return $proceed($saleableItem); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/Plugin/ConfigurableTypeSalableCheckPlugin.php: -------------------------------------------------------------------------------- 1 | loadService = $loadService; 22 | } 23 | 24 | public function aroundIsSalable(Configurable $subject, callable $proceed, Product $product) 25 | { 26 | $productId = (int)$product->getId(); 27 | 28 | if (!$this->loadService->has($productId, ConfigurableSalable::DATA_KEY)) { 29 | return $proceed($product); 30 | } 31 | $isSalable = ((int)$product->getStatus()) === Product\Attribute\Source\Status::STATUS_ENABLED; 32 | $isSalable = $isSalable 33 | && $this->loadService->get($productId, ConfigurableSalable::DATA_KEY)['is_salable'] ?? false; 34 | 35 | return $isSalable; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecomdev/magento2-configurable-preloader", 3 | "version": "1.0.0", 4 | "description": "Preloader for configurable data", 5 | "type": "magento2-module", 6 | "require": { 7 | "magento/framework": "*", 8 | "ecomdev/magento2-product-data-preloader": "*" 9 | }, 10 | "license": [ 11 | "MIT" 12 | ], 13 | "autoload": { 14 | "files": [ 15 | "registration.php" 16 | ], 17 | "psr-4": { 18 | "EcomDev\\ConfigurableProductPreloader\\": "" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EcomDev\ConfigurableProductPreloader\Loader\ConfigurablePrice 7 | EcomDev\ConfigurableProductPreloader\Loader\ConfigurableSalable 8 | EcomDev\ConfigurableProductPreloader\Loader\ConfigurableTierPrice 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EcomDev/ConfigurableProductPreloader/registration.php: -------------------------------------------------------------------------------- 1 | setData('cache_lifetime', false); 23 | return []; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /EcomDev/NoPriceCache/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecom-dev/no-price-cache", 3 | "version": "1.0.0", 4 | "description": "N/A", 5 | "type": "magento2-module", 6 | "require": { 7 | "magento/framework": "*" 8 | }, 9 | "license": [ 10 | "Proprietary" 11 | ], 12 | "autoload": { 13 | "files": [ 14 | "registration.php" 15 | ], 16 | "psr-4": { 17 | "EcomDev\\NoPriceCache\\": "" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EcomDev/NoPriceCache/etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /EcomDev/NoPriceCache/etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EcomDev/NoPriceCache/registration.php: -------------------------------------------------------------------------------- 1 |