Eliminar la babosa de taxonomía de un enlace permanente de taxonomía jerárquica personalizada

21

Creé una taxonomía de 'foro', usando estas reglas:

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

En el front-end, las URL se ven así:

forums/general-discussion/sub-forum

¿Cómo puedo eliminar la babosa frontal ("foros")? Es decir, cambie las URL a:

general-discussion/sub-forum

Si paso un argumento slug vacío a register_taxonomy () funciona, pero eso causa problemas con los enlaces permanentes del tipo de publicación asociado con esta taxonomía

Pony de un solo truco
fuente
@One Trick Pony - ¿Has probado en lugar de dejarlo en 'slug' => 'forums'blanco simplemente eliminándolo por completo y solo teniendo 'rewrite' => array('with_front' => false, 'hierarchical' => true)? Creo que eso ha funcionado en el pasado para mí. También asegúrate de limpiar los enlaces permanentes.
eileencodes
intenté eso, y los enlaces permanentes se ven igual. Agregar lo 'slug' => ''hace funcionar, pero luego las publicaciones que usan esta taxonomía generan 404
onetrickpony
@One Trick Pony: además de 'discusión general', ¿qué otros segmentos de ruta de nivel superior necesitas?
MikeSchinkel
cualquiera %forum%debe ser un segmento de nivel superior
onetrickpony
@ One Trick Pony: solo esperaba que me dieras algunos otros segmentos de ruta de nivel superior para el contexto.
MikeSchinkel

Respuestas:

11

ACTUALIZAR

Desde que escribió este núcleo de WordPress ha agregado el 'do_parse_request'gancho que permite que el enrutamiento de URL se maneje con elegancia y sin la necesidad de extender la WPclase. Cubrí el tema en profundidad en mi charla de Atlanta WordCamp 2014 titulada " Enrutamiento de URL Hardcore " ; Las diapositivas están disponibles en el enlace.

RESPUESTA ORIGINAL

El diseño de URL ha sido importante durante más de una década; Incluso escribí un blog al respecto hace varios años. Y aunque WordPress es la suma es un software brillante, desafortunadamente su sistema de reescritura de URL tiene poco cerebro (en mi humilde opinión, por supuesto. :) De todos modos, ¡me alegra ver a las personas preocupadas por el diseño de URL!

La respuesta que voy a proporcionar es un complemento que llamo WP_Extendedque es una prueba de concepto para esta propuesta en Trac (tenga en cuenta que la propuesta comenzó como una cosa y evolucionó hacia otra, por lo que debe leer todo para ver dónde se dirigía)

Básicamente, la idea es subclasificar la WPclase, anular el parse_request()método y luego asignar la $wpvariable global con una instancia de la subclase. Luego, dentro de parse_request()usted, inspeccione la ruta por segmento de ruta en lugar de usar una lista de expresiones regulares que deben coincidir con la URL en su totalidad.

Entonces, para decirlo explícitamente, esta técnica inserta lógica frente a la parse_request()que verifica las coincidencias de URL a RegEx y, en cambio, primero busca coincidencias de términos de taxonomía, pero SOLO reemplaza parse_request()y deja intacto todo el resto del sistema de enrutamiento URL de WordPress, incluyendo y especialmente el uso de la $query_varsvariable.

Para su caso de uso, esta implementación solo compara segmentos de ruta URL con términos de taxonomía, ya que eso es todo lo que necesita. Esta implementación inspecciona los términos de la taxonomía que respetan las relaciones entre los términos padre-hijo y cuando encuentra una coincidencia, asigna la ruta URL (menos las barras inclinadas iniciales y finales) a $wp->query_vars['category_name'], $wp->query_vars['tag']y $wp->query_vars['taxonomy']& $wp->query_vars['term']omite el parse_request()método de la WPclase.

Por otro lado, si la ruta de URL no coincide con un término de una taxonomía que haya especificado, delega la lógica de enrutamiento de URL al sistema de reescritura de WordPress llamando al parse_request()método de la WPclase.

Para usar WP_Extendedpara su caso de uso, deberá llamar a la register_url_route()función desde el functions.phparchivo de su tema de la siguiente manera:

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

Lo que aquí es el código fuente del complemento:

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

Aunque para un sitio determinado, creo que esta técnica funciona de manera brillante, pero NUNCA debería usarse para que un complemento se distribuya en WordPress.org para que otros lo usen . Si está en el núcleo de un paquete de software basado en WordPress, entonces eso podría estar bien. De lo contrario, esta técnica debería limitarse a mejorar el enrutamiento de URL para un sitio específico .

