代码之家  ›  专栏  ›  技术社区  ›  Thomas Müller

如何在Magento产品列表中以每种颜色显示可配置产品?

  •  4
  • Thomas Müller  · 技术社区  · 14 年前

    我有一个可配置的产品,有很多不同的颜色和尺寸。我希望可配置的产品对每种颜色都显示一次。我的想法是将每种颜色的可配置产品的一个简单产品分配给可配置产品的类别。然后我想更改列表,以便(彩色)简单产品链接到它的主产品(可配置产品)。

    另一种方法是,将可配置产品分配到一个类别,然后用不同的颜色多次列出。但我认为这会非常复杂。

    解决方案

    真的,我的密码丢了。但我是如何做到这一点的:

    1. 设置 visibility 对于所有从属产品 catalog 所以他们 出现在产品列表中
    2. 覆盖产品模型及其getProductUrl函数:
        public function getProductUrl($useSid = null)
        {
        $product = $this;
        $product->loadParentProductIds();
            $parentIds = $product->getParentProductIds();
    
        if(count($parentIds) > 0 && $product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_SIMPLE)
        {
                $parent = Mage::getModel("catalog/product")->setId($parentIds[0])->load();
                return $this->getUrlModel()->getProductUrl($parent, $useSid);
        }
    
        return $this->getUrlModel()->getProductUrl($product, $useSid);
        }
    

    这样,每个从属产品都链接到它的主产品。棘手的部分是将属性附加到url。你可以加上 #attributecode1=value1&attributecode2=value2 指向url以预选属性选择框。我只有这部分快速&脏兮兮的,我相信有人能做得更好。

    预选示例:

    http://demo.magentocommerce.com/anashria-womens-premier-leather-sandal-7.html http://demo.magentocommerce.com/anashria-womens-premier-leather-sandal-7.html#502=43

    2 回复  |  直到 3 年前
        1
  •  1
  •   Antti Ranta    5 年前

    如果其他人需要在Magento 2中执行此操作,请坏死此线程。

    下面是我的解决方案。请记住,它是黑客的,会破坏很多东西,所以只有当你是一个Magento开发人员,知道他/她在做什么,可以修复或生活在这个代码的负面影响。

    登记php

    <?php
    use \Magento\Framework\Component\ComponentRegistrar;
    
    ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Antti_ConfigurableProductSplitter', __DIR__);
    

    etc/模块。xml

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
        <module name="Antti_ConfigurableProductSplitter" >
            <sequence>
                <module name="Magento_Catalog" />
            </sequence>
        </module>
    </config>
    

    etc/前端/事件。xml

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
        <event name="catalog_block_product_list_collection">
            <observer name="cps_catalog_block_product_list_collection" instance="Antti\ConfigurableProductSplitter\Observer\CatalogBlockProductCollectionBeforeToHtmlObserver" shared="false" />
        </event>
        <event name="cps_product_data_merge_after">
            <observer name="cps_product_data_merge_after" instance="Antti\ConfigurableProductSplitter\Observer\SetColorPreselectedAfterProductDataMerge" shared="false" />
        </event>
    </config>
    

    Observer/CatalogBlockProductCollectionBeforeToHtmlObserver。php

    <?php
    namespace Antti\ConfigurableProductSplitter\Observer;
    
    use Magento\Framework\Event\ObserverInterface;
    use Antti\ConfigurableProductSplitter\Model\ProductCollectionSplitter;
    
    class CatalogBlockProductCollectionBeforeToHtmlObserver implements ObserverInterface
    {
        /**
         * @var ProductCollectionSplitter
         */
        private $productSplitter;
    
        /**
         * CatalogBlockProductCollectionBeforeToHtmlObserver constructor.
         *
         * @param ProductCollectionSplitter $productSplitter
         */
        public function __construct(
            ProductCollectionSplitter $productSplitter
        ) {
            $this->productSplitter = $productSplitter;
        }
    
        /**
         * @param \Magento\Framework\Event\Observer $observer
         *
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function execute(\Magento\Framework\Event\Observer $observer)
        {
            $productCollection = $observer->getEvent()->getCollection();
            if ($productCollection instanceof \Magento\Framework\Data\Collection) {
                if (!$productCollection->isLoaded()) {
                    $productCollection->load();
                }
                $this->productSplitter->splitConfigurables($productCollection);
            }
    
            return $this;
        }
    }
    

    Observer/SetColorPreselectedAfterProductDataMerge。php

    <?php
    namespace Antti\ConfigurableProductSplitter\Observer;
    
    use Magento\Framework\Event\ObserverInterface;
    use Magento\Catalog\Api\Data\ProductInterface;
    use Magento\Eav\Model\Config as EavConfig;
    
    class SetColorPreselectedAfterProductDataMerge implements ObserverInterface
    {
        /**
         * @var EavConfig
         */
        private $eavConfig;
    
        /**
         * ProductDataMerger constructor.
         *
         * @param EavConfig $eavConfig
         */
        public function __construct(
            EavConfig $eavConfig
        ) {
            $this->eavConfig = $eavConfig;
        }
    
        /**
         * @param \Magento\Framework\Event\Observer $observer
         *
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function execute(\Magento\Framework\Event\Observer $observer)
        {
            $product = $observer->getEvent()->getSimple();
            $merged = $observer->getEvent()->getMerged();
    
            $this->setColorPreselected($merged, $product->getColor());
    
            return $this;
        }
    
        /**
         * @param ProductInterface $product
         * @param int $color
         *
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        private function setColorPreselected(ProductInterface &$product, int $color)
        {
            $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'color');
    
            $preconfiguredValues = new \Magento\Framework\DataObject();
            $preconfiguredValues->setData('super_attribute', [$attribute->getId() => $color]);
            $product->setPreconfiguredValues($preconfiguredValues);
    
            // TODO: should test whether this works if there is no url rewrite
            $product->setRequestPath(sprintf('%s#%d=%d', $product->getRequestPath(), $attribute->getId(), $color));
        }
    }
    

    模型/产品数据合并。php

    <?php
    namespace Antti\ConfigurableProductSplitter\Model;
    
    use Magento\Catalog\Api\Data\ProductInterface;
    use Magento\Framework\EntityManager\EventManager;
    
    class ProductDataMerger
    {
        /**
         * @var EventManager
         */
        private $eventManager;
    
        /**
         * @param EventManager $eventManager
         */
        public function __construct(
            EventManager $eventManager
        ) {
            $this->eventManager = $eventManager;
        }
    
        /**
         * @param ProductInterface $product
         * @param ProductInterface $parentProduct
         *
         * @return ProductInterface
         */
        public function merge(ProductInterface $product, ProductInterface $parentProduct)
        {
            $merged = clone $parentProduct;
            $merged->setParentId($merged->getId());
            $merged->setId($product->getId());
    
            $this->setImageFromChildProduct($merged, $product);
    
            $this->eventManager->dispatch(
                'cps_product_data_merge_after',
                ['merged' => $merged, 'simple' => $product, 'configurable' => $parentProduct]
            );
    
            return $merged;
        }
    
        /**
         * @param ProductInterface $product
         * @param ProductInterface $childProduct
         */
        public function setImageFromChildProduct(ProductInterface &$product, ProductInterface $childProduct)
        {
            foreach (['image', 'small_image', 'thumbnail'] as $imageType) {
                if ($childProduct->getData($imageType) && $childProduct->getData($imageType) !== 'no_selection') {
                    $product->setData($imageType, $childProduct->getData($imageType));
                } else {
                    $product->setData($imageType, $childProduct->getData('image'));
                }
            }
        }
    }
    

    型号/产品集合拆分器。php

    <?php
    namespace Antti\ConfigurableProductSplitter\Model;
    
    use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
    use Antti\ConfigurableProductSplitter\Model\ProductDataMerger;
    use Magento\Catalog\Model\Layer\Resolver;
    
    class ProductCollectionSplitter
    {
        /**
         * @var \Magento\Catalog\Model\Layer
         */
        private $catalogLayer;
    
        /**
         * @var ProductDataMerger
         */
        private $productDataMerger;
    
        /**
         * ProductCollectionSplitter constructor.
         *
         * @param Resolver $layerResolver
         * @param ProductDataMerger $productDataMerger
         */
        public function __construct(
            Resolver $layerResolver,
            ProductDataMerger $productDataMerger
        ) {
            $this->catalogLayer = $layerResolver->get();
            $this->productDataMerger = $productDataMerger;
        }
    
        /**
         * @param \Magento\Framework\Data\Collection $collection
         *
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function splitConfigurables(\Magento\Framework\Data\Collection $collection)
        {
            $items = $collection->getItems();
    
            if (sizeof($items) == 0) {
                return $this;
            }
    
            $configurables = $otherProducts = [];
    
            $colorFilterValue = $this->getCurrentColorFilterValue();
    
            foreach ($items as $index => $product) {
                if ($product->getTypeId() === Configurable::TYPE_CODE) {
                    /** @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection $childProducts */
                    $childProducts = $product->getTypeInstance()->getUsedProductCollection($product);
                    if ($colorFilterValue !== null) {
                        $childProducts->addAttributeToFilter('color', ['eq' => $colorFilterValue]);
                    }
                    $childProducts->groupByAttribute('color');
    
                    foreach ($childProducts as $childProduct) {
                        $childProduct->setParentId($product->getId());
                        $otherProducts[] = $childProduct;
                    }
    
                    $configurables[$product->getId()] = $product;
                } else {
                    $otherProducts[] = $product;
                }
    
                $collection->removeItemByKey($index);
            }
    
            foreach ($otherProducts as $product) {
                if ($product->getParentId() && isset($configurables[$product->getParentId()])) {
                    $product = $this->productDataMerger->merge($product, $configurables[$product->getParentId()]);
                }
                $collection->addItem($product);
            }
    
            return $this;
        }
    
        /**
         * @return string|null
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        private function getCurrentColorFilterValue()
        {
            /** @var \Magento\Catalog\Model\Layer\Filter\Item $filter */
            foreach ($this->catalogLayer->getState()->getFilters() as $filter) {
                if($filter->getFilter()->getAttributeModel()->getName() == 'color') {
                    return $filter->getValueString();
                }
            }
    
            return null;
        }
    }
    

    已知问题:

    • 由于加载后修改了集合项目,因此集合计数将为 无效,这可能会在其他地方引起问题。
    • 此外,集合中的产品ID将无效,因为可配置项的ID将被简单产品的ID替换。
    • 如果集合将再次加载到其他地方,则可配置产品不会被拆分。
    • 产品每页限制器不再工作。
    • 产品列表上的产品计数无效。
    • Klevu等第三方搜索模块可能无法工作
    • 其他第三方模块的实现可能存在问题。
    • 产品库存数据、评论等在产品列表中被打破(尽管在前端可能很容易修复)。
    • 不确定在没有url重写的情况下实现是否可以工作(不过应该很容易修复)。
    • 其他我可能不知道的问题。
        2
  •  2
  •   Prattski    14 年前

    我不明白你为什么不根据每种颜色的大小来制作可配置的产品?这样你就不需要像Magento那样进行黑客攻击了。

    如果您使一个简单的产品成为可配置产品的一部分,并在前端可见,那么如果它是可配置产品的一部分,它将不会链接到可配置产品(如您所发现的)。这对你来说也没什么意义,因为如果你的可配置产品是基于尺寸和颜色的,那么简单的产品将有一个设定的尺寸和设定的颜色。

    如果你只是为每种衬衫颜色制作了一个可配置的产品,你就可以做到功能齐全、无黑客攻击。然后,你也可以使用相关产品展示其他衬衫颜色。

    黑客攻击越少越好。这是我的意见。

        3
  •  0
  •   Syntax Error    14 年前

    一种方法是将尺寸和颜色作为目录号的一部分(或用于产品的任何唯一标识号)

    假设你有一个小部件,有两种颜色和三种尺寸,它的目录号是“qwe123”。您可以在系统中输入以下两项,以及相应的图像。我想你已经有办法处理这些尺寸了。

    qwe123-red
    qwe123-blue

    这样做不需要额外的编程,但是如果你想链接到产品页面上提供的其他颜色,那么你必须解析出目录号的第一部分,并搜索匹配的颜色。

        4
  •  0
  •   Victor S.    3 年前

    为了将简单产品重定向到可配置的父产品,您可以创建 Plugin (Interceptor) 对于 Magento\Catalog\Model\Product::getProductUrl() ,更改简单产品的URL的位置:

    if ($product->getTypeId() === 'simple') {
        /*Get the configurable parent product URL and assign it to a simple product.*/
    }
    

    要在可配置产品中预选简单产品,简单产品的地址应如下所示,例如:

    /mona-pullover-hoodlie.html#143=167&93=53

    哪里

    /mona-pullover-hoodlie.html -可配置的产品URL,

    143 , 93 -属性ID,

    167 , 53 -选项ID。

    属性ID和选项ID可以使用 Magento\ConfigurableProduct\Model\Product\Type\Configurable::getConfigurableAttributesAsArray($product) 作用


    我犯了一个错误 VCT Simple Product URL Magento Marketplace上解决此问题的模块。