Почему Magento сохраняет только первые 1000 товаров в категории после сохранения категории?
Используя бэк-офис Magento, после сохранения категории, которая была связана со многими продуктами, сохраняются только первые 1000 продуктов (или 2000, или x000, в зависимости от конфигурации хоста).
Все другие ссылки на категории/продукты удаляются из таблицы catalog_category_product
в базе данных.
Это происходит даже при том, что в сетке товаров (вкладка "Товары категории") не были сняты флажки.
Сообщение об ошибке не отображается и не регистрируется.
Отслеживание данных, отправленных из браузера в сервер не обнаруживает отсутствующих идентификаторов продуктов, но даже если вы проверите больше продуктов в сетке бэк-офиса, при окончательном сохранении категории сервер сохранит только первые x000 и удалит другие идентификаторы...
6 answers
Хотя некоторые ответы можно найти на других сайтах, я подумал, что стоит поделиться этим на StackOverflow...
Источник проблемы можно найти в Mage_Adminhtml_Catalog_CategoryController
, где метод saveAction()
извлекает большую опубликованную строку (category_products
, закодированную как строка запроса), которая обрабатывается функцией PHP parse_str()
:
if (isset($data['category_products']) && !$category->getProductsReadonly()) {
$products = array();
parse_str($data['category_products'], $products);
$category->setPostedProducts($products);
}
Увы! Начиная с версии 5.3.9 PHP, существует новый параметр конфигурации под названием max_input_vars
, который ограничивает количество входных переменных, которые могут быть принято.
Это ограничение в основном применяется к $_GET
, $_POST
и $_COOKIE
суперглобальные, но также используется внутренне функцией parse_str()
!
(См. Руководство по PHP)
В зависимости от конфигурации php.ini
вашего хоста, количество продуктов, связанных с категорией, поэтому ограничено этим параметром...
Одним из решений является увеличение значения max_input_vars
, в php.ini
или в .htaccess
:
<IfModule mod_php5.c>
php_value max_input_vars 10000
</IfModule>
Это даже можно сделать более безопасно, изменив этот параметр только для страниц администратора (адаптируйте шаблон соответствия местоположению к вашему собственному стилю URL-адреса бэк-офиса):
<LocationMatch "mymagentostore/(index\.php/)?admin/">
<IfModule mod_php5.c>
php_value max_input_vars 10000
</IfModule>
</LocationMatch>
( Источник)
Это решает проблему только до тех пор, пока одна из ваших категорий не достигнет нового максимального количества товаров...
Другим решением было бы исправить код 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;
}
}
( Источник)
Трудно решить, какое решение является лучшим, так как первое делает ваш сервер более уязвимым для атак (поскольку max_input_vars
предназначено для снижения вероятности атак типа "отказ в обслуживании", в которых используются коллизии хэшей), а второе решение заставляет вас изменять основной класс Magento, что может привести к дальнейшим проблемам в будущих обновлениях Magento...
Надеюсь, это все равно полезно, я немного боролся время, прежде чем я смог выяснить, почему некоторые категории продолжали терять некоторые товары!
Лучшим решением было бы использовать событие наблюдателя "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);
}
}
}
( Источник)
Я нашел рабочее решение, поэтому делюсь им здесь!
Создайте имя файла php5.ini
в своей папке magento root
и в первой строке просто поместите этот код
[PHP]
max_input_vars = 5000
Теперь, если он не работает, вам нужно будет добавить эту строку в свой СЕРВЕР PHP,ini (/usr/local/lib/php.ini)
max_input_vars = 5000
Надеюсь, это поможет.
<IfModule mod_php5.c>
php_value max_input_vars 10000
</IfModule>
Сделал трюк для меня, у меня была та же проблема, что и описанная выше, Magento 1.4.1.1 сохранил только 1001 продукт в категории, но после добавления кода выше в мой .htaccess ошибка исчезла.
Сопоставление местоположений не разрешено в.htaccess, оно может быть размещено только в файле 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 Директивы для http.conf
Я думаю, что это самое элегантное решение, оно не затрагивает ядро Magento, применяется ко всем сайтам на вашем сервере, а также защищает внешний интерфейс.
Решение 2 от programmeurweb прослушивается на Magento 1.8.0.0!
if (!empty($k) && !empty($v)) {
$products[$k] = $v;
}
Это должно быть:
if (!empty($k)) {
$products[$k] = $v;
}
$v используется для "позиции" и не является значением флажка =1 (как флажок связать продукт с текущей категорией).
Я уже проверял это. Я ошибаюсь?
Это напоминает о том, что никогда не копируйте и не вставляйте код из сети.
PS: Сейчас я не могу комментировать ответы.