Удалить слаг таксономии из пользовательской постоянной иерархической таксономии

21

Я создал таксономию «форум», используя следующие правила:

register_taxonomy(
  'forum',
  array('topic'),
  array(
    'public' => true,
    'name' => _a('Forums'),
    'singular_name' => _a('Forum'),
    'show_ui' => true,
    'show_in_nav_menus' => true,
    'hierarchical' => true,

    'labels' => array(
      'name' => _a('Forums'),
      'singular_name' => _a('Forum'),
      'search_items' => _a('Search Forums'),
      'popular_items' => _a('Popular Forums'),
      'all_items' => _a('All Forums'),
      'parent_item' => _a('Parent Forum'),
      'parent_item_colon' => _a('Parent Forum:'),
      'edit_item' => _a('Edit Forum'),
      'update_item' => _a('Update Forum'),
      'add_new_item' => _a('Add New Forum'),
      'new_item_name' => _a('New Forum Name'),
    ),
    'query_var' => true,
    'rewrite' => array('slug' => 'forums', 'with_front' => false, 'hierarchical' => true),  
  )
);

В интерфейсе URL выглядит следующим образом:

forums/general-discussion/sub-forum

Как я могу удалить передний слаг ("форумы")? Т.е. измените URL на:

general-discussion/sub-forum

Если я передаю пустой аргумент slug в register_taxonomy (), он работает, но это вызывает проблемы с постоянными ссылками типа записей, связанных с этой таксономией.

onetrickpony
источник
@ One Trick Pony - Вы пытались вместо того, чтобы оставить 'slug' => 'forums'пустым, просто удалить его и просто иметь 'rewrite' => array('with_front' => false, 'hierarchical' => true)? Я думаю, что это сработало в прошлом для меня. Также убедитесь, что вы стираете постоянные ссылки.
eileencodes
попробовал это, и постоянные ссылки выглядят одинаково. Добавление 'slug' => ''заставляет это работать, но тогда сообщения, использующие эту таксономию, генерируют
404s
@ One Trick Pony - Помимо «общего обсуждения», какие еще сегменты пути верхнего уровня вам нужны?
MikeSchinkel
любой %forum%должен быть сегментом верхнего уровня
onetrickpony
@ One Trick Pony - я просто надеялся, что вы дадите мне несколько других сегментов пути верхнего уровня для контекста.
MikeSchinkel

Ответы:

11

ОБНОВИТЬ

С момента написания этого ядра WordPress был добавлен 'do_parse_request'хук, который позволяет элегантно обрабатывать URL-маршрутизацию без необходимости расширять WPкласс. Я подробно рассказал об этой теме в своем выступлении на WordCamp в Атланте в 2014 году под названием « Жесткая маршрутизация URL » ; слайды доступны по ссылке.

ОРИГИНАЛЬНЫЙ ОТВЕТ

Дизайн URL был важен уже более десяти лет; Я даже написал блог об этом несколько лет назад. И хотя WordPress является суммой, это отличное программное обеспечение, к сожалению, его система перезаписи URL не хватает мозгов (ИМХО, конечно. :) В любом случае, рад видеть людей, заботящихся о дизайне URL!

Ответ, который я собираюсь дать, - это плагин, который я называю, WP_Extendedкоторый является доказательством концепции этого предложения на Trac (обратите внимание, что предложение началось как одно, а превратилось в другое, так что вам нужно прочитать всю вещь, чтобы увидеть, где это было во главе.)

По сути, идея состоит в том WP, чтобы создать подкласс класса, переопределить parse_request()метод, а затем назначить глобальную $wpпеременную экземпляром подкласса. Затем внутри parse_request()вы фактически проверяете путь по сегменту пути, а не используете список регулярных выражений, которые должны полностью соответствовать URL-адресу.

Таким образом, чтобы заявить об этом явно, этот метод вставляет логику перед parse_request()проверкой совпадений URL-to-RegEx и вместо этого сначала ищет совпадения терминов таксономии, но ТОЛЬКО заменяет parse_request()и оставляет неповрежденной всю остальную систему маршрутизации URL WordPress, включая и особенно использование $query_varsпеременной.

Для вашего варианта использования эта реализация сравнивает только сегменты пути URL с терминами таксономии, поскольку это все, что вам нужно. Эта реализация инспектирует таксономии терминов уважая родитель-ребенок длительные отношения , и когда он находит совпадение, присваивает URL - путь (минус передней и задней косой черты) , чтобы $wp->query_vars['category_name'], $wp->query_vars['tag']или $wp->query_vars['taxonomy']& $wp->query_vars['term']и обходит parse_request()метод WPкласса.

С другой стороны, если путь URL не совпадает с термином из указанной таксономии, он делегирует логику маршрутизации URL в систему переписывания WordPress, вызывая parse_request()метод WPкласса.

