1414use Magento \Catalog \Model \ProductFactory ;
1515use Magento \Catalog \Model \ProductRepository as ProductRepositoryBase ;
1616use Magento \Catalog \Model \ResourceModel \Product as ProductResourceModel ;
17+ use Magento \Catalog \Model \ResourceModel \Product \CollectionFactory as ProductCollectionFactory ;
1718use Magento \Framework \Api \SearchCriteriaBuilder ;
1819use Magento \Framework \Api \SearchCriteriaInterface ;
1920use Magento \Framework \App \Area ;
@@ -66,6 +67,9 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
6667 /** @var \Magento\Catalog\Model\ProductFactory */
6768 protected $ productFactory ;
6869
70+ /** @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */
71+ protected $ productCollectionFactory ;
72+
6973 /** @var \Magento\Framework\Api\SearchCriteriaBuilder */
7074 protected $ searchCriteriaBuilder ;
7175
@@ -107,6 +111,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
107111 * @param MagentoStoreConfig $magentoStoreConfig Magento core store config.
108112 * @param ScopeConfigInterface $scopeConfig Magento core scope config.
109113 * @param ProductFactory $productFactory Product factory instance.
114+ * @param ProductCollectionFactory $productCollectionFactory Product collection factory instance.
110115 * @param SearchCriteriaBuilder $searchCriteriaBuilder Search criteria builder.
111116 * @param ProductResourceModel $resourceModel Product resource model.
112117 * @param StoreManagerInterface $storeManager Store manager interface.
@@ -126,6 +131,7 @@ public function __construct(
126131 MagentoStoreConfig $ magentoStoreConfig ,
127132 ScopeConfigInterface $ scopeConfig ,
128133 ProductFactory $ productFactory ,
134+ ProductCollectionFactory $ productCollectionFactory ,
129135 SearchCriteriaBuilder $ searchCriteriaBuilder ,
130136 ProductResourceModel $ resourceModel ,
131137 StoreManagerInterface $ storeManager ,
@@ -143,6 +149,7 @@ public function __construct(
143149 $ this ->magentoStoreConfig = $ magentoStoreConfig ;
144150 $ this ->scopeConfig = $ scopeConfig ;
145151 $ this ->productFactory = $ productFactory ;
152+ $ this ->productCollectionFactory = $ productCollectionFactory ;
146153 $ this ->searchCriteriaBuilder = $ searchCriteriaBuilder ;
147154 $ this ->resourceModel = $ resourceModel ;
148155 $ this ->storeManager = $ storeManager ;
@@ -688,20 +695,29 @@ private function getProductImagesLinks(ProductInterface $product, ?string $image
688695 */
689696 private function getEnabledConfigurableLinks ($ configurableLinksIds )
690697 {
691- $ enabledProductIds = [];
692-
693- if (null === $ configurableLinksIds ) {
694- return $ enabledProductIds ;
698+ if (empty ($ configurableLinksIds )) {
699+ return [];
695700 }
696701
702+ /* Resolve the enabled status of every linked product in a single query
703+ * instead of loading each product individually (N+1), which is
704+ * prohibitively slow for configurables with many variants.
705+ * Status is read at the current (emulated) store scope so that
706+ * store-view level overrides are respected, matching the previous
707+ * getById() behaviour.
708+ */
709+ $ collection = $ this ->productCollectionFactory ->create ();
710+ $ collection ->setStoreId ((int ) $ this ->storeManager ->getStore ()->getId ());
711+ $ collection ->addIdFilter ($ configurableLinksIds );
712+ $ collection ->addAttributeToFilter ('status ' , Status::STATUS_ENABLED );
713+ $ enabledIds = array_flip (array_map ('intval ' , $ collection ->getAllIds ()));
714+
715+ // Keep the original order of the configurable links.
716+ $ enabledProductIds = [];
697717 foreach ($ configurableLinksIds as $ productId ) {
698- $ product = $ this ->getById ($ productId );
699-
700- if (Status::STATUS_ENABLED !== (int ) $ product ->getStatus ()) {
701- continue ;
718+ if (isset ($ enabledIds [(int ) $ productId ])) {
719+ $ enabledProductIds [] = $ productId ;
702720 }
703-
704- $ enabledProductIds [] = $ productId ;
705721 }
706722
707723 return $ enabledProductIds ;
0 commit comments