Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 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);
}

Увы! Начиная с версии 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>

Это можно сделать более безопасно, изменив этот параметр только для страниц администратора (адаптируйте шаблон LocationMatch к собственному стилю 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...

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

Ответ 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 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);
        }
    }
}

(Источник)

Ответ 3

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

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

[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

LocationMatch не разрешено в .htaccess, его можно разместить только в Apache httpd.conf

Предпочтительным способом является создание Apache, а не изменение httpd.conf напрямую, поскольку он будет разбит на следующий процесс обновления/восстановления.

В моем случае я создал файл include 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, распространяется на все сайты вашего сервера, а также защищает интерфейс.

Ответ 6

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

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

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

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

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

Я уже тестировал это. Я не прав?

Это напоминает никогда не копировать и вставлять код из сети.

PS: теперь я не могу комментировать ответы.