Где, Когда и Как Правильно Сбрасывать Правила Перезаписи в Рамках Плагина?

15 нояб. 2013 г., 19:20:41
Просмотры: 25.7K
Голосов: 16

У меня возникла странная проблема с некорректным сбросом правил перезаписи.

Я пробовал использовать flush_rewrite_rules(); и flush_rewrite_rules(true);.

Я также пробовал глобализировать $wp_rewrite используя $wp_rewrite->flush_rules(); и $wp_rewrite->flush_rules(true);

Ни один из этих методов не очищает правила перезаписи корректно. Эти вызовы действительно сбрасывают правила перезаписи при вызове. Откуда я это знаю? Используя решение для отладки сброса правил перезаписи.

В настоящее время у меня правила перезаписи сбрасываются при активации и деактивации плагина. С этим проблем нет.

У меня есть страница настроек администрирования плагина для пользователей. Некоторые настройки изменяют структуру постоянных ссылок, поэтому правила перезаписи должны быть сброшены при "Сохранении настроек" на странице администрирования плагина. (Использует стандартный update_option(); для сохранения настроек.)

Хочу отметить, что в зависимости от указанных настроек, создаются пользовательские типы записей, соответствующие указанным пользователем настройкам. Поэтому правила перезаписи должны быть сброшены сразу после сохранения настроек. Именно здесь всё работает некорректно.

Вышеуказанное решение для отладки правил перезаписи, предоставленное @toscho, показывает, что сбрасывается множество правил перезаписи. Однако при посещении отдельной записи пользовательского типа или даже архива пользовательского типа, каждый возвращает ошибки 404.

Пользовательский тип записи зарегистрирован правильно и корректно. Я точно знаю, что проблема не в этом.

Сразу после сохранения настроек страницы администрирования плагина. Пользовательские типы записей создаются, структура постоянных ссылок корректируется, и выполняется попытка сброса всех правил перезаписи.

Пользовательские типы записей затем загружаются всегда и загружаются в init как обычно.

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

Теперь странная часть: если я просто захожу на страницу настроек постоянных ссылок в админке, а затем возвращаюсь на фронтенд для просмотра отдельных записей или разделов архива пользовательского типа, они волшебным образом работают как ожидалось.

Что делает эта страница настроек постоянных ссылок в админке, чего не делаю я, что позволяет правилам перезаписи сбрасываться правильно, а у меня нет?

То есть, как временное решение, я перенаправляю пользователя на страницу настроек постоянных ссылок после сохранения страницы настроек плагина, но это не идеальное решение. Я бы предпочел, чтобы правила перезаписи просто правильно сбрасывались в коде моего плагина.

Есть ли определенный момент в WordPress, когда сброс правил перезаписи просто перестает сбрасывать ВСЕ правила?

admin_menu - Страница настроек плагина добавляется в администрирование WordPress.

add_options_page() - Страница настроек плагина добавляется в меню Настройки.

Страница настроек отображается в callback-функции для add_options_page(). Здесь же обрабатывается $_POST для обновления настроек плагина и сброса правил перезаписи.

Поскольку это уже длинный вопрос, я готов предоставить блоки кода (если это поможет) по внешней ссылке, чтобы помочь получить правильный ответ.

3
Комментарии

Похоже, что у вас, возможно, неправильный порядок действий, трудно сказать без просмотра кода. Страница управления постоянными ссылками просто вызывает flush_rewrite_rules, которая просто удаляет опцию rewrite_rules и восстанавливает её заново. Вы можете открыть файл wp-admin/options-permalinks.php и посмотреть, где это происходит. Поскольку эта операция просто удаляет всю опцию, невозможно частично сбросить правила перезаписи.

Milo Milo
15 нояб. 2013 г. 20:27:18

@Milo Думаю, вы правы. У меня есть класс, который загружается на хуке init и регистрирует типы записей. Я предположил, что настройки страницы сохраняются, страница перезагружается... затем снова срабатывает хук init, чтобы зарегистрировать необходимые типы записей. Я думал, что типы записей уже будут загружены, и мне нужно только обновить опцию, а затем сбросить правила перезаписи со страницы настроек моего плагина. Я опубликую ответ о том, как я нашёл решение.

Michael Ecklund Michael Ecklund
15 нояб. 2013 г. 21:21:56

Предупреждение: функция flush_rewrite_rules() в моем плагине оказалась частью проблемы. Я удалил PHP-хук и в итоге просто обновил постоянные ссылки вручную, после чего ошибки 404 для моего CPT исчезли.

