Usuń informacje o taksonomii z niestandardowego linku do hierarchicznej taksonomii

21

Utworzyłem systematykę „forum”, korzystając z następujących zasad:

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),  
  )
);

W interfejsie URL wygląda następująco:

forums/general-discussion/sub-forum

Jak mogę usunąć przedni ślimak („fora”)? To znaczy zmień adresy URL na:

general-discussion/sub-forum

Jeśli przekażę pusty argument o błędzie do register_taxonomy (), to działa, ale powoduje to problemy z permalinkami typu post powiązanymi z tą taksonomią

onetrickpony
źródło
@ One Trick Pony - Czy próbowałeś zamiast pozostawiać 'slug' => 'forums'puste, po prostu usuwając je całkowicie i po prostu mając 'rewrite' => array('with_front' => false, 'hierarchical' => true)? Myślę, że to działało w przeszłości dla mnie. Upewnij się także, czy przepłukałeś permalinki.
eileencodes
próbowałem tego, a permalinki wyglądają tak samo. Dodanie 'slug' => ''sprawia, że ​​działa, ale następnie posty korzystające z tej taksonomii generują 404s
onetrickpony
@One Trick Pony - poza „ogólną dyskusją”, jakich innych segmentów ścieżki najwyższego poziomu potrzebujesz?
MikeSchinkel
każdy %forum%powinien być segmentem najwyższego poziomu
onetrickpony
@ One Trick Pony - Miałem tylko nadzieję, że dasz mi inne segmenty ścieżki najwyższego poziomu dla kontekstu.
MikeSchinkel,

Odpowiedzi:

11

AKTUALIZACJA

Od czasu napisania tego rdzenia WordPress dodano 'do_parse_request'haczyk, który pozwala na eleganckie zarządzanie routingiem adresów URL bez konieczności rozszerzania WPklasy. Szczegółowo omówiłem ten temat w moim wykładzie WordCamp 2014 w Atlancie zatytułowanym Hardcore URL Routing ; slajdy są dostępne pod linkiem.

ORYGINALNA ODPOWIEDŹ

Projektowanie adresów URL było ważne od ponad dekady; Napisałem nawet o tym blog kilka lat temu. I chociaż WordPress to suma, to genialne oprogramowanie, niestety jego system do przepisywania adresów URL nie ma już mózgów (IMHO, oczywiście. :) W każdym razie cieszę się, że ludzie dbają o projektowanie adresów URL!

Mam zamiar udzielić wtyczki, którą nazywam, WP_Extendedktóra jest dowodem koncepcji tej propozycji na Tracu (zauważ, że propozycja zaczęła się jako jedna rzecz i ewoluowała w inną, więc musisz przeczytać całą rzecz, aby zobaczyć, gdzie to było na czele.)

Zasadniczo chodzi o podklasę WPklasy, przesłonięcie parse_request()metody, a następnie przypisanie $wpzmiennej globalnej instancją podklasy. Następnie w parse_request()tobie sprawdź ścieżkę według segmentu ścieżki zamiast korzystać z listy wyrażeń regularnych, które muszą pasować do adresu URL w całości.

Tak więc, mówiąc wprost, technika ta wstawia logikę przed parse_request()sprawdzaniem zgodności adresów URL z RegEx i zamiast tego najpierw szuka dopasowań terminów taksonomicznych, ale TYLKO zastępuje parse_request()i pozostawia nienaruszoną resztę systemu routingu URL WordPress, w tym i zwłaszcza użycie $query_varszmiennej.

W twoim przypadku użycia ta implementacja porównuje tylko segmenty ścieżki URL z warunkami taksonomii, ponieważ to wszystko, czego potrzebujesz. Ta implementacja kontroluje warunki taksonomii poszanowaniem relacji rodzic-dziecko utrzymujące a kiedy znajdzie dopasowanie przypisuje ścieżkę URL (minus początkowe i końcowe ukośniki) do $wp->query_vars['category_name'], $wp->query_vars['tag']lub $wp->query_vars['taxonomy']& $wp->query_vars['term']i omija parse_request()sposobu WPklasie.

