且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

保存类别后,Magento为什么只保留类别中的前1000种产品?

更新时间:2023-11-29 23:16:40

尽管可以在其他网站上找到一些答案,但我认为值得在***上分享这一点...

问题的根源可以在Mage_Adminhtml_Catalog_CategoryController中找到,其中的saveAction()方法检索由PHP函数parse_str()处理的较大的POSTed字符串(category_products,类似于查询字符串): /p>

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

A!从PHP 5.3.9版本开始,有一个名为max_input_vars的新配置设置,该设置限制了可以接受的输入变量的数量.
此限制主要适用于$_GET$_POST$_COOKIE超全局变量,但也由parse_str()函数在内部使用!
(请参见 PHP手册)>

取决于主机的php.ini配置,因此链接到类别的产品数受此设置的限制.

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

<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>

(来源)

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

另一种解决方案是修复Magento的代码,以便此时 not 不能使用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;
    }
}

()

很难确定哪种解决方案是***的,因为第一个解决方案使您的服务器更容易受到攻击(因为max_input_vars旨在减轻使用哈希冲突的拒绝服务攻击的可能性),第二个解决方案该解决方案使您可以修改Magento核心类,这可能在将来的Magento升级中导致更多问题……

希望无论如何这都是有用的,我费了一些时间才弄清楚为什么有些类别会不断失去某些产品!

Using Magento's back-office, after saving a category which was linked to many products, only the first 1000 products (or 2000, or x000, depending on host configuration) are kept.

All other category/product links are deleted from the catalog_category_product table in the database.
This occurs even though no checkboxes have been unchecked in the products grid (Category Products tab).

No error message is displayed or logged.
Tracing data posted from the browser to the server doesn't reveal any missing product IDs, but even if you check more products on the back-office grid, when finally saving the category, the server only keeps the first x000 and removes other IDs...

Though some answers can be found on other websites, I thought it was worth sharing this on ***...

The source of the problem can be found in Mage_Adminhtml_Catalog_CategoryController, where the saveAction() method retrieves a big POSTed string (category_products, encoded like a query string), which is processed by PHP function parse_str():

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

Alas! As from version 5.3.9 of PHP, there is a new configuration setting called max_input_vars, which limits the number of input variables that may be accepted.
This limit is mainly applied to $_GET, $_POST and $_COOKIE superglobals, but is also used internally by the parse_str() function!
(See PHP manual)

Depending on the php.ini configuration of your host, the number of products linked to a category is therefore limited by this setting...

One solution is to increase the value of max_input_vars, in php.ini or in .htaccess:

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

This can even be done more safely by changing this setting only for admin pages (adapt the LocationMatch pattern to your own back-office URL style):

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

(Source)

This only solves the problem until one of your categories reaches the new max number of products...

Another solution would be to fix Magento's code in order not to use the parse_str() function at this point.
For instance, in Mage_Adminhtml_Catalog_CategoryController, saveAction() method, replace:

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

with:

$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)

It's hard to decide which solution is the best, since the first one makes your server more vulnerable to attacks (as the max_input_vars is meant to mitigate the possibility of denial of service attacks which use hash collisions), and the second solution makes you modify a Magento core class, which can lead to further problems in future Magento upgrades...

Hope this is useful anyway, I struggled some time before I could find out why some categories kept loosing some products!