Supprimer le slug de taxonomie d'un permalien de taxonomie hiérarchique personnalisé

21

J'ai créé une taxonomie de «forum» en utilisant ces règles:

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

Dans le front-end, les URL ressemblent à:

forums/general-discussion/sub-forum

Comment puis-je supprimer la limace avant ("forums")? Autrement dit, changez les URL en:

general-discussion/sub-forum

Si je passe un argument slug vide à register_taxonomy () cela fonctionne, mais cela provoque des problèmes avec les permaliens du type de message associé à cette taxonomie

onetrickpony
la source
@One Trick Pony - Avez-vous essayé au lieu de laisser le 'slug' => 'forums'blanc simplement le retirer complètement et simplement l'avoir 'rewrite' => array('with_front' => false, 'hierarchical' => true)? Je pense que cela a fonctionné dans le passé pour moi. Assurez-vous également de vider les permaliens.
eileencodes
essayé cela, et les permaliens se ressemblent. L'ajout le 'slug' => ''fait fonctionner, mais les publications utilisant cette taxonomie génèrent 404
onetrickpony
@One Trick Pony - Outre la «discussion générale», de quels autres segments de chemin de niveau supérieur avez-vous besoin?
MikeSchinkel
tout %forum%devrait être un segment de haut niveau
onetrickpony
@One Trick Pony - J'espérais juste que vous me donneriez d'autres segments de chemin de niveau supérieur pour le contexte.
MikeSchinkel

Réponses:

11

MISE À JOUR

Depuis l'écriture, ce noyau WordPress a ajouté le 'do_parse_request'crochet qui permet au routage d'URL d'être géré avec élégance et sans avoir à étendre la WPclasse. J'ai couvert le sujet en profondeur dans mon exposé WordCamp d'Atlanta 2014 intitulé " Routage d'URL hardcore " ; les diapositives sont disponibles sur le lien.

RÉPONSE ORIGINALE

La conception d'URL est importante depuis plus d'une décennie; J'ai même écrit un blog à ce sujet il y a plusieurs années. Et bien que WordPress soit la somme d'un logiciel génial, malheureusement, son système de réécriture d'URL est juste à court de cervelle (à mon humble avis, bien sûr. :) Quoi qu'il en soit, heureux de voir des gens se soucier de la conception d'URL!

La réponse que je vais fournir est un plugin que j'appelle WP_Extendedqui est une preuve de concept pour cette proposition sur Trac (Notez que la proposition a commencé comme une chose et a évolué en une autre, vous devez donc lire la chose entière pour voir où il était dirigé.)

Fondamentalement, l'idée est de sous- WPclasser la classe, de remplacer la parse_request()méthode, puis d'affecter la $wpvariable globale avec une instance de la sous-classe. Ensuite, en parse_request()vous, inspectez réellement le chemin par segment de chemin au lieu d'utiliser une liste d'expressions régulières qui doivent correspondre à l'URL dans leur intégralité.

Donc, pour le dire explicitement, cette technique insère une logique devant celle parse_request()qui vérifie les correspondances URL-à-RegEx et recherche d'abord des correspondances de termes de taxonomie, mais elle remplace et laisse UNIQUEMENTparse_request() le reste du système de routage d'URL WordPress intact, y compris et en particulier l'utilisation de la $query_varsvariable.

Pour votre cas d'utilisation, cette implémentation compare uniquement les segments de chemin d'URL aux termes de taxonomie, car c'est tout ce dont vous avez besoin. Cette implémentation inspecte les termes de taxonomie respectant les relations de terme parent-enfant et lorsqu'elle trouve une correspondance, elle attribue le chemin URL (moins les barres obliques de début et de fin) à $wp->query_vars['category_name'], $wp->query_vars['tag']ou $wp->query_vars['taxonomy']& $wp->query_vars['term']et elle contourne la parse_request()méthode de la WPclasse.

En revanche, si le chemin URL ne correspond pas à un terme d'une taxonomie que vous avez spécifiée, il délègue la logique de routage URL au système de réécriture WordPress en appelant la parse_request()méthode de la WPclasse.

Pour l'utiliser WP_Extendedpour votre cas d'utilisation, vous devrez appeler la register_url_route()fonction à partir du functions.phpfichier de votre thème comme ceci:

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

Voici le code source du plugin:

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

Bien que pour un site donné, je pense que cette technique fonctionne à merveille mais cette technique ne doit JAMAIS être utilisée pour qu'un plugin soit distribué sur WordPress.org pour que d'autres puissent l'utiliser . S'il est au cœur d'un progiciel basé sur WordPress, cela pourrait être correct. Sinon, cette technique devrait se limiter à améliorer le routage d'URL pour un site spécifique .

Pourquoi? Parce qu'un seul plugin peut utiliser cette technique . Si deux plugins essaient de l'utiliser, ils entreront en conflit.