Z drugiej strony, jeśli ścieżka adresu URL nie zgadza się z terminem z określonej taksonomii, deleguje logikę routingu adresu URL do systemu przepisywania WordPress, wywołując parse_request()metodę WPklasy.

Aby użyć w WP_Extendedprzypadku użycia, musisz wywołać register_url_route()funkcję z functions.phppliku motywu w następujący sposób:

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

Co to jest kod źródłowy wtyczki:

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

Chociaż dla danej strony myślę, że ta technika działa doskonale, ale NIGDY nie należy jej używać do dystrybucji wtyczki na WordPress.org, z której mogą korzystać inni . Jeśli jest to rdzeń pakietu oprogramowania opartego na WordPress, może to być w porządku. W przeciwnym razie technika ta powinna być ograniczona do poprawy routingu adresów URL dla konkretnej witryny .

Czemu? Ponieważ tylko jedna wtyczka może korzystać z tej techniki . Jeśli dwie wtyczki spróbują go użyć, będą ze sobą konfliktować.

Nawiasem mówiąc, tę strategię można rozszerzyć, aby generalnie obsługiwać praktycznie każdy wzorzec przypadku użycia, który może być wymagany, i właśnie to zamierzam wdrożyć, gdy tylko znajdę wolny czas lub klienta, który może sponsorować czas, który zajmie budować w pełni ogólne implementacje.

CAVEAT # 2

Napisałem to, aby zastąpić parse_request()tę bardzo dużą funkcję, i jest całkiem możliwe, że przegapiłem właściwość lub dwa globalne $wpobiekty, które powinienem był ustawić. Więc jeśli coś zadziwia, daj mi znać i chętnie zbadaj go i w razie potrzeby popraw odpowiedź.

Tak czy inaczej...

MikeSchinkel
źródło
Po napisaniu tego zdałem sobie sprawę, że testowałem dla kategorii zamiast ogólnie dla taksonomii, więc powyższe nie będzie działać dla 'forum'taksonomii, ale
zmienię
Zaktualizowałem więc kod, aby rozwiązać problem, o którym wspomniałem w poprzednim komentarzu.
MikeSchinkel
nie mogę dostać tej pracy ... czy muszę zmienić reguły przepisywania?
onetrickpony
@ One Trick Pony - Pomogłoby trochę więcej informacji diagnostycznych. :) Czego próbowałeś? Co się stanie, gdy wpiszesz adresy URL w przeglądarce? Czy przypadkiem nazwałeś swoją taksonomię, 'forums'a nie 'forum'? Czy spodziewasz się, że adresy URL prowadzące do tych stron zmienią się (jeśli tak, nic dziwnego, mój kod nie dotyczy drukowania adresów URL, a jedynie routing adresów URL.)
MikeSchinkel
nie, mogę zmienić adresy URL (myślę, że jest to funkcja term_link, do której muszę się przyłączyć). site/rootforum/działa, ale site/rootforum/subforum/nie działa (błąd 404) ...
onetrickpony 24.02.11
7

Naprawdę proste.

Krok 1: W ogóle przestań używać parametru przepisywania. Wprowadzimy twoje własne przepisy.

'rewrite'=>false;

Krok 2: Ustaw pełne zasady strony. To zmusza normalne strony do posiadania własnych reguł zamiast do łapania wszystkiego na dole strony.

Krok 3: Utwórz kilka reguł przepisywania, aby obsłużyć przypadki użycia.

Krok 4: Ręcznie wymuś regułę koloru. Najłatwiejszy sposób: przejdź do ustawień-> bezpośredni link i kliknij przycisk Zapisz. Wolę to niż metodę aktywacji wtyczki na własny użytek, ponieważ mogę zmusić reguły do ​​opróżnienia przy każdej zmianie.