myol myol
20 окт. 2014 г. 11:38:33
Все ответы на вопрос 8
2

Лучшее место для сброса правил перезаписи — это активация/деактивация плагина.

function myplugin_activate() {
    // регистрируем таксономии/типы записей здесь
    flush_rewrite_rules();
}

register_activation_hook( __FILE__, 'myplugin_activate' );

function myplugin_deactivate() {
    flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

См. статью в кодексе

Заранее извиняюсь, я не до конца разобрался в вашем вопросе, так что это довольно шаблонный ответ.

15 нояб. 2013 г. 19:37:09
Комментарии

Спасибо за предложение, но я в курсе. Проблема не в активации/деактивации плагина. Она связана с тем, что пользователи изменяют настройки уже активного плагина, что приводит к изменению правил перезаписи и требует их сброса.

Michael Ecklund Michael Ecklund
15 нояб. 2013 г. 21:17:10

Я так и подумал, но был довольно уставшим, когда читал ваш вопрос. Решение от ialocin выглядит перспективным, надеюсь, у вас получилось разобраться.

helgatheviking helgatheviking
17 нояб. 2013 г. 02:22:41
4

Трудно сказать, что не так, не видя ваш код. Но после сохранения некоторых настроек целесообразно использовать хук admin_init, как показано ниже, чтобы сбросить правила перезаписи.

Код:

add_action('admin_init', 'wpse_123401_plugin_settings_flush_rewrite');
function wpse_123401_plugin_settings_flush_rewrite() {
    if ( get_option('plugin_settings_have_changed') == true ) {
        flush_rewrite_rules();
        update_option('plugin_settings_have_changed', false);
    }
}


Вам нужно установить опцию где-то на странице настроек или, точнее, в процессе сохранения настроек. Делать это без опции — плохая практика, так как вы не хотите сбрасывать правила каждый раз.


Примечание: не тестировалось

15 нояб. 2013 г. 20:47:37
Комментарии

Или можно использовать transient, возможно? Но определенно +1 за то, чтобы не сбрасывать правила при каждом admin_init.

helgatheviking helgatheviking
17 нояб. 2013 г. 02:23:11

Конечно, ты прав насчет transient, думаю я выбрал *_option() из-за страницы настроек. @helgatheviking

Nicolai Grossherr Nicolai Grossherr
28 нояб. 2013 г. 00:11:18

Это было очень полезно. У меня была похожая ситуация, как у Майкла. В итоге я использовал предложение helga о transient и установил его в функции валидации настроек. В результате мне пришлось установить transient в false после сброса, иначе он продолжал сбрасываться при каждой загрузке админки, пока transient не истекал. Так что функционально использование option или transient было одинаковым. Наверное, transient может быть удобнее, чтобы немного очистить таблицу опций. Но это уже мелочи.

MatthewLee MatthewLee
15 февр. 2014 г. 19:59:35

Разница действительно незначительна, особенно если транзиенты постоянные, без срока действия, но, конечно, транзиенты обладают другими возможностями и преимуществами, которые полезно иметь. @MatthewLee

Nicolai Grossherr Nicolai Grossherr
15 февр. 2014 г. 21:34:22
0

У меня был файл класса типов записей, который отвечал за чтение настроек плагина и создание необходимых пользовательских типов записей на основе указанных пользователем параметров.

Этот файл класса типов записей загружался на хуке init.

Я предполагал, что мне нужно просто обновить настройки плагина, а затем сбросить правила перезаписи. Поскольку класс типов записей уже загружался на основе настроек плагина. Но с административными страницами ситуация иная — они загружаются ПОСЛЕ хука init.

Типы записей фактически не регистрировались, потому что настройки ещё не были установлены. Класс регистрации типов записей завершался досрочно без регистрации каких-либо типов.

Решение оказалось следующим:

  1. Обновить настройки плагина.
  2. Загрузить файл класса типов записей ТОЛЬКО один раз в этом месте, чтобы создать новые правила перезаписи.
  3. Сбросить правила перезаписи.

(Ранее... шаг 2 отсутствовал — Как упоминалось выше...)

Теперь типы записей будут загружаться на хуке init с уже указанными настройками, что позволит создавать типы записей и связывать их с соответствующими правилами перезаписи.

По какой-то причине мне пришлось добавить JavaScript-вызов для перенаправления на текущую страницу после выполнения трёх шагов.

Также мне потребовалось добавить вызов flush_rewrite_rules(); на странице настроек плагина в админ-панели.

Таким образом, для гарантированного сброса всего:

Шаг 1) Перейти на страницу настроек плагина в админ-панели — Первоначальный сброс.

