【问题标题】:How to allow multiple product selection in Magento widget configuration?如何在 Magento 小部件配置中允许多个产品选择?
【发布时间】:2012-11-21 12:48:46
【问题描述】:

我正在尝试实现嵌入式小部件。管理员将能够配置此小部件并将其嵌入所见即所得编辑器中。众多配置选项中的两个是应显示在前端的产品列表和类别列表。

我想允许使用“adminhtml/catalog_product_widget_chooser”和“adminhtml/catalog_category_widget_chooser”进行此选择。我尝试使用网络上提供的稀疏文档来实现这些小部件,但我设法完成的只是选择一种产品或选择一个类别的实现。我需要多选行为。

据我所知,当前的实现不允许多选。我检查了类和 grid.phtml 模板的代码,它接缝写得很糟糕,并且超出当前的使用意图不可扩展。例如,这是您假设为小部件参数初始化帮助程序块以允许多选的方式:

<helper_block>
    <type>adminhtml/catalog_product_widget_chooser</type>
        <data>
            <button translate="open">
                <open>Select Products...</open>
            </button>
            <use_massaction>1</use_massaction>
        </data>
</helper_block>

但产品选择器是硬编码的,无需使用这部分代码进行大规模操作:

public function prepareElementHtml(Varien_Data_Form_Element_Abstract $element)
{
    $uniqId = Mage::helper('core')->uniqHash($element->getId());
    $sourceUrl = $this->getUrl('*/catalog_product_widget/chooser', array(
        'uniq_id' => $uniqId,
        'use_massaction' => false,
    ));
    ...

并且应该有某种按钮来确认多项选择的 grid.phtml 模板只显示“搜索”和“重置过滤器”按钮。并且没有处理添加另一个按钮。例如这里是负责打印按钮 html 的默认代码:

public function getMainButtonsHtml()
{
    $html = '';
    if($this->getFilterVisibility()){
        $html.= $this->getResetFilterButtonHtml();
        $html.= $this->getSearchButtonHtml();
    }
    return $html;
}

默认情况下只打印这两个按钮。

所以我基于上面提到的两个实现开始了我自己的实现,它变得越来越丑陋,最终可能会变成一团无法维护的复制意大利面。而且我的工作原则是,如果事情开始变得难看,那我就做错了。

那么有没有一种直接的方法可以通过使用网格小部件在小部件配置屏幕上实现多产品和多类别选择?

【问题讨论】:

    标签: php magento-1.6 magento


    【解决方案1】:

    我找到了一种使用基于adminhtml/system_config_source_category 的源模型在小部件参数上获取类别多选的快速方法。我删除了根级过滤器并为子类别添加了缩进。

    widget.xml:

    <widgets>
        <my_widget type="mymodule/block" translate="name" module="mymodule">
            <name>Widget with Multiselect Categories</name>
            <parameters>
                <category_ids translate="label description">
                    <visible>1</visible>
                    <required>1</required>
                    <label>Categories</label>
                    <type>multiselect</type>
                    <source_model>mymodule/system_config_source_category</source_model>
                </category_ids>
            </parameters>
        </my_widget>
    </widgets>
    

    源模型:

    class Mynamespace_Mymodule_Model_System_Config_Source_Category
    {
        public function toOptionArray()
        {
            $collection = Mage::getResourceModel('catalog/category_collection');
    
            $collection->addAttributeToSelect('name')
                ->addFieldToFilter('path', array('neq' => '1'))
                ->load();
    
            $options = array();
    
            foreach ($collection as $category) {
                $depth = count(explode('/', $category->getPath())) - 2;
                $indent = str_repeat('-', max($depth * 2, 0));
                $options[] = array(
                   'label' => $indent . $category->getName(),
                   'value' => $category->getId()
                );
            }
    
            return $options;
        }
    }
    

    结果:

    来源:http://www.magentocommerce.com/knowledge-base/entry/tutorial-creating-a-magento-widget-part-2

    【讨论】:

    • 请说明:源代码是否应该放在文件:Mymodule/Model/System/Config/Source/Category.php
    【解决方案2】:

    我已经添加了这个问题的答案。 Implement multiple product chooser widget Magento

    我已经检查了https://github.com/dio5/magento-multiproducts-widget 下的模块。

    使用 FORK 选项而不是下载 ZIP。

    它有效并为我们提供了准确的结果,即 WIDGET 中的多个产品选择。如果有任何错误,请告诉我。

    让我知道这是否适合你。

    谢谢!

    [编辑我之前的评论,直接在此处请求代码]

    /命名空间/模块名/etc/widget.xml

    <widgets>
      <catalog_product_multiproducts type="namespace_modulename/widget_catalog_product_multiproducts" translate="name description" module="namespace_modulename">
        <name>Catalog Multiple Products Widget</name>
        <description>Select multiple products for display</description>
        <parameters>
            <title translate="label">
                <visible>1</visible>
                <label>Title</label>
                <type>text</type>
            </title>
            <products_count translate="label">
                <visible>1</visible>
                <required>1</required>
                <label>No of Products</label>
                <type>text</type>
            </products_count>
            <ids translate="label">
                <visible>1</visible>
                <required>1</required>
                <label>Products</label>
                <type>label</type>
                <helper_block>
                    <type>namespace_modulename/adminhtml_catalog_product_widget_multiproducts_chooser</type>
                    <data>
                        <button translate="open">
                            <open>Select Products...</open>
                        </button>
                    </data>
                </helper_block>
                <sort_order>10</sort_order>
            </ids>
            <template translate="label description">
                <required>1</required>
                <visible>1</visible>
                <label>Product Carousel Template</label>
                <type>text</type>
                <value>catalog/product/widget/products_carousel.phtml</value>
                <values>
                    <default translate="label">                                                       <value>catalog/product/widget/products_carousel.phtml</value>
                        <label>New Products Grid Template</label>
                    </default>
                    <list translate="label">
                        <value>catalog/product/widget/new/content/new_list.phtml</value>
                        <label>New Products List Template</label>
                    </list>
                </values>
                <description>Template path cannot be changed/updated</description>
            </template>       
        </parameters>
      </catalog_product_multiproducts>
    </widgets>
    

    /NameSpace/ModuleName/Block/Adminhtml/Catalog/Product/MultiProducts/Chooser.php

    此函数将调用 DOCHOOSE() 函数,该函数将帮助“选择”选中/选定的产品。

    /**
     * prepare layout for products grid
     * 
     * @return type Mage_Adminhtml_Block_Catalog_Product_Widget_Chooser
     */
     protected function _prepareLayout()
     {
        $this->setChild('choose_button', $this->getLayout()->createBlock('adminhtml/widget_button')
                        ->setData(array(
                            'label' => Mage::helper('adminhtml')->__('Choose Selected Products'),
                            'onclick' => $this->getJsObjectName() . '.doChoose()'
                        ))
        );
        return parent::_prepareLayout();
    }
    

    需要使用以下函数来准备产品元素的 HTML,格式为 {1}{2}

    /**
     * Prepare chooser element HTML
     *
     * @param Varien_Data_Form_Element_Abstract $element Form Element
     * @return Varien_Data_Form_Element_Abstract
     */
    public function prepareElementHtml(Varien_Data_Form_Element_Abstract $element)
    {
        $uniqueId = Mage::helper('core')->uniqHash($element->getId());
    
        $sourceUrl = $this->getUrl('*/multiproducts/chooser', array(
            'uniq_id' => $uniqueId,
            'use_massaction' => true,
        ));
    
        $chooser = $this->getLayout()->createBlock('widget/adminhtml_widget_chooser')
                ->setElement($element)
                ->setTranslationHelper($this->getTranslationHelper())
                ->setConfig($this->getConfig())
                ->setFieldsetId($this->getFieldsetId())
                ->setSourceUrl($sourceUrl)
                ->setUniqId($uniqueId);
    
        if ($element->getValue())
        {
            $label = "";
            $ids = explode('}{', $element->getValue());
            $cleanIds = array();
            foreach ($ids as $id)
            {
                $id = str_replace('{', '', $id);
                $id = str_replace('}', '', $id);
                $cleanIds[] = $id;
            }
    
            $products = $this->_getProductsByIDs($cleanIds);
    
            if ($products)
            {
                $label .= '<ul>';
                foreach ($products as $product)
                {
                    $label .= '<li>' . $product->getName() . '</li>';
                }
                $label .= '</ul>';
                $chooser->setLabel($label);
            }
        }
    
        $element->setData('after_element_html', $chooser->toHtml());
    
        return $element;
    }
    

    选中/取消选中复选框的 JS

    /**
     * Checkbox Check JS Callback
     *
     * @return string
     */
    public function getCheckboxCheckCallback()
    {
        if ($this->getUseMassaction())
        {
            return "function (grid, element) {
                $(grid.containerId).fire('product:changed', {element: element});                 
            }";
        }
    }
    

    点击/检查/选择的行/产品的 JS

    /**
     * Grid Row JS Callback
     *
     * @return string
     */
    public function getRowClickCallback()
    {
        if (!$this->getUseMassaction())
        {
            $chooserJsObject = $this->getId();
            return '
                function (grid, event) {
                    var trElement = Event.findElement(event, "tr");
                    var productId = trElement.down("td").innerHTML;
                    var productName = trElement.down("td").next().next().innerHTML;
                    var optionLabel = productName;
                    var optionValue = "product/" + productId.replace(/^\s+|\s+$/g,"");
                    if (grid.categoryId) {
                        optionValue += "/" + grid.categoryId;
                    }
                    if (grid.categoryName) {
                        optionLabel = grid.categoryName + " / " + optionLabel;
                    }
                    ' . $chooserJsObject . '.setElementValue(optionValue);
                    ' . $chooserJsObject . '.setElementLabel(optionLabel);
                    ' . $chooserJsObject . '.close();
                }
            ';
        }
    }
    

    JS 代码,如果用户有兴趣从特定类别中选择产品。

    /**
     * Category Tree node onClick listener js function
     *
     * @return string
     */
    public function getCategoryClickListenerJs()
    {
        $js = '
            function (node, e) {
                {jsObject}.addVarToUrl("category_id", node.attributes.id);
                {jsObject}.reload({jsObject}.url);
                {jsObject}.categoryId = node.attributes.id != "none" ? node.attributes.id : false;
                {jsObject}.categoryName = node.attributes.id != "none" ? node.text : false;
            }
        ';
    
        $js = str_replace('{jsObject}', $this->getJsObjectName(), $js);
    
        return $js;
    }
    

    用于准备带有产品 ID 的 POST 元素的附加 JS。

    /**
     * return additional JS for controls
     * 
     * @return JS
     */
    public function getAdditionalJavascript()
    {
        $chooserJsObject = $this->getId();
    
        $js = '    
            {jsObject}.initChecked = function() {
                $$("#' . $chooserJsObject . '_table tbody input:checkbox").each(function(element, i) {
                    var values = ' . $chooserJsObject . '.getElementValue();
                    var capture = values.replace("{"+element.value+"}", "match");                    
                    var searchValue = "match";
    
                    if(capture.search(searchValue) != -1)
                    {
                        element.checked = true;
                    }
                });
            }
    
            {jsObject}.initChecked();
    
            var values = ' . $chooserJsObject . '.getElementValue();
    
            $("' . $chooserJsObject . '").insert({bottom: "<div class=\"filter\"><input type=\"hidden\" value=\"+values+\" name=\"selected_products\" /></div>"});
    
            $$("#' . $chooserJsObject . '_table tbody input:checkbox").invoke("observe", "change", function(event) {
                var element = Event.element(event);
                var label = element.up("td").next().next().next().innerHTML;
                label = label.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
                if(element.checked)
                {
                    {jsObject}.addValue(element.value);
                    {jsObject}.addLabel(label);
                } else {
                    {jsObject}.removeValue(element.value);
                    {jsObject}.removeLabel(label);
                }
            });
    
            {jsObject}.removeValue = function(value) {
                var currentValue =  ' . $chooserJsObject . '.getElementValue();
                currentValue =  currentValue.replace("{"+value+"}", "");
                ' . $chooserJsObject . '.setElementValue(currentValue);
            }
    
            {jsObject}.addValue = function(value) {
                var currentValue = ' . $chooserJsObject . '.getElementValue();
                currentValue = currentValue.replace("{"+value+"}", "");
                currentValue = currentValue + "{"+value+"}";
                ' . $chooserJsObject . '.setElementValue(currentValue);
            }
    
            {jsObject}.removeLabel = function(label) {
                var currentLabel =  ' . $chooserJsObject . '.getElementLabelText();
                currentLabel = currentLabel.replace("<li>"+label+"</li>", "");
                ' . $chooserJsObject . '.setElementLabel(currentLabel);         
            }
    
            {jsObject}.addLabel = function(label) {
                var currentLabel = ' . $chooserJsObject . '.getElementLabelText();
                if(currentLabel.search("ul") != -1)
                {
                    currentLabel = currentLabel.replace("</ul>", "");
                    currentLabel = currentLabel.replace("<li>"+label+"</li>", "");
                } else {
                    currentLabel = "<ul>";
                }    
                currentLabel = currentLabel +"<li>"+label+"</li></ul>";
                ' . $chooserJsObject . '.setElementLabel(currentLabel);
            }
    
            {jsObject}.doChoose = function(node,e) {
                ' . $chooserJsObject . '.close();
            }
        ';
    
        $js = str_replace('{jsObject}', $this->getJsObjectName(), $js);
    
        return $js;
    }
    

    以上是主要功能,可帮助您在弹出窗口中从 GRID 中选择多个产品。

    还有更多代码可以在这里查看:https://github.com/dio5/magento-multiproducts-widget

    步骤:

    1. 在管理面板中导航到 CMS 页面
    2. 在所见即所得编辑器中单击“插入小部件”
    3. 选择小部件类型 - 目录多个产品小部件
    4. 输入标题、产品数量
    5. 选择模板(可以根据需要添加任意数量的模板)
    6. 点击“选择产品”按钮
    7. 从 GRID 中选择产品
    8. 点击“选择产品”按钮

    希望这对某人有所帮助!

    快乐编码...

    【讨论】:

    • 请在您的答案中分享代码,而不是链接到它们,至少是基本部分。
    • 已尝试编辑我的答案并提供主要代码块。这可能对前进的人有所帮助。谢谢!
    • 此代码运行正常。但是它有一个问题,例如在小部件左侧我们无法使用类别制作过滤产品。
    【解决方案3】:

    看起来您不是第一个为此开发自己的实现的人。

    David Manners 似乎用他的Manners_Widgets 解决了同样的问题。

    Manners_Widgets 扩展的特点:

    • 产品和类别多选

    我没有与 David 联系过,也没有使用过这个解决方案,所以无法评论这个代码的质量(或完整性)......但如果你还没有看到这个解决方案,它可能会节省一些时间(或者在最坏的情况下,给你一个联系人,以便在这个问题上进行合作)。

    希望对你有帮助,祝你好运!

    【讨论】:

    • 是的,我开始研究这个问题,但遗憾的是最近没有太多时间或运气 :( 如果有人愿意看一看并随时提供帮助,它在 gitHub 上。如果我发现我会在这里更新解决方案
    • 我刚刚尝试了 Magento 1.9x 上的扩展。选择掩码来自产品列表,具有“更改状态”和“更改属性”等按钮,但没有实际的“选择”按钮。
    【解决方案4】:

    这里有一个快速解决方法:不要使用产品小部件选择器,而是使用允许以逗号分隔的 SKU 的文本字段。

    然后在您的代码中分解 sku 并按 sku 获取产品。将其返回到您的模板。容易得多:)

    【讨论】:

      【解决方案5】:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-05
        • 1970-01-01
        • 2017-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多