Czas kodu:

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' );

Pamiętaj, że po dodaniu tego kodu musisz mieć go aktywnym, kiedy idziesz opróżniać reguły permalink (zapisując stronę w Ustawienia-> Permalinki)!

Po wyczyszczeniu reguł i zapisaniu ich w bazie danych, / cokolwiek powinno przejść do twojego forum = jakakolwiek strona systematyki.

Reguły przepisywania naprawdę nie są trudne, jeśli rozumiesz wyrażenia regularne. Używam tego kodu, aby pomóc mi w debugowaniu:

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

W ten sposób mogę zobaczyć obecne zasady na pierwszy rzut oka na mojej stronie. Pamiętaj tylko, że pod jakimkolwiek adresem URL system zaczyna się na górze reguł i przechodzi przez nie, aż znajdzie taki, który pasuje. Dopasowanie jest następnie używane do przepisania zapytania na bardziej normalnie wyglądający? Klucz = zestaw wartości. Te klucze są analizowane w tym, co trafia do obiektu WP_Query. Prosty.

Edycja: Uwaga: ta metoda prawdopodobnie będzie działać tylko wtedy, gdy normalna niestandardowa struktura postu zaczyna się od czegoś, co nie jest przeszkodą, na przykład% category% lub czegoś podobnego. Musisz go rozpocząć od ciągu statycznego lub liczbowego, na przykład% rok%. Ma to na celu zapobieganie przechwytywaniu adresu URL przez użytkownika, zanim dotrze on do reguł.

Otto
źródło
Jeśli chcesz łatwiej debugować reguły przepisywania, polecam (ponownie) moją wtyczkę analizatora przepisywania , która pozwala wypróbować reguły i zobaczyć zmienne zapytania w locie.
Jan Fabry
Niestety obecny system przepisywania adresów URL wymusza spłaszczenie wszystkich potencjalnych wzorców adresów URL na dużą listę w porównaniu z nieodłączną strukturą drzewa ścieżek adresów URL. Obecna konfiguracja nie może wygodnie pasować do szeregu literałów, takich jak kategorie lub nazwy forum ; jak wiesz to zmusza wszystkie „Strona” URL należy najpierw ocenić. Dopasowywanie według segmentu ścieżki i dopasowanie na wiele sposobów (tablica literałów, kategorii, tagów, warunków podatkowych, nazw użytkowników, typów postów, nazw postów, wywołań zwrotnych, haczyków filtrów i wreszcie RegEx) skalowałoby się pod kątem złożoności i byłoby łatwiejsze rozumieć.
MikeSchinkel,
Mike: Właściwie to wcale nie jest łatwiejsze do zrozumienia, ponieważ nie mam pierwszej wskazówki WTF, o której tu mówisz. Twoje pomysły dotyczące routingu adresów URL są mylące i trudne, a jak zapewne wiesz, nie zgadzam się z nimi. Wyszukiwanie płaskie ma sens i jest bardziej elastyczne, niż można to przypisać. Większość ludzi nie chce całej tej niepotrzebnej złożoności w swoich adresach URL i prawie nikt też jej nie potrzebuje.
Otto,
Dzięki, ale myślę, że już tego próbowałem ( wordpress.stackexchange.com/questions/9455/… )
onetrickpony
Na szczęście WordPress Answers pozwala teraz osobom, które chcą kontrolować swoje adresy URL, mieć w końcu głos i wydaje się, że jest ich wiele (ponad 100). Ale szanuję, że możesz nie być w stanie pójść za moim przykładem przed pełnym wdrożeniem. Przewiduję, że gdy podejście, które zalecam, zostanie w pełni zaimplementowane we wtyczce, a po około 6-12 miesiącach stanie się preferowanym sposobem dla stron CMS opartych na WordPress do kierowania ich adresów URL. Wznówmy więc tę debatę za około 9 miesięcy.
MikeSchinkel
4