En aparté, cette stratégie peut être étendue pour gérer de manière générique pratiquement tous les modèles de cas d'utilisation qui pourraient être requis et c'est ce que j'ai l'intention de mettre en œuvre dès que je trouve le temps libre ou un client qui peut parrainer le temps qu'il faudrait pour construire des implémentations entièrement génériques.

CAVEAT # 2

J'ai écrit ceci pour remplacer parse_request()ce qui est une très grande fonction, et il est fort possible que j'ai raté une propriété ou deux de l' $wpobjet global que j'aurais dû définir. Donc, si quelque chose agit de manière chancelante, faites le moi savoir et je serai heureux de recherchez-le et révisez la réponse si nécessaire.

En tous cas...

MikeSchinkel
la source
Après avoir écrit ceci, j'ai réalisé que j'avais testé pour les catégories plutôt que pour les termes de taxonomie en général, donc ce qui précède ne fonctionnera pas pour la 'forum'taxonomie, mais je le réviserai pour fonctionner plus tard dans la
journée
J'ai donc mis à jour le code pour résoudre le problème que je mentionne dans le commentaire précédent.
MikeSchinkel
ne peut pas obtenir ce travail ... dois-je changer les règles de réécriture?
onetrickpony
@One Trick Pony - Un peu plus d'informations de diagnostic seraient utiles. :) Qu'avez-vous essayé? Que se passe-t-il lorsque vous saisissez les URL dans votre navigateur? Avez-vous par hasard appelé votre taxonomie 'forums'plutôt que 'forum'? Vous attendez-vous à ce que les URL qui pointent vers ces pages changent (si oui, pas étonnant, mon code ne traite pas de l'impression des URL, mais uniquement du routage des URL.)
MikeSchinkel
non, je peux changer les URL (je pense que c'est la fonction term_link à laquelle je dois me connecter pour cela). site/rootforum/fonctionne, mais site/rootforum/subforum/ne fonctionne pas (erreur 404) ...
onetrickpony
7

Vraiment simple.

Étape 1: arrêtez complètement d'utiliser le paramètre de réécriture. Nous allons lancer vos propres réécritures.

'rewrite'=>false;

Étape 2: définir des règles de page détaillées. Cela force les pages normales à avoir leurs propres règles au lieu d'être un fourre-tout au bas de la page.

Étape 3: créez des règles de réécriture pour gérer vos cas d'utilisation.

Étape 4: Forcer manuellement les règles de vidage à se produire. Manière la plus simple: allez dans Paramètres-> permalien et cliquez sur le bouton Enregistrer. Je préfère cela à une méthode d'activation de plugin pour mon propre usage, car je peux forcer les règles à vider chaque fois que je change les choses.

Donc, codez l'heure:

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

N'oubliez pas qu'après avoir ajouté ce code, vous devez l'avoir actif lorsque vous allez vider les règles de permalien (en enregistrant la page sur Paramètres-> Permaliens)!

Après avoir vidé les règles et enregistré dans la base de données, alors / quoi que ce soit devrait aller sur votre page forum = quelle que soit la taxonomie.

La réécriture des règles n'est vraiment pas si difficile si vous comprenez les expressions régulières. J'utilise ce code pour m'aider lors du débogage:

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

De cette façon, je peux voir les règles actuelles en un coup d'œil sur ma page. N'oubliez pas que compte tenu de n'importe quelle URL, le système commence en haut des règles et les parcourt jusqu'à ce qu'il en trouve une qui correspond. La correspondance est ensuite utilisée pour réécrire la requête dans un ensemble? Key = value plus normal. Ces clés sont analysées dans ce qui se trouve dans l'objet WP_Query. Simple.

Edit: Note latérale, cette méthode ne fonctionnera probablement que si votre structure de publication personnalisée normale commence par quelque chose qui n'est pas un fourre-tout, comme% category% ou quelque chose comme ça. Vous devez le démarrer avec une chaîne statique ou numérique, comme% year%. C'est pour l'empêcher d'attraper votre URL avant qu'elle n'atteigne vos règles.

Otto
la source
Si vous voulez un débogage plus facile de vos règles de réécriture, je recommande (encore) mon plugin d'analyseur de réécriture , qui vous permet d'essayer les règles et de voir les variables de requête à la volée.
Jan Fabry
Malheureusement, le système de réécriture d'URL actuel force l'aplatissement de tous les modèles d'URL potentiels dans une grande liste par rapport à la structure arborescente inhérente des chemins d'URL. La configuration actuelle ne peut pas facilement correspondre à un tableau de littéraux tels que des catégories ou des noms de forum ; comme vous le savez, toutes les URL "Page" doivent être évaluées en premier. La correspondance par segment de chemin et la correspondance de plusieurs façons (tableau de littéraux, catégories, balises, termes fiscaux, noms d'utilisateur, types de publication, noms de publication, rappels, crochets de filtre et enfin RegEx) augmenteraient la complexité et seraient plus faciles. comprendre.
MikeSchinkel
Mike: En fait, ce n'est pas plus facile à comprendre du tout, car je n'ai pas le premier indice WTF dont vous parlez. Vos idées de routage d'URL sont déroutantes et difficiles, et comme vous le savez probablement, je ne suis pas d'accord avec elles. La recherche plate a plus de sens et est plus flexible que vous ne le pensez. La plupart des gens ne veulent pas de toute cette complexité inutile dans leurs URL, et presque personne n'en a besoin non plus.
Otto
Merci, mais je pense que j'ai déjà essayé cela avant ( wordpress.stackexchange.com/questions/9455/… )
onetrickpony
Heureusement WordPress Réponses permet maintenant des gens qui font le contrôle de défaut de leurs URL d'avoir enfin une voix, et ils semblent être beaucoup (100+). Mais je respecte le fait que vous ne puissiez pas suivre mon exemple avant une mise en œuvre complète. Je prédis qu'une fois que l'approche que je préconise est entièrement implémentée dans un plugin et après environ 6 à 12 mois, elle sera devenue le moyen préféré des sites CMS basés sur WordPress pour router leurs URL. Reprenons donc ce débat dans environ 9 mois.
MikeSchinkel
4

Vous ne pourrez pas le faire en utilisant WP_Rewrite seul, car il ne peut pas faire la distinction entre les slugs de terme et les post-slugs.

Vous devez également vous connecter à la «demande» et empêcher le 404, en définissant la variable post-requête au lieu de la taxonomie.

Quelque chose comme ça:

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

Notez que la taxonomie doit être définie avant le type de message.

Ce serait un bon moment pour souligner que le fait d'avoir une taxonomie et un type de publication avec la même requête var est une mauvaise idée.

De plus, vous ne pourrez pas accéder aux publications qui ont le même slug que l'un des termes.

scribu
la source
Convenu qu'avoir une taxonomie et un type de publication avec la même requête var est une mauvaise idée, mais cela pourrait impliquer que les personnes ayant une taxonomie et un type de publication avec le même nom sont une mauvaise idée, ce qui n'est pas le cas. Si vous utilisez le même nom, seul l'un des deux doit avoir une requête var.
MikeSchinkel
2

Je jetterais un œil au code du plugin de chats de haut niveau:

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

Vous pouvez facilement l'adapter afin qu'il recherche votre limace de taxonomie personnalisée en changeant le

$category_base = get_option('category_base');

sur la ligne 74 à quelque chose comme:

$category_base = 'forums';
Pabline
la source
Pourrait fonctionner pour les catégories, mais pas pour les taxonomies personnalisées (au moins dans wp 3.1) ... J'ai réussi à changer les URL, mais j'obtiens 404 erreurs
onetrickpony
2

Je suggère de jeter un œil au plugin Custom Post Permalinks . Je n'ai pas le temps de tester pour le moment, mais cela peut vous aider avec votre situation.

Travis Northcutt
la source
il ne le fait pas, il ne gère que les publications, pas les taxonomies, et même si c'était le cas, je devrais ajouter une sorte de préfixe avant %forum%, c'est exactement ce que j'essaie d'éviter ...
onetrickpony
2

Comme je connais bien votre autre question , je vais répondre en gardant cela à l'esprit.

Je n'ai pas testé cela du tout, mais cela pourrait fonctionner si vous l'exécutez une fois juste après avoir enregistré toutes les infrastructures que vous souhaitez .:

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

Ce que cela fait: il supprime les règles de réécriture générées à partir des rubriques permalien du flux normal du tableau de règles et les fusionne à la fin du tableau. Cela empêche ces règles d'interférer avec d'autres règles de réécriture. Ensuite, il force les règles de réécriture verbeuses (chaque page obtient une règle individuelle avec une expression régulière spécifique). Cela empêche les pages d'interférer avec les règles de votre sujet. Enfin, il exécute un vidage dur (assurez-vous que votre fichier .htaccess est accessible en écriture, sinon cela ne fonctionnera pas) et enregistre le très grand tableau très compliqué de règles de réécriture.

John P Bloch
la source
essayé, rien ne change
onetrickpony
2

Je ne sais pas si cela fonctionnera pour les taxonomies, mais cela a fonctionné pour les types de publication personnalisés

Bien qu'il n'ait pas été mis à jour depuis 2 ans, le plugin ci-dessous a fonctionné pour moi: http://wordpress.org/plugins/remove-slug-from-custom-post-type/

Pour info, je lance WP 3.9.1avec WP Types1.5.7

Max
la source
2

Utiliser une barre oblique comme valeur pour slug ... 100% de travail

'rewrite' => array(
    'slug'       => '/', 
    'with_front' => FALSE
 ),
Sathish Jayaraman
la source
2
pas tout à fait, cela provoque tout le pagetype de message à 404.
Milo