¿Por qué? Porque solo un complemento puede usar esta técnica . Si dos complementos intentan usarlo, entrarán en conflicto entre sí.

Además, esta estrategia se puede ampliar para manejar genéricamente prácticamente todos los patrones de casos de uso que puedan ser necesarios y eso es lo que pretendo implementar tan pronto como encuentre el tiempo libre o un cliente que pueda patrocinar el tiempo que me tomaría construir implementaciones completamente genéricas.

CUEVA # 2

Escribí esto para anular, parse_request()que es una función muy grande, y es muy posible que haya omitido una o dos propiedades del $wpobjeto global que debería haber establecido ... Entonces, si algo funciona mal, hágamelo saber y estaré feliz de investigue y revise la respuesta si es necesario.

De todas formas...

MikeSchinkel
fuente
Después de escribir esto me di cuenta de que la prueba del lugar de categorías para los términos de la taxonomía en general lo que lo anterior no funciona para la 'forum'taxonomía sin embargo voy a revisarlo para trabajar el día de hoy ...
MikeSchinkel
Así que actualicé el código para abordar el problema que menciono en el comentario anterior.
MikeSchinkel
no puedo hacer que esto funcione ... ¿necesito cambiar las reglas de reescritura?
onetrickpony
@ One Trick Pony: un poco más de información de diagnóstico ayudaría. :) ¿Qué intentaste? ¿Qué sucede cuando ingresas las URL en tu navegador? ¿Por casualidad llamaste a tu taxonomía en 'forums'lugar de 'forum'? ¿Espera que las URL que enlazan con estas páginas cambien (en caso afirmativo, no es de extrañar, mi código no aborda la impresión de URL, solo el enrutamiento de URL)
MikeSchinkel
no, puedo cambiar las URL (creo que es la función term_link que necesito conectar para eso). site/rootforum/funciona, pero site/rootforum/subforum/no funciona (error 404) ...
onetrickpony
7

Simple, de verdad.

Paso 1: deja de usar el parámetro de reescritura. Vamos a lanzar sus propias reescrituras.

'rewrite'=>false;

Paso 2: establece reglas detalladas de página. Esto obliga a las páginas normales a tener sus propias reglas en lugar de ser un tema general en la parte inferior de la página.

Paso 3: Cree algunas reglas de reescritura para manejar sus casos de uso.

Paso 4: Forzar manualmente las reglas de descarga para que sucedan. La forma más fácil: vaya a configuración-> enlace permanente y haga clic en el botón Guardar. Prefiero esto sobre un método de activación de complemento para mi propio uso, ya que puedo forzar el vaciado de las reglas cada vez que cambio las cosas.

Entonces, código de tiempo:

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

¡Recuerde que después de agregar este código, debe tenerlo activo cuando elimine las reglas de enlace permanente (guardando la página en Configuración-> Enlaces permanentes)!

Después de haber vaciado las reglas y guardado en la base de datos, entonces / whatever debería ir a su foro = cualquier página de taxonomía.

Las reglas de reescritura realmente no son tan difíciles si entiendes las expresiones regulares. Utilizo este código para ayudarme cuando los depuro:

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

De esta manera, puedo ver las reglas actuales de un vistazo en mi página. Solo recuerde que, dada cualquier URL, el sistema comienza en la parte superior de las reglas y las sigue hasta encontrar una que coincida. Luego, la coincidencia se utiliza para reescribir la consulta en un conjunto? Key = value más normal. Esas claves se analizan en lo que va al objeto WP_Query. Simple.

Editar: Nota al margen, este método probablemente solo funcionará si su estructura de publicación personalizada normal comienza con algo que no es una atracción general, como% category% o algo así. Debe comenzar con una cadena estática o numérica, como% year%. Esto es para evitar que atrape su URL antes de que llegue a sus reglas.