Для использования WP_Extendedв вашем случае вам нужно вызвать register_url_route()функцию из functions.phpфайла вашей темы следующим образом:

add_action('init','init_forum_url_route');
function init_forum_url_route() {
  register_url_route(array('taxonomy'=>'forum'));
}

Что здесь является исходным кодом для плагина:

<?php
/*
Filename: wp-extended.php
Plugin Name: WP Extended for Taxonomy URL Routes
Author: Mike Schinkel
*/
function register_url_route($args=array()) {
  if (isset($args['taxonomy']))
    WP_Extended::register_taxonomy_url($args['taxonomy']);
}
class WP_Extended extends WP {
  static $taxonomies = array();
  static function on_load() {
    add_action('setup_theme',array(__CLASS__,'setup_theme'));
  }
  static function register_taxonomy_url($taxonomy) {
    self::$taxonomies[$taxonomy] = get_taxonomy($taxonomy);
  }
  static function setup_theme() { // Setup theme is 1st code run after WP is created.
    global $wp;
    $wp = new WP_Extended();  // Replace the global $wp
  }
  function parse_request($extra_query_vars = '') {
    $path = $_SERVER['REQUEST_URI'];
    $domain = str_replace('.','\.',$_SERVER['SERVER_NAME']);
    //$root_path = preg_replace("#^https?://{$domain}(/.*)$#",'$1',WP_SITEURL);
$root_path = $_SERVER['HTTP_HOST'];

    if (substr($path,0,strlen($root_path))==$root_path)
      $path = substr($path,strlen($root_path));
    list($path) = explode('?',$path);
    $path_segments = explode('/',trim($path,'/'));
    $taxonomy_term = array();
    $parent_id = 0;
    foreach(self::$taxonomies as $taxonomy_slug => $taxonomy) {
      $terms = get_terms($taxonomy_slug);
      foreach($path_segments as $segment_index => $path_segment) {
        foreach($terms as $term_index => $term) {
          if ($term->slug==$path_segments[$segment_index]) {
            if ($term->parent!=$parent_id) { // Make sure we test parents
              $taxonomy_term = array();
            } else {
              $parent_id = $term->term_id; // Capture parent ID for verification
              $taxonomy_term[] = $term->slug; // Collect slug as path segment
              unset($terms[$term_index]); // No need to scan it again
            }
            break;
          }
        }
      }
      if (count($taxonomy_term))
        break;
    }
    if (count($taxonomy_term)) {
      $path = implode('/',$taxonomy_term);
      switch ($taxonomy_slug) {
        case 'category':
          $this->query_vars['category_name'] = $path;
          break;
        case 'post_tag':
          $this->query_vars['tag'] = $path;
          break;
        default:
          $this->query_vars['taxonomy'] = $taxonomy_slug;
          $this->query_vars['term'] = $path;
          break;
      }
    } else {
      parent::parse_request($extra_query_vars); // Delegate to WP class
    }
  }
}
WP_Extended::on_load();

PS CAVEAT # 1

Хотя для данного сайта я думаю, что этот метод работает великолепно, но этот метод НИКОГДА не должен использоваться для распространения плагина на WordPress.org для использования другими . Если это ядро ​​программного пакета, основанного на WordPress, тогда это может быть хорошо. В противном случае этот метод должен быть ограничен улучшением маршрутизации URL для конкретного сайта .

Зачем? Потому что только один плагин может использовать эту технику . Если два плагина попытаются использовать его, они будут конфликтовать друг с другом.

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

ПЕРЕДАЧА № 2

Я написал это для переопределения, parse_request()которое является очень большой функцией, и вполне возможно, что я пропустил свойство или два из глобального $wpобъекта, который я должен был установить ... Так что, если что-то будет работать не так, как надо, дайте мне знать, и я буду рад исследуйте это и пересмотрите ответ, если это будет необходимо.

Так или иначе...

MikeSchinkel
источник
После написания этого я понял, что я тестировал категории, а не таксономические термины в целом, поэтому вышеприведенное не сработает для 'forum'таксономии, однако я пересмотрю его для работы позже сегодня ...
MikeSchinkel
Поэтому я обновил код для решения проблемы, упомянутой в предыдущем комментарии.
MikeSchinkel
не могу получить эту работу ... мне нужно изменить правила перезаписи?
onetrickpony
@ Один Trick Pony - немного больше диагностической информации поможет. :) Что ты пробовал? Что происходит, когда вы вводите URL в свой браузер? Вы случайно назвали свою таксономию, 'forums'а не 'forum'? Ожидаете ли вы, что URL-адреса, ссылающиеся на эти страницы, изменятся (если да, неудивительно, что мой код не касается печати URL-адресов, а только маршрутизации URL-адресов.)
MikeSchinkel
нет, я могу изменить URL-адреса (я думаю, что это функция term_link, которую мне нужно подключить для этого). site/rootforum/работает, но site/rootforum/subforum/не работает (ошибка 404) ...
onetrickpony
7