Nie będziesz w stanie tego zrobić przy użyciu samego WP_Rewrite, ponieważ nie można odróżnić ślimaków terminowych od postowych.

Trzeba również podłączyć się do „żądania” i zapobiec 404, ustawiając zmienną zapytania post zamiast taksonomii.

Coś takiego:

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' );

Pamiętaj, że taksonomia musi zostać zdefiniowana przed typem postu.

Byłby to dobry moment na wskazanie, że taksonomia i typ postu z tym samym zapytaniem var to zły pomysł.

Ponadto nie będziesz w stanie dotrzeć do postów, które mają taki sam ślimak jak jeden z warunków.

scribu
źródło
Zgodził się, że taksonomia i typ postu z tym samym zapytaniem var jest złym pomysłem, ale może to oznaczać, że ludzie mający taksonomię i typ postu o tej samej nazwie to zły pomysł, co nie jest prawdą. Jeśli używasz tej samej nazwy, tylko jeden z dwóch powinien mieć zapytanie var.
MikeSchinkel,
2

Rzuciłbym okiem na kod wtyczki najwyższego poziomu dla kotów:

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

Możesz łatwo to dostosować, aby szukał niestandardowego ślimaka taksonomii, zmieniając

$category_base = get_option('category_base');

w linii 74 do czegoś takiego jak:

$category_base = 'forums';
Pabline
źródło
Może działa w przypadku kategorii, ale nie dotyczy niestandardowych systematyk (przynajmniej w WP 3.1) ... Udało mi się zmienić adresy URL, ale dostaję błędy 404
onetrickpony
2

Proponuję rzucić okiem na wtyczkę Custom Post Permalinks . Nie mam teraz czasu na testowanie, ale może to pomóc w twojej sytuacji.

Travis Northcutt
źródło
tak nie jest, obsługuje tylko posty, a nie taksonomie, a nawet gdyby to zrobił, musiałbym wcześniej dodać jakiś prefiks %forum%, czego dokładnie staram się uniknąć ...
onetrickpony
2

Ponieważ znam drugie pytanie , odpowiem na to pytanie.

Nie przetestowałem tego wcale, ale może działać, jeśli wykonasz to zaraz po zarejestrowaniu wszystkich wymaganych permastruktów:

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();

Co to robi: usuwa reguły przepisywania wygenerowane z linków tematycznych z normalnego przepływu tablicy reguł i ponownie łączy je na końcu tablicy. Zapobiega to zakłócaniu tych reguł przez inne reguły przepisywania. Następnie wymusza pełne przepisywanie reguł (każda strona otrzymuje indywidualną regułę z określonym wyrażeniem regularnym). Zapobiega to ingerencji stron w reguły Twojego tematu. Na koniec wykonuje twardy zapis (upewnij się, że plik .htaccess jest zapisywalny, w przeciwnym razie nie zadziała) i zapisuje bardzo dużą, bardzo skomplikowaną tablicę reguł przepisywania.

John P. Bloch
źródło
próbowałem, nic się nie zmienia
onetrickpony
2

Nie jestem pewien, czy to zadziała w przypadku taksonomii, ale działało w przypadku niestandardowych typów postów

Chociaż nie była aktualizowana przez 2 lata, poniższa wtyczka działała dla mnie: http://wordpress.org/plugins/remove-slug-from-custom-post-type/

Do Twojej wiadomości używam WP 3.9.1z typami WP1.5.7

Max
źródło
2

Użyj ukośnika jako wartości dla ślimaka ... 100% działa

'rewrite' => array(
    'slug'       => '/', 
    'with_front' => FALSE
 ),
Sathish Jayaraman
źródło
2
niezupełnie, powoduje to, że wszystkie pagetypy postów mają wartość 404.
Milo,