77namespace Magento \Catalog \Block \Product ;
88
99use Magento \Catalog \Api \CategoryRepositoryInterface ;
10+ use Magento \Catalog \Block \Product \ProductList \Toolbar ;
1011use Magento \Catalog \Model \Category ;
1112use Magento \Catalog \Model \Product ;
13+ use Magento \Catalog \Model \ResourceModel \Product \Collection ;
1214use Magento \Eav \Model \Entity \Collection \AbstractCollection ;
1315use Magento \Framework \Exception \NoSuchEntityException ;
1416use Magento \Framework \DataObject \IdentityInterface ;
@@ -24,7 +26,7 @@ class ListProduct extends AbstractProduct implements IdentityInterface
2426 *
2527 * @var string
2628 */
27- protected $ _defaultToolbarBlock = \ Magento \ Catalog \ Block \ Product \ ProductList \ Toolbar::class;
29+ protected $ _defaultToolbarBlock = Toolbar::class;
2830
2931 /**
3032 * Product Collection
@@ -84,50 +86,23 @@ public function __construct(
8486 /**
8587 * Retrieve loaded category collection
8688 *
89+ * The goal of this method is to choose whether the existing collection should be returned
90+ * or a new one should be initialized.
91+ *
92+ * It is not just a caching logic, but also is a real logical check
93+ * because there are two ways how collection may be stored inside the block:
94+ * - Product collection may be passed externally by 'setCollection' method
95+ * - Product collection may be requested internally from the current Catalog Layer.
96+ *
97+ * And this method will return collection anyway,
98+ * even when it did not pass externally and therefore isn't cached yet
99+ *
87100 * @return AbstractCollection
88101 */
89102 protected function _getProductCollection ()
90103 {
91104 if ($ this ->_productCollection === null ) {
92- $ layer = $ this ->getLayer ();
93- /* @var $layer \Magento\Catalog\Model\Layer */
94- if ($ this ->getShowRootCategory ()) {
95- $ this ->setCategoryId ($ this ->_storeManager ->getStore ()->getRootCategoryId ());
96- }
97-
98- // if this is a product view page
99- if ($ this ->_coreRegistry ->registry ('product ' )) {
100- // get collection of categories this product is associated with
101- $ categories = $ this ->_coreRegistry ->registry ('product ' )
102- ->getCategoryCollection ()->setPage (1 , 1 )
103- ->load ();
104- // if the product is associated with any category
105- if ($ categories ->count ()) {
106- // show products from this category
107- $ this ->setCategoryId (current ($ categories ->getIterator ()));
108- }
109- }
110-
111- $ origCategory = null ;
112- if ($ this ->getCategoryId ()) {
113- try {
114- $ category = $ this ->categoryRepository ->get ($ this ->getCategoryId ());
115- } catch (NoSuchEntityException $ e ) {
116- $ category = null ;
117- }
118-
119- if ($ category ) {
120- $ origCategory = $ layer ->getCurrentCategory ();
121- $ layer ->setCurrentCategory ($ category );
122- }
123- }
124- $ this ->_productCollection = $ layer ->getProductCollection ();
125-
126- $ this ->prepareSortableFieldsByCategory ($ layer ->getCurrentCategory ());
127-
128- if ($ origCategory ) {
129- $ layer ->setCurrentCategory ($ origCategory );
130- }
105+ $ this ->_productCollection = $ this ->initializeProductCollection ();
131106 }
132107
133108 return $ this ->_productCollection ;
@@ -170,47 +145,17 @@ public function getMode()
170145 */
171146 protected function _beforeToHtml ()
172147 {
173- $ toolbar = $ this ->getToolbarBlock ();
174-
175- // called prepare sortable parameters
176148 $ collection = $ this ->_getProductCollection ();
177-
178- // use sortable parameters
179- $ orders = $ this ->getAvailableOrders ();
180- if ($ orders ) {
181- $ toolbar ->setAvailableOrders ($ orders );
182- }
183- $ sort = $ this ->getSortBy ();
184- if ($ sort ) {
185- $ toolbar ->setDefaultOrder ($ sort );
186- }
187- $ dir = $ this ->getDefaultDirection ();
188- if ($ dir ) {
189- $ toolbar ->setDefaultDirection ($ dir );
190- }
191- $ modes = $ this ->getModes ();
192- if ($ modes ) {
193- $ toolbar ->setModes ($ modes );
194- }
195-
196- // set collection to toolbar and apply sort
197- $ toolbar ->setCollection ($ collection );
198-
199- $ this ->setChild ('toolbar ' , $ toolbar );
200- $ this ->_eventManager ->dispatch (
201- 'catalog_block_product_list_collection ' ,
202- ['collection ' => $ this ->_getProductCollection ()]
203- );
204-
205- $ this ->_getProductCollection ()->load ();
149+ $ this ->configureToolbar ($ this ->getToolbarBlock (), $ collection );
150+ $ collection ->load ();
206151
207152 return parent ::_beforeToHtml ();
208153 }
209154
210155 /**
211156 * Retrieve Toolbar block
212157 *
213- * @return \Magento\Catalog\Block\Product\ProductList\ Toolbar
158+ * @return Toolbar
214159 */
215160 public function getToolbarBlock ()
216161 {
@@ -379,4 +324,107 @@ protected function getPriceRender()
379324 {
380325 return $ this ->getLayout ()->getBlock ('product.price.render.default ' );
381326 }
327+
328+ /**
329+ * Configures product collection from a layer and returns its instance.
330+ *
331+ * Also in the scope of a product collection configuration, this method initiates configuration of Toolbar.
332+ * The reason to do this is because we have a bunch of legacy code
333+ * where Toolbar configures several options of a collection and therefore this block depends on the Toolbar.
334+ *
335+ * This dependency leads to a situation where Toolbar sometimes called to configure a product collection,
336+ * and sometimes not.
337+ *
338+ * To unify this behavior and prevent potential bugs this dependency is explicitly called
339+ * when product collection initialized.
340+ *
341+ * @return Collection
342+ */
343+ private function initializeProductCollection ()
344+ {
345+ $ layer = $ this ->getLayer ();
346+ /* @var $layer \Magento\Catalog\Model\Layer */
347+ if ($ this ->getShowRootCategory ()) {
348+ $ this ->setCategoryId ($ this ->_storeManager ->getStore ()->getRootCategoryId ());
349+ }
350+
351+ // if this is a product view page
352+ if ($ this ->_coreRegistry ->registry ('product ' )) {
353+ // get collection of categories this product is associated with
354+ $ categories = $ this ->_coreRegistry ->registry ('product ' )
355+ ->getCategoryCollection ()->setPage (1 , 1 )
356+ ->load ();
357+ // if the product is associated with any category
358+ if ($ categories ->count ()) {
359+ // show products from this category
360+ $ this ->setCategoryId (current ($ categories ->getIterator ()));
361+ }
362+ }
363+
364+ $ origCategory = null ;
365+ if ($ this ->getCategoryId ()) {
366+ try {
367+ $ category = $ this ->categoryRepository ->get ($ this ->getCategoryId ());
368+ } catch (NoSuchEntityException $ e ) {
369+ $ category = null ;
370+ }
371+
372+ if ($ category ) {
373+ $ origCategory = $ layer ->getCurrentCategory ();
374+ $ layer ->setCurrentCategory ($ category );
375+ }
376+ }
377+ $ collection = $ layer ->getProductCollection ();
378+
379+ $ this ->prepareSortableFieldsByCategory ($ layer ->getCurrentCategory ());
380+
381+ if ($ origCategory ) {
382+ $ layer ->setCurrentCategory ($ origCategory );
383+ }
384+
385+ $ toolbar = $ this ->getToolbarBlock ();
386+ $ this ->configureToolbar ($ toolbar , $ collection );
387+
388+ $ this ->_eventManager ->dispatch (
389+ 'catalog_block_product_list_collection ' ,
390+ ['collection ' => $ collection ]
391+ );
392+
393+ return $ collection ;
394+ }
395+
396+ /**
397+ * Configures the Toolbar block with options from this block and configured product collection.
398+ *
399+ * The purpose of this method is the one-way sharing of different sorting related data
400+ * between this block, which is responsible for product list rendering,
401+ * and the Toolbar block, whose responsibility is a rendering of these options.
402+ *
403+ * @param ProductList\Toolbar $toolbar
404+ * @param Collection $collection
405+ * @return void
406+ */
407+ private function configureToolbar (Toolbar $ toolbar , Collection $ collection )
408+ {
409+ // use sortable parameters
410+ $ orders = $ this ->getAvailableOrders ();
411+ if ($ orders ) {
412+ $ toolbar ->setAvailableOrders ($ orders );
413+ }
414+ $ sort = $ this ->getSortBy ();
415+ if ($ sort ) {
416+ $ toolbar ->setDefaultOrder ($ sort );
417+ }
418+ $ dir = $ this ->getDefaultDirection ();
419+ if ($ dir ) {
420+ $ toolbar ->setDefaultDirection ($ dir );
421+ }
422+ $ modes = $ this ->getModes ();
423+ if ($ modes ) {
424+ $ toolbar ->setModes ($ modes );
425+ }
426+ // set collection to toolbar and apply sort
427+ $ toolbar ->setCollection ($ collection );
428+ $ this ->setChild ('toolbar ' , $ toolbar );
429+ }
382430}
0 commit comments