Просто, правда.

Шаг 1: Прекратить использование параметра перезаписи вообще. Мы собираемся свернуть ваши собственные переписывает.

'rewrite'=>false;

Шаг 2: Установите подробные правила страницы. Это заставляет обычные страницы иметь свои собственные правила вместо того, чтобы быть основным в нижней части страницы.

Шаг 3: Создайте несколько правил перезаписи для обработки ваших вариантов использования.

Шаг 4: Вручную заставьте правила сброса. Самый простой способ: перейдите в настройки-> Постоянная ссылка и нажмите кнопку Сохранить. Я предпочитаю это методу активации плагина для собственного использования, так как я могу заставить правила сбрасываться всякий раз, когда я что-то меняю.

Итак, время кода:

function test_init() {
    // create a new taxonomy
    register_taxonomy(
        'forum',
        'post',
        array(
            'query_var' => true,
            'public'=>true,
            'label'=>'Forum',
            'rewrite' => false,
        )
    );

    // force verbose rules.. this makes every Page have its own rule instead of being a 
    // catch-all, which we're going to use for the forum taxo instead
    global $wp_rewrite;
    $wp_rewrite->use_verbose_page_rules = true;

    // two rules to handle feeds
    add_rewrite_rule('(.+)/feed/(feed|rdf|rss|rss2|atom)/?$','index.php?forum=$matches[1]&feed=$matches[2]');
    add_rewrite_rule('(.+)/(feed|rdf|rss|rss2|atom)/?$','index.php?forum=$matches[1]&feed=$matches[2]');

    // one rule to handle paging of posts in the taxo
    add_rewrite_rule('(.+)/page/?([0-9]{1,})/?$','index.php?forum=$matches[1]&paged=$matches[2]');

    // one rule to show the forum taxo normally
    add_rewrite_rule('(.+)/?$', 'index.php?forum=$matches[1]');
}

add_action( 'init', 'test_init' );

Помните, что после добавления этого кода его нужно активировать, когда вы очищаете правила постоянной ссылки (сохраняя страницу в Настройки-> Постоянные ссылки)!

После того как вы сбросили правила и сохранили их в базе данных, тогда / что угодно должно перейти на ваш форум = любую страницу таксономии.

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

function test_foot() {
    global $wp_rewrite;
    echo '<pre>';
    var_dump($wp_rewrite->rules);
    echo '</pre>';
}
add_action('wp_footer','test_foot');

Таким образом, я могу сразу увидеть текущие правила на своей странице. Просто помните, что при наличии любого URL-адреса система запускается в верхней части правил и просматривает их, пока не найдет тот, который соответствует. Затем совпадение используется для переписывания запроса в более нормальный вид? Key = value set. Эти ключи анализируются в объекте WP_Query. Просто.

Изменить: Заметьте, этот метод, вероятно, будет работать, только если ваша обычная структура постов начинается с чего-то, что не является ловушкой, например,% category% или что-то подобное. Вам нужно начать его со статической строки или числа, например% year%. Это сделано для того, чтобы он не перехватывал ваш URL до того, как он достигнет ваших правил.

эфирное масло
источник
Если вам нужна более простая отладка ваших правил перезаписи, я (опять же) рекомендую мой плагин анализатора перезаписи , который позволяет вам опробовать правила и увидеть переменные запроса на лету.
Ян Фабри
К сожалению, текущая система перезаписи URL вынуждает объединять все потенциальные шаблоны URL в большой список по сравнению с внутренней структурой URL-путей. Текущая настройка не может соответствовать массиву литералов, таких как категории или названия форумов ; как вы знаете, это заставляет все URL-адреса «страницы» оцениваться в первую очередь. Сопоставление по сегментам пути и сопоставление несколькими способами (массив литералов, категорий, тегов, налоговых терминов, имен пользователей, типов постов, имен постов, обратных вызовов, перехватчиков фильтров и, наконец, RegEx) будет лучше масштабироваться для повышения сложности и будет проще чтобы понять.
MikeSchinkel
Майк: На самом деле, это совсем не просто понять, потому что у меня нет первого понятия о WTF, о котором ты там говоришь. Ваши идеи маршрутизации URL являются запутанными и сложными, и, как вы, вероятно, знаете, я не согласен с ними. Плоский поиск имеет больше смысла и является более гибким, чем вы полагаете. Большинству людей не нужны все эти ненужные сложности в их URL-адресах, и почти никому это не нужно.
Отто
Спасибо, но я думаю, что уже пробовал это раньше ( wordpress.stackexchange.com/questions/9455/… )
onetrickpony
К счастью WordPress Ответы теперь позволяет людям , которые делают управление неимением их URL - адресов , чтобы , наконец, голос, и они , кажется, многие (100+). Но я уважаю, что вы не сможете последовать моему примеру до полной реализации. Я предсказываю, что, как только подход, который я защищаю, будет полностью реализован в плагине, и через 6-12 месяцев он станет предпочтительным способом для сайтов CMS на основе WordPress направлять свои URL-адреса. Итак, давайте возобновим эту дискуссию примерно через 9 месяцев.
MikeSchinkel
4

