Почему Magento сохраняет только первые 1000 товаров в категории после сохранения категории?


Используя бэк-офис Magento, после сохранения категории, которая была связана со многими продуктами, сохраняются только первые 1000 продуктов (или 2000, или x000, в зависимости от конфигурации хоста).

Все другие ссылки на категории/продукты удаляются из таблицы catalog_category_product в базе данных.
Это происходит даже при том, что в сетке товаров (вкладка "Товары категории") не были сняты флажки.

Сообщение об ошибке не отображается и не регистрируется.
Отслеживание данных, отправленных из браузера в сервер не обнаруживает отсутствующих идентификаторов продуктов, но даже если вы проверите больше продуктов в сетке бэк-офиса, при окончательном сохранении категории сервер сохранит только первые x000 и удалит другие идентификаторы...

Author: programmeurweb, 2013-02-13

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

Надеюсь, это все равно полезно, я немного боролся время, прежде чем я смог выяснить, почему некоторые категории продолжали терять некоторые товары!

 27
Author: programmeurweb, 2013-02-13 10:11:41

Лучшим решением было бы использовать событие наблюдателя "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);
        }
    }
}

( Источник)

 5
Author: Maksold, 2014-02-26 11:46:33

Я нашел рабочее решение, поэтому делюсь им здесь!

Создайте имя файла php5.ini в своей папке magento root и в первой строке просто поместите этот код

[PHP]
max_input_vars = 5000

Теперь, если он не работает, вам нужно будет добавить эту строку в свой СЕРВЕР PHP,ini (/usr/local/lib/php.ini)

max_input_vars = 5000

Надеюсь, это поможет.

 1
Author: user2690463, 2013-09-26 13:24:14

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

Сделал трюк для меня, у меня была та же проблема, что и описанная выше, Magento 1.4.1.1 сохранил только 1001 продукт в категории, но после добавления кода выше в мой .htaccess ошибка исчезла.

 0
Author: Arne, 2013-05-10 08:19:12

Сопоставление местоположений не разрешено в.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, применяется ко всем сайтам на вашем сервере, а также защищает внешний интерфейс.

 0
Author: Ilian Andreev, 2013-09-12 22:57:13

Решение 2 от programmeurweb прослушивается на Magento 1.8.0.0!

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

Это должно быть:

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

$v используется для "позиции" и не является значением флажка =1 (как флажок связать продукт с текущей категорией).

Я уже проверял это. Я ошибаюсь?

Это напоминает о том, что никогда не копируйте и не вставляйте код из сети.

PS: Сейчас я не могу комментировать ответы.

 0
Author: obscure, 2014-04-24 20:38:43