77
88namespace Magento \Bundle \Model \Plugin ;
99
10+ use Magento \Bundle \Model \Product \Price ;
11+ use Magento \Catalog \Api \Data \ProductInterface ;
12+ use Magento \Catalog \Api \ProductRepositoryInterface ;
13+ use Magento \Catalog \Model \Product \Type ;
1014use Magento \Catalog \Model \ResourceModel \Product \Indexer \Price \IndexTableStructure ;
15+ use Magento \CatalogInventory \Api \StockConfigurationInterface ;
1116use Magento \CatalogInventory \Model \Indexer \ProductPriceIndexFilter ;
1217use Magento \Framework \App \ResourceConnection ;
18+ use Magento \Framework \EntityManager \MetadataPool ;
19+ use Magento \Framework \Exception \NoSuchEntityException ;
1320
1421/**
1522 * Checks if product is part of dynamic price bundle and skips price reindex
1623 */
1724class ProductPriceIndexModifier
1825{
26+ /**
27+ * @var StockConfigurationInterface
28+ */
29+ private StockConfigurationInterface $ stockConfiguration ;
30+
1931 /**
2032 * @var ResourceConnection
2133 */
2234 private ResourceConnection $ resourceConnection ;
2335
36+ /**
37+ * @var MetadataPool
38+ */
39+ private MetadataPool $ metadataPool ;
40+
41+ /**
42+ * @var ProductRepositoryInterface
43+ */
44+ private ProductRepositoryInterface $ productRepository ;
45+
2446 /**
2547 * @var string
2648 */
2749 private string $ connectionName ;
2850
2951 /**
52+ * @param StockConfigurationInterface $stockConfiguration
3053 * @param ResourceConnection $resourceConnection
54+ * @param MetadataPool $metadataPool
55+ * @param ProductRepositoryInterface|null $productRepository
3156 * @param string $connectionName
3257 */
33- public function __construct (ResourceConnection $ resourceConnection , string $ connectionName = 'indexer ' )
58+ public function __construct (
59+ StockConfigurationInterface $ stockConfiguration ,
60+ ResourceConnection $ resourceConnection ,
61+ MetadataPool $ metadataPool ,
62+ ?ProductRepositoryInterface $ productRepository = null ,
63+ string $ connectionName = 'indexer '
64+ )
3465 {
66+ $ this ->stockConfiguration = $ stockConfiguration ;
3567 $ this ->resourceConnection = $ resourceConnection ;
68+ $ this ->metadataPool = $ metadataPool ;
3669 $ this ->connectionName = $ connectionName ;
70+ $ this ->productRepository = $ productRepository ?: \Magento \Framework \App \ObjectManager::getInstance ()
71+ ->get (ProductRepositoryInterface::class);
3772 }
3873
3974 /**
@@ -44,49 +79,63 @@ public function __construct(ResourceConnection $resourceConnection, string $conn
4479 * @param IndexTableStructure $priceTable
4580 * @param array $entityIds
4681 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
82+ * @throws \Exception
4783 */
4884 public function aroundModifyPrice (
4985 ProductPriceIndexFilter $ subject ,
5086 callable $ proceed ,
5187 IndexTableStructure $ priceTable ,
5288 array $ entityIds = []
5389 ) {
54- if (empty ($ entityIds )) {
55- $ proceed ($ priceTable , []);
56- }
57-
58- $ filteredEntities = [];
59- foreach ($ entityIds as $ id ) {
60- if (!$ this ->isWithinDynamicPriceBundle ($ priceTable ->getTableName (), (int ) $ id )) {
61- $ filteredEntities [] = $ id ;
62- }
90+ if (empty ($ entityIds ) || $ this ->stockConfiguration ->isShowOutOfStock ()) {
91+ $ proceed ($ priceTable , $ entityIds );
6392 }
93+ $ filteredEntities = $ this ->filterProductsFromDynamicPriceBundle ($ priceTable ->getTableName (), $ entityIds );
6494
6595 if (!empty ($ filteredEntities )) {
6696 $ proceed ($ priceTable , $ filteredEntities );
6797 }
6898 }
6999
70100 /**
71- * Check if the product is part of a dynamic price bundle configuration
101+ * Filter products that are part of a dynamic bundle price configuration
72102 *
73103 * @param string $priceTableName
74- * @param int $productId
75- * @return bool
104+ * @param array $productIds
105+ * @return array
106+ * @throws NoSuchEntityException
76107 */
77- private function isWithinDynamicPriceBundle (string $ priceTableName , int $ productId ): bool
108+ private function filterProductsFromDynamicPriceBundle (string $ priceTableName , array $ productIds ): array
78109 {
110+ $ linkField = $ this ->metadataPool ->getMetadata (ProductInterface::class)->getLinkField ();
79111 $ connection = $ this ->resourceConnection ->getConnection ($ this ->connectionName );
80112 $ select = $ connection ->select ();
81- $ select ->from (['selection ' => 'catalog_product_bundle_selection ' ], 'selection_id ' );
113+ $ select ->from (['selection ' => $ this ->resourceConnection ->getTableName ('catalog_product_bundle_selection ' )]);
114+ $ select ->columns (['product.entity_id AS bundle_id ' , 'selection.product_id AS child_product_id ' ]);
82115 $ select ->joinInner (
83- ['price ' => $ priceTableName ],
84- implode (' AND ' , ['price.entity_id = selection.product_id ' ]),
85- null
116+ ['price ' => $ this ->resourceConnection ->getTableName ($ priceTableName )],
117+ implode (' AND ' , ['price.entity_id = selection.product_id ' ])
86118 );
87- $ select ->where ('selection.product_id = ? ' , $ productId );
88- $ select ->where ('price.tax_class_id = ? ' , \Magento \Bundle \Model \Product \Price::PRICE_TYPE_DYNAMIC );
119+ $ select ->joinInner (
120+ ['product ' => $ this ->resourceConnection ->getTableName ('catalog_product_entity ' )],
121+ "product. $ linkField = selection.parent_product_id "
122+ );
123+ $ select ->where ('selection.product_id IN (?) ' , $ productIds );
124+ $ select ->where ('product.type_id = ? ' , Type::TYPE_BUNDLE );
125+ $ bundleProducts = $ connection ->fetchAll ($ select );
126+
127+ if (empty ($ bundleProducts )) {
128+ return [];
129+ }
130+
131+ $ filteredProducts = [];
132+ foreach ($ bundleProducts as $ bundle ) {
133+ $ bundleProduct = $ this ->productRepository ->getById ($ bundle ['bundle_id ' ]);
134+ if ($ bundleProduct ->getPriceType () != Price::PRICE_TYPE_DYNAMIC ) {
135+ $ filteredProducts [] = $ bundle ['child_product_id ' ];
136+ }
137+ }
89138
90- return ( int ) $ connection -> fetchOne ( $ select ) != 0 ;
139+ return $ filteredProducts ;
91140 }
92141}
0 commit comments