Шаг 2) Обновить настройки плагина — Второй сброс.

Шаг 3) Перенаправление на страницу настроек плагина вызывает... Третий и окончательный сброс (аналогично первоначальному — выполняется автоматически при посещении страницы настроек)

Не могу сказать, что это практичное решение, но у меня оно сработало. Очень странная проблема, скорее всего связанная с архитектурой моего кода.

15 нояб. 2013 г. 21:38:08
0

У меня была точно такая же проблема. В моем плагине есть типы записей, которые создаются динамически. Поэтому их нельзя зарегистрировать через register_post_type() в статическом методе во время activation_hook, и они ещё не активны, когда выполняется flush_rewrite_rules() в этом хуке (что обычно является рекомендуемым способом сброса правил перезаписи).

Самое чистое решение, которое я в итоге придумал, — сбрасывать правила перезаписи после регистрации типов записей, но только если это действительно необходимо (поскольку операция медленная). В моем случае у меня несколько пользовательских типов записей, наследующихся от одного базового класса, поэтому было желательно реализовать код для сброса именно там.

Необходимость сброса можно определить, проверив результат get_option( 'rewrite_rules' ):

class MyPostTypeClass {

public final function register_as_custom_post_type() {
    ...   // выполните здесь всю настройку вашего типа записи     
    $args = array(
                  ... // заполните остальные аргументы по своему усмотрению
                  'rewrite' => array('slug' => 'slug-of-your-post-type')
                 );
    register_post_type('post-type-name-of-your-post-type', $args );

    $rewrite_rules_must_be_fluhed = true;
    foreach( get_option( 'rewrite_rules' ) as $key => $rule)
        if(strpos($key, $args['rewrite']['slug'] ) === 0)
        {
            $rewrite_rules_must_be_fluhed = false;
            break;
        }
    if($rewrite_rules_must_be_fluhed)
        flush_rewrite_rules(true);
}
}

Недостатки:

  • Частично зависит от того, какие именно правила перезаписи генерирует WP во время register_post_type().
  • Проверка необходимости сброса при каждой загрузке страницы также создает некоторую нагрузку.

Преимущества:

  • Полностью инкапсулировано в классе, представляющем тип записи.
  • Сбрасывает правила перезаписи только при реальной необходимости.

Используйте это только если вы не можете зарегистрировать ваш тип записи в статической функции, которая может вызываться как во время init, так и во время activation_hook!

Зависимость от вида сгенерированных правил можно уменьшить, заменив проверку if(strpos($key, $args['rewrite']['slug'] ) === 0) на более сложную, например, регулярное выражение.

21 июл. 2018 г. 12:08:20
0

@tazo-todua это также сработало у меня при использовании мультисайта.

add_action( 'wpmu_new_blog', 'set_my_permalink_structure', 11, 2 );

function set_my_permalink_structure( $blog_id ) {

    switch_to_blog( $blog_id );

    global $wp_rewrite;
    $wp_rewrite->set_permalink_structure( '/%postname%/' );
    $wp_rewrite->flush_rules();
    $wp_rewrite->init();

    restore_current_blog();
}
11 нояб. 2016 г. 23:30:54
0

Если нужно выполнить flush_rewrite_rules при активации или деактивации ЛЮБОГО плагина

add_action('init', function(){

    // Получаем пути всех установленных плагинов
    $pluginsInstalledPaths = glob(WP_PLUGIN_DIR . '/*', GLOB_ONLYDIR);

    foreach ($pluginsInstalledPaths as $pluginPath) {
        // Получаем все PHP-файлы в корневой директории плагина
        $files = glob($pluginPath . '/*.php');
        foreach ($files as $file) {
            $contents = file_get_contents($file);
            // Если файл содержит "Plugin Name" - это главный файл плагина
            if (preg_match('/\/\*\*[^\/]*?Plugin Name:/', $contents)) {
                add_action('activate_' . plugin_basename($file), function(){
                    flush_rewrite_rules();
                });
                add_action('deactivate_' . plugin_basename($file), function(){
                    flush_rewrite_rules();
                });
            }
        }
    }
});
26 янв. 2022 г. 15:43:39
0
-1

МОЁ РЕШЕНИЕ:

global $wp_rewrite; $wp_rewrite->flush_rules(); $wp_rewrite->init();
20 апр. 2015 г. 10:00:51
0
-1

Добавьте этот код в любое место, где вам нужно сбросить правила перезаписи, например, при активации плагина или любом другом событии.

add_action('init','flush_rewrite_rules');
22 июн. 2023 г. 14:41:10