Skip to content

Commit 0328a45

Browse files
author
Yaroslav Onischenko
authored
Merge pull request #829 from magento-dragons/DRAGONS-BUGFIXES-JAN
[Dragons] Bug fixes
2 parents 7c81c1d + 8e48823 commit 0328a45

File tree

43 files changed

+1374
-268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1374
-268
lines changed

app/code/Magento/Catalog/Block/Product/ListProduct.php

+121-73
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
namespace Magento\Catalog\Block\Product;
88

99
use Magento\Catalog\Api\CategoryRepositoryInterface;
10+
use Magento\Catalog\Block\Product\ProductList\Toolbar;
1011
use Magento\Catalog\Model\Category;
1112
use Magento\Catalog\Model\Product;
13+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
1214
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
1315
use Magento\Framework\Exception\NoSuchEntityException;
1416
use 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
}

app/code/Magento/Catalog/Model/Attribute/Backend/Startdate.php

-6
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ protected function _getValueForSave($object)
4444
{
4545
$attributeName = $this->getAttribute()->getName();
4646
$startDate = $object->getData($attributeName);
47-
if ($startDate === false) {
48-
return false;
49-
}
50-
if ($startDate == '' && $object->getSpecialPrice()) {
51-
$startDate = $this->_localeDate->date();
52-
}
5347

5448
return $startDate;
5549
}

app/code/Magento/Catalog/Model/ProductRepository.php

