【问题标题】:Why does Magento keep only the first 1000 products in a category, after saving the category?为什么 Magento 在保存类别后只保留类别中的前 1000 个产品?
【发布时间】:2013-02-13 10:11:41
【问题描述】:

使用 Magento 的后台,在保存链接到许多产品的类别后,仅保留前 1000 个产品(或 2000 个或 x000 个,取决于主机配置)。

从数据库的catalog_category_product 表中删除所有其他类别/产品链接。
即使在产品网格(类别产品选项卡)中没有取消选中任何复选框,也会发生这种情况。

没有显示或记录错误消息。
跟踪从浏览器发布到服务器的数据不会显示任何丢失的产品 ID,但即使您在后台网格上检查更多产品,当最终保存类别时,服务器只保留第一个 x000 并删除其他 ID。 ..

【问题讨论】:

    标签: php magento magento-1.7


    【解决方案1】:

    虽然可以在其他网站上找到一些答案,但我认为值得在 StackOverflow 上分享这个...

    问题的根源可以在Mage_Adminhtml_Catalog_CategoryController找到,其中saveAction()方法检索到一个大的POSTed字符串(category_products,编码为查询字符串),由PHP函数parse_str()处理:

    if (isset($data['category_products']) && !$category->getProductsReadonly()) {
        $products = array();
        parse_str($data['category_products'], $products);
        $category->setPostedProducts($products);
    }
    

    唉!从 PHP 5.3.9 版本开始,有一个名为 max_input_vars 的新配置设置,它限制了可以接受的输入变量的数量。
    此限制主要应用于$_GET$_POST$_COOKIE 超全局变量,但也被parse_str() 函数内部使用!
    (见PHP manual

    根据您主机的php.ini 配置,链接到一个类别的产品数量因此受到此设置的限制...

    一种解决方案是增加max_input_varsphp.ini.htaccess中的值:

    <IfModule mod_php5.c>
        php_value max_input_vars 10000
    </IfModule>
    

    这甚至可以通过仅为管理页面更改此设置来更安全地完成(使 LocationMatch 模式适应您自己的后台 URL 样式):

    <LocationMatch "mymagentostore/(index\.php/)?admin/">
        <IfModule mod_php5.c>
            php_value max_input_vars 10000
        </IfModule>
    </LocationMatch>
    

    (Source)

    这只能解决问题,直到您的一个类别达到新的最大产品数量...

    另一种解决方案是修复 Magento 的代码,以便此时使用 parse_str() 函数。
    比如在Mage_Adminhtml_Catalog_CategoryControllersaveAction()方法中,替换:

    parse_str($data['category_products'], $products);
    

    与:

    $cat_products_split = explode('&', $data['category_products']);
    foreach($cat_products_split as $row) {
        $arr = array();
        parse_str($row, $arr); //This will always work
        list($k, $v) = each($arr);
        if (!empty($k) && !empty($v)) {
            $products[$k] = $v;
        }
    }
    

    (Source)

    很难确定哪种解决方案最好,因为第一个解决方案会使您的服务器更容易受到攻击(因为max_input_vars 旨在降低使用哈希冲突的拒绝服务攻击的可能性),而第二个解决方案让您修改 Magento 核心类,这可能会导致未来 Magento 升级出现更多问题...

    希望这对我还是有用的,我挣扎了一段时间才弄明白为什么某些类别不断失去一些产品!

    【讨论】:

    • 您认为将其增加到大于 10,000 是否存在问题?我们有一些类别超过 31,000 种产品...
    • 好吧,我想您可以将限制提高到任意数量(但是,请理解这会使您的服务器更容易受到某些 DoS 攻击)。
    • 对于第二种解决方案,您不能只从自定义模块中覆盖控制器。这将使其升级安全,因为核心文件保持未修改。
    • @russjman 在这里查看解决方案 2 的完整实现:​​magento.stackexchange.com/questions/6319/…
    • @programmeurweb:您的解决方案 2 的代码对我来说不能正常工作(不知道为什么,但它将类别大小限制为 527 个产品):我已经“修复”了您的代码(它是几乎等价)
    【解决方案2】:

    最好的解决方案是使用观察者事件“catalog_category_prepare_save”。 因此,您需要更改模块的 config.xml:

    <config>
        <global>
            <events>
                ...
                <catalog_category_prepare_save>
                    <observers>
                        <your_module>
                             <class>your_module/observer</class>
                             <method>onCatalogCategoryPrepareSave</method>
                        </your_module>
                    </observers>
                </catalog_category_prepare_save>
    

    然后将函数添加到app/code/local/Your/Module/Model/Observer.php

    <?php
    class Your_Module_Model_Observer
    {
        /**
         * Fix bug with saving only first 1000 products in a category
         * It's related to default php config 'max_input_vars' = 1000
         *
         * @param Varien_Event_Observer $observer
         */
         public function onCatalogCategoryPrepareSave(Varien_Event_Observer $observer)
         {
             $phpDefaultMaxInputVars = (int)ini_get('max_input_vars');
            /** @var Mage_Core_Controller_Request_Http $request */
            $request = $observer->getRequest();
            $dataProducts = $request->getPost('category_products');
            $dataProducts = preg_split('/&/', $dataProducts, -1, PREG_SPLIT_NO_EMPTY);
            /** @var Mage_Catalog_Model_Category $category */
            $category = $observer->getCategory();
    
            if ($dataProducts && !$category->getProductsReadonly() && count($dataProducts) > $phpDefaultMaxInputVars) {
                $products = array();
                foreach ($dataProducts as $_product) {
                    $_product = trim($_product);
                    if (preg_match('/([0-9]*)=([\-]?[0-9]*)/', $_product, $matches)) {
                        $products[$matches[1]] = $matches[2];
                    }
                }
                $category->setPostedProducts($products);
            }
        }
    }
    

    (Source)

    【讨论】:

      【解决方案3】:

      我找到了一个可行的解决方案,所以我在这里分享它!

      在您的magento root 文件夹中创建一个文件名php5.ini,并在第一行放置此代码

      [PHP]
      max_input_vars = 5000
      

      现在,如果它不起作用,您需要在 SERVER PHP,ini (/usr/local/lib/php.ini) 中添加这一行

      max_input_vars = 5000
      

      希望它有所帮助。

      【讨论】:

        【解决方案4】:

        <IfModule mod_php5.c> php_value max_input_vars 10000 </IfModule>

        为我做了这个伎俩,我遇到了与上述相同的问题,Magento 1.4.1.1 仅在类别中保存了 1001 个产品,但将上面的代码放入我的 .htaccess 后,错误就消失了。

        【讨论】:

          【解决方案5】:

          programurweb 的解决方案 2 在 Magento 1.8.0.0 上存在错误!

          if (!empty($k) && !empty($v)) {
              $products[$k] = $v;
          }
          

          应该是:

          if (!empty($k)) {
              $products[$k] = $v;
          }
          

          $v 用于“位置”,不是复选框值 = 1(将产品关联到当前类别复选框)。

          我已经对此进行了测试。 我错了吗?

          这提醒不要从网上复制和粘贴代码。

          PS:我现在不能评论答案。

          【讨论】:

            【解决方案6】:

            .htaccess中不允许LocationMatch,只能放在A​​pache httpd.conf中

            首选方法是创建一个 Apache 包含而不是直接修改 httpd.conf,因为它会在下一次更新/重建时中断。

            在我的例子中,我创建了一个包含文件 pre_virtualhost_global.conf 并将此代码放入其中:

            <LocationMatch "mymagentostore/(index\.php/)?admin/">
            <IfModule mod_php5.c>
                php_value max_input_vars 10000
            </IfModule>
            

            当然,之后你需要重启apache。我使用了我的 WebHostManager,所以这是一个简单的过程。

            更多信息请访问:

            [http://docs.cpanel.net/twiki/bin/view/EasyApache3/WebHome#Custom Directives to http.conf][1]

            我认为这是最优雅的解决方案,不涉及 Magento Core,适用于您服务器上的所有站点,还可以保护前端。

            [1]: https://web.archive.org/web/20140804033608/http://docs.cpanel.net:80/twiki/bin/view/EasyApache3/WebHome?#Custom 指向 http.conf 的指令

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-05-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-08-21
              相关资源
              最近更新 更多