Вы не сможете сделать это, используя только WP_Rewrite, так как он не может различить термины slugs и post slugs.

Вы также должны подключиться к 'request' и предотвратить 404, установив переменную post query вместо таксономической.

Что-то вроде этого:

function fix_post_request( $request ) {
    $tax_qv = 'forum';
    $cpt_name = 'post';

    if ( !empty( $request[ $tax_qv ] ) ) {
        $slug = basename( $request[ $tax_qv ] );

        // if this would generate a 404
        if ( !get_term_by( 'slug', $slug, $tax_qv ) ) {
            // set the correct query vars
            $request[ 'name' ] = $slug;
            $request[ 'post_type' ] = $cpt_name;
            unset( $request[$tax_qv] );
        }
    }

    return $request;
}
add_filter( 'request', 'fix_post_request' );

Обратите внимание, что таксономия должна быть определена до типа сообщения.

Это было бы хорошим моментом для того, чтобы указать, что наличие таксономии и типа записи с одинаковым var запроса - плохая идея.

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

scribu
источник
Согласился, что наличие таксономии и типа поста с одинаковым запросом var - это плохая идея, но это может означать, что для людей, имеющих таксономию и тип поста с одним и тем же именем, это плохая идея, но это не так. Если используется одно и то же имя, тогда только один из двух должен иметь запрос var.
MikeSchinkel
2

Я бы взглянул на код плагина для кошек верхнего уровня:

http://fortes.com/projects/wordpress/top-level-cats/

Вы можете легко адаптировать его так, чтобы он искал свой пользовательский блок таксономии, изменив

$category_base = get_option('category_base');

в строке 74 что-то вроде:

$category_base = 'forums';
Pabline
источник
Может работать для категорий, но не для пользовательских таксономий (по крайней мере, в wp 3.1) ... Мне удалось изменить URL-адреса, но я получаю 404 ошибки
onetrickpony
2

Я бы посоветовал взглянуть на плагин Custom Post Permalinks . У меня нет времени на тестирование сейчас, но это может помочь в вашей ситуации.

Трэвис Норткатт
источник
это не так, он обрабатывает только сообщения, а не таксономии, и даже если бы это было так, мне пришлось бы добавить какой-то префикс %forum%, чего я и стараюсь избегать ...
onetrickpony
2

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

Я не проверял это вообще, но это может сработать, если вы выполните это один раз сразу после того, как зарегистрируете все необходимые permastructs.

class RRSwitcher {
  var $rules;
  function RRSwitcher(){
    add_filter( 'topic_rewrite_rules', array( $this, 'topics' ) );
    add_filter( 'rewrite_rules_array', array( $this, 'rules' ) );
  }
  function topics( $array ){
    $this->rules = $array;
    return array();
  }
  function rules( $array ){
    return array_merge( (array)$array, (array)$this->rules );
  }
}
$RRSwitcher = new RRSwitcher();
global $wp_rewrite;
$wp_rewrite->use_verbose_rules = true;
$wp_rewrite->flush_rules();

Что это делает: он удаляет правила перезаписи, сгенерированные из постоянной ссылки на темы, из обычного потока массива правил и повторно объединяет их в конце массива. Это предотвращает вмешательство этих правил в любые другие правила перезаписи. Затем он вызывает подробные правила перезаписи (каждая страница получает отдельное правило с определенным регулярным выражением). Это предотвращает вмешательство страниц в правила вашей темы. Наконец, он выполняет полную очистку (убедитесь, что ваш файл .htaccess доступен для записи, иначе это не сработает) и сохраняет очень большой и очень сложный массив правил перезаписи.

Джон П Блох
источник
попробовал, ничего не меняется
onetrickpony
2

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

Хотя он не обновлялся в течение 2 лет, плагин ниже работал для меня: http://wordpress.org/plugins/remove-slug-from-custom-post-type/

К вашему сведению, я использую WP 3.9.1с WP Types1.5.7

Максимум
источник
2

Используйте слеш в качестве значения для пули ... 100% работает

'rewrite' => array(
    'slug'       => '/', 
    'with_front' => FALSE
 ),
Сатиш Джаяраман
источник
2
не совсем, это вызывает все pageсообщения типа до 404.
Майло