55 */
66namespace Magento \ProductAlert \Model ;
77
8+ use Magento \Backend \App \Area \FrontNameResolver ;
9+ use Magento \Catalog \Api \ProductRepositoryInterface ;
10+ use Magento \Catalog \Helper \Data ;
11+ use Magento \Customer \Api \CustomerRepositoryInterface ;
12+ use Magento \Framework \App \Config \ScopeConfigInterface ;
13+ use Magento \Framework \App \ObjectManager ;
14+ use Magento \Framework \Mail \Template \TransportBuilder ;
15+ use Magento \Framework \Model \ResourceModel \Db \Collection \AbstractCollection ;
16+ use Magento \Framework \Stdlib \DateTime \DateTimeFactory ;
17+ use Magento \Framework \Translate \Inline \StateInterface ;
18+ use Magento \ProductAlert \Model \ResourceModel \Stock \CollectionFactory as StockCollectionFactory ;
19+ use Magento \ProductAlert \Model \ResourceModel \Price \CollectionFactory as PriceCollectionFactory ;
20+ use Magento \Store \Model \ScopeInterface ;
21+ use Magento \Store \Model \Store ;
22+ use Magento \Store \Model \StoreManagerInterface ;
23+ use Magento \Store \Model \Website ;
24+
825/**
926 * ProductAlert observer
1027 *
11- * @author Magento Core Team <core@magentocommerce.com>
28+ * @author Magento Core Team <core@magentocommerce.com>
1229 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1330 */
1431class Observer
@@ -40,6 +57,11 @@ class Observer
4057 */
4158 const XML_PATH_STOCK_ALLOW = 'catalog/productalert/allow_stock ' ;
4259
60+ /**
61+ * Default value of bunch size to load alert items
62+ */
63+ private const DEFAULT_BUNCH_SIZE = 10000 ;
64+
4365 /**
4466 * Website collection array
4567 *
@@ -57,59 +79,59 @@ class Observer
5779 /**
5880 * Catalog data
5981 *
60- * @var \Magento\Catalog\Helper\ Data
82+ * @var Data
6183 */
6284 protected $ _catalogData = null ;
6385
6486 /**
6587 * Core store config
6688 *
67- * @var \Magento\Framework\App\Config\ ScopeConfigInterface
89+ * @var ScopeConfigInterface
6890 */
6991 protected $ _scopeConfig ;
7092
7193 /**
72- * @var \Magento\Store\Model\ StoreManagerInterface
94+ * @var StoreManagerInterface
7395 */
7496 protected $ _storeManager ;
7597
7698 /**
77- * @var \Magento\ProductAlert\Model\ResourceModel\Price\CollectionFactory
99+ * @var PriceCollectionFactory
78100 */
79101 protected $ _priceColFactory ;
80102
81103 /**
82- * @var \Magento\Customer\Api\ CustomerRepositoryInterface
104+ * @var CustomerRepositoryInterface
83105 */
84106 protected $ customerRepository ;
85107
86108 /**
87- * @var \Magento\Catalog\Api\ ProductRepositoryInterface
109+ * @var ProductRepositoryInterface
88110 */
89111 protected $ productRepository ;
90112
91113 /**
92- * @var \Magento\Framework\Stdlib\DateTime\ DateTimeFactory
114+ * @var DateTimeFactory
93115 */
94116 protected $ _dateFactory ;
95117
96118 /**
97- * @var \Magento\ProductAlert\Model\ResourceModel\Stock\CollectionFactory
119+ * @var StockCollectionFactory
98120 */
99121 protected $ _stockColFactory ;
100122
101123 /**
102- * @var \Magento\Framework\Mail\Template\ TransportBuilder
124+ * @var TransportBuilder
103125 */
104126 protected $ _transportBuilder ;
105127
106128 /**
107- * @var \Magento\ProductAlert\Model\ EmailFactory
129+ * @var EmailFactory
108130 */
109131 protected $ _emailFactory ;
110132
111133 /**
112- * @var \Magento\Framework\Translate\Inline\ StateInterface
134+ * @var StateInterface
113135 */
114136 protected $ inlineTranslation ;
115137
@@ -119,33 +141,40 @@ class Observer
119141 protected $ productSalability ;
120142
121143 /**
122- * @param \Magento\Catalog\Helper\Data $catalogData
123- * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
124- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
125- * @param \Magento\ProductAlert\Model\ResourceModel\Price\CollectionFactory $priceColFactory
126- * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
127- * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
128- * @param \Magento\Framework\Stdlib\DateTime\DateTimeFactory $dateFactory
129- * @param \Magento\ProductAlert\Model\ResourceModel\Stock\CollectionFactory $stockColFactory
130- * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
131- * @param \Magento\ProductAlert\Model\EmailFactory $emailFactory
132- * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
144+ * @var int
145+ */
146+ private $ bunchSize ;
147+
148+ /**
149+ * @param Data $catalogData
150+ * @param ScopeConfigInterface $scopeConfig
151+ * @param StoreManagerInterface $storeManager
152+ * @param PriceCollectionFactory $priceColFactory
153+ * @param CustomerRepositoryInterface $customerRepository
154+ * @param ProductRepositoryInterface $productRepository
155+ * @param DateTimeFactory $dateFactory
156+ * @param StockCollectionFactory $stockColFactory
157+ * @param TransportBuilder $transportBuilder
158+ * @param EmailFactory $emailFactory
159+ * @param StateInterface $inlineTranslation
133160 * @param ProductSalability|null $productSalability
161+ * @param int $bunchSize
134162 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
135163 */
136164 public function __construct (
137- \Magento \Catalog \Helper \Data $ catalogData ,
138- \Magento \Framework \App \Config \ScopeConfigInterface $ scopeConfig ,
139- \Magento \Store \Model \StoreManagerInterface $ storeManager ,
140- \Magento \ProductAlert \Model \ResourceModel \Price \CollectionFactory $ priceColFactory ,
141- \Magento \Customer \Api \CustomerRepositoryInterface $ customerRepository ,
142- \Magento \Catalog \Api \ProductRepositoryInterface $ productRepository ,
143- \Magento \Framework \Stdlib \DateTime \DateTimeFactory $ dateFactory ,
144- \Magento \ProductAlert \Model \ResourceModel \Stock \CollectionFactory $ stockColFactory ,
145- \Magento \Framework \Mail \Template \TransportBuilder $ transportBuilder ,
146- \Magento \ProductAlert \Model \EmailFactory $ emailFactory ,
147- \Magento \Framework \Translate \Inline \StateInterface $ inlineTranslation ,
148- ProductSalability $ productSalability = null
165+ Data $ catalogData ,
166+ ScopeConfigInterface $ scopeConfig ,
167+ StoreManagerInterface $ storeManager ,
168+ PriceCollectionFactory $ priceColFactory ,
169+ CustomerRepositoryInterface $ customerRepository ,
170+ ProductRepositoryInterface $ productRepository ,
171+ DateTimeFactory $ dateFactory ,
172+ StockCollectionFactory $ stockColFactory ,
173+ TransportBuilder $ transportBuilder ,
174+ EmailFactory $ emailFactory ,
175+ StateInterface $ inlineTranslation ,
176+ ProductSalability $ productSalability = null ,
177+ int $ bunchSize = 0
149178 ) {
150179 $ this ->_catalogData = $ catalogData ;
151180 $ this ->_scopeConfig = $ scopeConfig ;
@@ -158,8 +187,9 @@ public function __construct(
158187 $ this ->_transportBuilder = $ transportBuilder ;
159188 $ this ->_emailFactory = $ emailFactory ;
160189 $ this ->inlineTranslation = $ inlineTranslation ;
161- $ objectManager = \ Magento \ Framework \ App \ ObjectManager::getInstance ();
190+ $ objectManager = ObjectManager::getInstance ();
162191 $ this ->productSalability = $ productSalability ?: $ objectManager ->get (ProductSalability::class);
192+ $ this ->bunchSize = $ bunchSize ?: self ::DEFAULT_BUNCH_SIZE ;
163193 }
164194
165195 /**
@@ -184,40 +214,41 @@ protected function _getWebsites()
184214 /**
185215 * Process price emails
186216 *
187- * @param \Magento\ProductAlert\Model\ Email $email
217+ * @param Email $email
188218 * @return $this
189219 * @throws \Exception
190220 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
191221 * @SuppressWarnings(PHPMD.NPathComplexity)
192222 */
193- protected function _processPrice (\ Magento \ ProductAlert \ Model \ Email $ email )
223+ protected function _processPrice (Email $ email )
194224 {
195225 $ email ->setType ('price ' );
196226 foreach ($ this ->_getWebsites () as $ website ) {
197- /* @var $website \Magento\Store\Model\ Website */
227+ /* @var $website Website */
198228 if (!$ website ->getDefaultGroup () || !$ website ->getDefaultGroup ()->getDefaultStore ()) {
199229 continue ;
200230 }
201231 if (!$ this ->_scopeConfig ->getValue (
202- self ::XML_PATH_PRICE_ALLOW ,
203- \ Magento \ Store \ Model \ ScopeInterface::SCOPE_STORE ,
204- $ website ->getDefaultGroup ()->getDefaultStore ()->getId ()
205- )
232+ self ::XML_PATH_PRICE_ALLOW ,
233+ ScopeInterface::SCOPE_STORE ,
234+ $ website ->getDefaultGroup ()->getDefaultStore ()->getId ()
235+ )
206236 ) {
207237 continue ;
208238 }
209239 try {
210- $ collection = $ this ->_priceColFactory ->create ()->addWebsiteFilter (
211- $ website ->getId ()
212- )->setCustomerOrder ();
240+ $ collection = $ this ->_priceColFactory ->create ()
241+ ->addWebsiteFilter ($ website ->getId ())
242+ ->setCustomerOrder ()
243+ ->addOrder ('product_id ' );
213244 } catch (\Exception $ e ) {
214245 $ this ->_errors [] = $ e ->getMessage ();
215246 throw $ e ;
216247 }
217248
218249 $ previousCustomer = null ;
219250 $ email ->setWebsite ($ website );
220- foreach ($ collection as $ alert ) {
251+ foreach ($ this -> loadItems ( $ collection, $ this -> bunchSize ) as $ alert ) {
221252 $ this ->setAlertStoreId ($ alert , $ email );
222253 try {
223254 if (!$ previousCustomer || $ previousCustomer ->getId () != $ alert ->getCustomerId ()) {
@@ -274,44 +305,44 @@ protected function _processPrice(\Magento\ProductAlert\Model\Email $email)
274305 /**
275306 * Process stock emails
276307 *
277- * @param \Magento\ProductAlert\Model\ Email $email
308+ * @param Email $email
278309 * @return $this
279310 * @throws \Exception
280311 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
281312 * @SuppressWarnings(PHPMD.NPathComplexity)
282313 */
283- protected function _processStock (\ Magento \ ProductAlert \ Model \ Email $ email )
314+ protected function _processStock (Email $ email )
284315 {
285316 $ email ->setType ('stock ' );
286317
287318 foreach ($ this ->_getWebsites () as $ website ) {
288- /* @var $website \Magento\Store\Model\ Website */
319+ /* @var $website Website */
289320
290321 if (!$ website ->getDefaultGroup () || !$ website ->getDefaultGroup ()->getDefaultStore ()) {
291322 continue ;
292323 }
293324 if (!$ this ->_scopeConfig ->getValue (
294325 self ::XML_PATH_STOCK_ALLOW ,
295- \ Magento \ Store \ Model \ ScopeInterface::SCOPE_STORE ,
326+ ScopeInterface::SCOPE_STORE ,
296327 $ website ->getDefaultGroup ()->getDefaultStore ()->getId ()
297328 )
298329 ) {
299330 continue ;
300331 }
301332 try {
302- $ collection = $ this ->_stockColFactory ->create ()-> addWebsiteFilter (
303- $ website ->getId ()
304- ) ->addStatusFilter (
305- 0
306- )-> setCustomerOrder ( );
333+ $ collection = $ this ->_stockColFactory ->create ()
334+ -> addWebsiteFilter ( $ website ->getId () )
335+ ->addStatusFilter (0 )
336+ -> setCustomerOrder ()
337+ -> addOrder ( ' product_id ' );
307338 } catch (\Exception $ e ) {
308339 $ this ->_errors [] = $ e ->getMessage ();
309340 throw $ e ;
310341 }
311342
312343 $ previousCustomer = null ;
313344 $ email ->setWebsite ($ website );
314- foreach ($ collection as $ alert ) {
345+ foreach ($ this -> loadItems ( $ collection, $ this -> bunchSize ) as $ alert ) {
315346 $ this ->setAlertStoreId ($ alert , $ email );
316347 try {
317348 if (!$ previousCustomer || $ previousCustomer ->getId () != $ alert ->getCustomerId ()) {
@@ -374,7 +405,7 @@ protected function _sendErrorEmail()
374405 if (count ($ this ->_errors )) {
375406 if (!$ this ->_scopeConfig ->getValue (
376407 self ::XML_PATH_ERROR_TEMPLATE ,
377- \ Magento \ Store \ Model \ ScopeInterface::SCOPE_STORE
408+ ScopeInterface::SCOPE_STORE
378409 )
379410 ) {
380411 return $ this ;
@@ -385,24 +416,24 @@ protected function _sendErrorEmail()
385416 $ transport = $ this ->_transportBuilder ->setTemplateIdentifier (
386417 $ this ->_scopeConfig ->getValue (
387418 self ::XML_PATH_ERROR_TEMPLATE ,
388- \ Magento \ Store \ Model \ ScopeInterface::SCOPE_STORE
419+ ScopeInterface::SCOPE_STORE
389420 )
390421 )->setTemplateOptions (
391422 [
392- 'area ' => \ Magento \ Backend \ App \ Area \ FrontNameResolver::AREA_CODE ,
393- 'store ' => \ Magento \ Store \ Model \ Store::DEFAULT_STORE_ID ,
423+ 'area ' => FrontNameResolver::AREA_CODE ,
424+ 'store ' => Store::DEFAULT_STORE_ID ,
394425 ]
395426 )->setTemplateVars (
396427 ['warnings ' => join ("\n" , $ this ->_errors )]
397428 )->setFrom (
398429 $ this ->_scopeConfig ->getValue (
399430 self ::XML_PATH_ERROR_IDENTITY ,
400- \ Magento \ Store \ Model \ ScopeInterface::SCOPE_STORE
431+ ScopeInterface::SCOPE_STORE
401432 )
402433 )->addTo (
403434 $ this ->_scopeConfig ->getValue (
404435 self ::XML_PATH_ERROR_RECIPIENT ,
405- \ Magento \ Store \ Model \ ScopeInterface::SCOPE_STORE
436+ ScopeInterface::SCOPE_STORE
406437 )
407438 )->getTransport ();
408439
@@ -421,7 +452,7 @@ protected function _sendErrorEmail()
421452 */
422453 public function process ()
423454 {
424- /* @var $email \Magento\ProductAlert\Model\ Email */
455+ /* @var $email Email */
425456 $ email = $ this ->_emailFactory ->create ();
426457 $ this ->_processPrice ($ email );
427458 $ this ->_processStock ($ email );
@@ -433,11 +464,11 @@ public function process()
433464 /**
434465 * Set alert store id.
435466 *
436- * @param \Magento\ProductAlert\Model\ Price|\Magento\ProductAlert\Model\ Stock $alert
467+ * @param Price|Stock $alert
437468 * @param Email $email
438469 * @return Observer
439470 */
440- private function setAlertStoreId ($ alert , \ Magento \ ProductAlert \ Model \ Email $ email ) : Observer
471+ private function setAlertStoreId ($ alert , Email $ email ): Observer
441472 {
442473 $ alertStoreId = $ alert ->getStoreId ();
443474 if ($ alertStoreId ) {
@@ -446,4 +477,26 @@ private function setAlertStoreId($alert, \Magento\ProductAlert\Model\Email $emai
446477
447478 return $ this ;
448479 }
480+
481+ /**
482+ * Load items by bunch size
483+ *
484+ * @param AbstractCollection $collection
485+ * @param int $bunchSize
486+ * @return \Generator
487+ */
488+ private function loadItems (AbstractCollection $ collection , int $ bunchSize ): \Generator
489+ {
490+ $ collection ->setPageSize ($ bunchSize );
491+ $ pageCount = $ collection ->getLastPageNumber ();
492+ $ curPage = 1 ;
493+ while ($ curPage <= $ pageCount ) {
494+ $ collection ->clear ();
495+ $ collection ->setCurPage ($ curPage );
496+ foreach ($ collection as $ item ) {
497+ yield $ item ;
498+ }
499+ $ curPage ++;
500+ }
501+ }
449502}
0 commit comments