使用Magento的后台办公室,保存链接到许多产品的类别后,只保留前1000个产品(或2000或x000,具体取决于主机配置)。
从数据库中的catalog_category_product
表中删除所有其他类别/产品链接
即使在产品网格(“类别产品”选项卡)中未选中任何复选框,也会发生这种情况。
不显示或记录错误信息 跟踪从浏览器发布到服务器的数据不会显示任何缺少的产品ID,但即使您检查后台网格上的更多产品,最终保存类别时,服务器仅保留第一个x000并删除其他ID。 ..
答案 0 :(得分:27)
虽然可以在其他网站上找到一些答案,但我认为值得在StackOverflow上分享...
问题的根源可以在Mage_Adminhtml_Catalog_CategoryController
中找到,其中saveAction()
方法检索一个大的POST字符串(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_vars
或php.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_CategoryController
,saveAction()
方法中,替换:
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升级中出现进一步的问题......
希望这无论如何都是有用的,在我找到为什么有些类别会丢失某些产品之前,我挣扎了一段时间!
答案 1 :(得分:5)
最好的解决方案是使用观察者事件“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)
答案 2 :(得分:1)
我找到了一个有效的解决方案,所以我在这里分享它!
在php5.ini
文件夹中创建文件名magento root
,然后在第一行放置此代码
[PHP]
max_input_vars = 5000
现在如果它不起作用,你需要在SERVER PHP,ini(/usr/local/lib/php.ini)中添加这一行
max_input_vars = 5000
希望它有所帮助。
答案 3 :(得分:0)
<IfModule mod_php5.c>
php_value max_input_vars 10000
</IfModule>
为我做了诀窍,我遇到了与上述相同的问题,Magento 1.4.1.1只在类别中保存了1001个产品,但在将上面的代码放入我的.htaccess后,错误就消失了。
答案 4 :(得分:0)
.htaccess中不允许使用LocationMatch,它只能放在Apache httpd.conf中
首选的方法是创建一个Apache include而不是直接修改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
我认为这是最优雅的解决方案,不触及Magento Core,适用于服务器上的所有网站,也可以保护前端。
答案 5 :(得分:0)
来自programmeurweb的解决方案2在Magento 1.8.0.0上被窃听!
if (!empty($k) && !empty($v)) {
$products[$k] = $v;
}
应该是:
if (!empty($k)) {
$products[$k] = $v;
}
$ v用于&#39; position&#39;并且不是复选框值= 1(作为当前类别的关联产品复选框)。
我已经测试了这个。 我错了吗?
这让人想起网上永远不会复制和粘贴的代码。
PS:我现在无法评论答案。