Otón
fuente
Si desea una depuración más fácil de sus reglas de reescritura, le recomiendo (nuevamente) mi complemento analizador de reescritura , que le permite probar las reglas y ver las variables de consulta sobre la marcha.
Jan Fabry
Desafortunadamente, el sistema actual de reescritura de URL fuerza el aplanamiento de todos los patrones de URL potenciales en una lista grande frente a seguir la estructura de árbol inherente de las rutas de URL. La configuración actual no puede coincidir convenientemente con una variedad de literales , como categorías o nombres de foros ; como saben, obliga a evaluar todas las URL de "Página" primero. La coincidencia por segmento de ruta y la coincidencia de múltiples maneras (matriz de literales, categorías, etiquetas, términos de impuestos, nombres de usuario, tipos de publicaciones, nombres de publicaciones, devoluciones de llamada, ganchos de filtro y, finalmente, RegEx) se escalará mejor para la complejidad, y sería más fácil comprender.
MikeSchinkel
Mike: En realidad, eso no es más fácil de entender en absoluto, porque no tengo la primera pista de lo que estás hablando allí. Sus ideas sobre el enrutamiento de URL son confusas y difíciles, y como probablemente sepa, no estoy de acuerdo con ellas. La búsqueda plana tiene más sentido y es más flexible de lo que tiende a darle crédito. La mayoría de la gente no quiere toda esa complejidad innecesaria en sus URL, y casi nadie la necesita tampoco.
Otto
Gracias, pero creo que ya lo intenté antes ( wordpress.stackexchange.com/questions/9455/… )
onetrickpony
Afortunadamente WordPress Respuestas permite ahora que las personas que hacen el control de falta de sus URLs para finalmente tienen una voz, y que parecen ser muchos (más de 100). Pero respeto que no pueda seguir mi ejemplo antes de una implementación completa. Predigo que, una vez que el enfoque que estoy recomendando se implemente completamente en un complemento y después de aproximadamente 6-12 meses, se haya convertido en la forma preferida para que los sitios CMS basados ​​en WordPress enruten sus URL. Entonces, reanudemos este debate en unos 9 meses.
MikeSchinkel
4

No podrá hacer esto usando WP_Rewrite solo, ya que no puede distinguir entre las babosas de término y las babosas posteriores.

También debe conectarse a 'request' y evitar el 404, configurando la var de consulta posterior en lugar de la taxonomía.

Algo como esto:

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

Tenga en cuenta que la taxonomía debe definirse antes del tipo de publicación.

Este sería un buen momento para señalar que tener una taxonomía y un tipo de publicación con la misma consulta var es una mala idea.

Además, no podrá llegar a publicaciones que tengan la misma babosa que uno de los términos.

scribu
fuente
Convino en que tener una taxonomía y un tipo de publicación con la misma consulta var es una mala idea, pero eso podría implicar que las personas que tienen una taxonomía y un tipo de publicación con el mismo nombre es una mala idea, lo cual no es el caso. Si usa el mismo nombre, entonces solo uno de los dos debería tener una consulta var.
MikeSchinkel
2

Echaría un vistazo al código del complemento de gatos de nivel superior:

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

Puede adaptarlo fácilmente para que esté buscando su babosa de taxonomía personalizada cambiando el

$category_base = get_option('category_base');

en la línea 74 a algo como:

$category_base = 'forums';
Pabline
fuente
Podría funcionar para categorías, pero no para taxonomías personalizadas (al menos en wp 3.1) ... logré cambiar las URL, pero obtengo errores 404
onetrickpony
2

Sugeriría que eche un vistazo al complemento de enlaces permanentes personalizados . No tengo tiempo para hacer la prueba en este momento, pero puede ayudar con su situación.

Travis Northcutt
fuente
no lo hace, solo maneja publicaciones, no taxonomías, e incluso si lo hiciera, tendría que agregar algún tipo de prefijo antes %forum%, que es exactamente lo que estoy tratando de evitar ...
onetrickpony
2

Como estoy familiarizado con su otra pregunta , responderé con eso en mente.

No he probado esto en absoluto, pero podría funcionar si lo ejecuta una vez justo después de registrar todas las permastructs que desee:

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

Qué hace esto: elimina las reglas de reescritura generadas a partir de los enlaces permanentes de temas del flujo normal de la matriz de reglas y las vuelve a fusionar al final de la matriz. Esto evita que esas reglas interfieran con otras reglas de reescritura. A continuación, fuerza reglas de reescritura detalladas (cada página obtiene una regla individual con una expresión regular específica). Esto evita que las páginas interfieran con las reglas de su tema. Finalmente, ejecuta un vaciado forzado (asegúrese de que su archivo .htaccess sea grabable, de lo contrario, esto no funcionará) y guarda la gran variedad de reglas de reescritura muy complicadas.

John P Bloch
fuente
lo intenté, nada cambia
onetrickpony
2

Use una barra como valor para la babosa ... 100% de trabajo

'rewrite' => array(
    'slug'       => '/', 
    'with_front' => FALSE
 ),
Sathish Jayaraman
fuente
2
no del todo, esto hace que todo el pagetipo de publicación sea 404.
Milo