+27-12
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ protected function getCacheKey($data)
289289
*/
290290
protected function initializeProductData(array $productData, $createNew)
291291
{
292+
unset($productData['media_gallery']);
292293
if ($createNew) {
293294
$product = $this->productFactory->create();
294295
if ($this->storeManager->hasSingleStore()) {
@@ -437,8 +438,15 @@ private function processLinks(\Magento\Catalog\Api\Data\ProductInterface $produc
437438
}
438439

439440
/**
440-
* @param ProductInterface $product
441-
* @param array $mediaGalleryEntries
441+
* Process Media gallery data before save product.
442+
*
443+
* Compare Media Gallery Entries Data with existing Media Gallery
444+
* * If Media entry has not value_id set it as new
445+
* * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag
446+
* * Merge Existing and new media gallery
447+
*
448+
* @param ProductInterface $product contains only existing media gallery items
449+
* @param array $mediaGalleryEntries array which contains all media gallery items
442450
* @return $this
443451
* @throws InputException
444452
* @throws StateException
@@ -448,11 +456,10 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE
448456
{
449457
$existingMediaGallery = $product->getMediaGallery('images');
450458
$newEntries = [];
459+
$entriesById = [];
451460
if (!empty($existingMediaGallery)) {
452-
$entriesById = [];
453461
foreach ($mediaGalleryEntries as $entry) {
454-
if (isset($entry['id'])) {
455-
$entry['value_id'] = $entry['id'];
462+
if (isset($entry['value_id'])) {
456463
$entriesById[$entry['value_id']] = $entry;
457464
} else {
458465
$newEntries[] = $entry;
@@ -461,6 +468,9 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE
461468
foreach ($existingMediaGallery as $key => &$existingEntry) {
462469
if (isset($entriesById[$existingEntry['value_id']])) {
463470
$updatedEntry = $entriesById[$existingEntry['value_id']];
471+
if ($updatedEntry['file'] === null) {
472+
unset($updatedEntry['file']);
473+
}
464474
$existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
465475
} else {
466476
//set the removed flag
@@ -488,11 +498,18 @@ protected function processMediaGallery(ProductInterface $product, $mediaGalleryE
488498
}
489499
/** @var ImageContentInterface $contentDataObject */
490500
$contentDataObject = $this->contentFactory->create()
491-
->setName($newEntry['content'][ImageContentInterface::NAME])
492-
->setBase64EncodedData($newEntry['content'][ImageContentInterface::BASE64_ENCODED_DATA])
493-
->setType($newEntry['content'][ImageContentInterface::TYPE]);
501+
->setName($newEntry['content']['data'][ImageContentInterface::NAME])
502+
->setBase64EncodedData($newEntry['content']['data'][ImageContentInterface::BASE64_ENCODED_DATA])
503+
->setType($newEntry['content']['data'][ImageContentInterface::TYPE]);
494504
$newEntry['content'] = $contentDataObject;
495505
$this->processNewMediaGalleryEntry($product, $newEntry);
506+
507+
$finalGallery = $product->getData('media_gallery');
508+
$newEntryId = key(array_diff_key($product->getData('media_gallery')['images'], $entriesById));
509+
$newEntry = array_replace_recursive($newEntry, $finalGallery['images'][$newEntryId]);
510+
$entriesById[$newEntryId] = $newEntry;
511+
$finalGallery['images'][$newEntryId] = $newEntry;
512+
$product->setData('media_gallery', $finalGallery);
496513
}
497514
return $this;
498515
}
@@ -520,8 +537,6 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO
520537
$productDataArray = $this->extensibleDataObjectConverter
521538
->toNestedArray($product, [], \Magento\Catalog\Api\Data\ProductInterface::class);
522539
$productDataArray = array_replace($productDataArray, $product->getData());
523-
unset($productDataArray['media_gallery']);
524-
525540
$ignoreLinksFlag = $product->getData('ignore_links_flag');
526541
$productLinks = null;
527542
if (!$ignoreLinksFlag && $ignoreLinksFlag !== null) {
@@ -531,8 +546,8 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO
531546
$product = $this->initializeProductData($productDataArray, empty($existingProduct));
532547

533548
$this->processLinks($product, $productLinks);
534-
if (isset($productDataArray['media_gallery_entries'])) {
535-
$this->processMediaGallery($product, $productDataArray['media_gallery_entries']);
549+
if (isset($productDataArray['media_gallery'])) {
550+
$this->processMediaGallery($product, $productDataArray['media_gallery']['images']);
536551
}
537552

538553
if (!$product->getOptionsReadonly()) {

app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php

+21-5
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,30 @@ protected function _prepareFinalPriceData($entityIds = null)
235235
* @param string|null $type product type, all if null
236236
* @return $this
237237
* @throws \Magento\Framework\Exception\LocalizedException
238-
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
239238
*/
240239
protected function prepareFinalPriceDataForType($entityIds, $type)
241240
{
242241
$this->_prepareDefaultFinalPriceTable();
242+
243+
$select = $this->getSelect($entityIds, $type);
244+
$query = $select->insertFromSelect($this->_getDefaultFinalPriceTable(), [], false);
245+
$this->getConnection()->query($query);
246+
return $this;
247+
}
248+
249+
/**
250+
* Forms Select for collecting price related data for final price index table
251+
* Next types of prices took into account: default, special, tier price
252+
* Moved to protected for possible reusing
253+
*
254+
* @param int|array $entityIds Ids for filtering output result
255+
* @param string|null $type Type for filtering output result by specified product type (all if null)
256+
* @return \Magento\Framework\DB\Select
257+
* @throws \Magento\Framework\Exception\LocalizedException
258+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
259+
*/
260+
protected function getSelect($entityIds = null, $type = null)
261+
{
243262
$metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
244263
$connection = $this->getConnection();
245264
$select = $connection->select()->from(
@@ -371,10 +390,7 @@ protected function prepareFinalPriceDataForType($entityIds, $type)
371390
'store_field' => new \Zend_Db_Expr('cs.store_id'),
372391
]
373392
);
374-
375-
$query = $select->insertFromSelect($this->_getDefaultFinalPriceTable(), [], false);
376-
$connection->query($query);
377-
return $this;
393+
return $select;
378394
}
379395

380396
/**

0 commit comments

Comments
 (0)