Regla de reescritura personalizada para tipos de post personalizados jerárquicos

15 ene 2016, 04:43:53
Vistas: 16.7K
Votos: 1

Trabajando en un sitio de un cliente me he encontrado con mi peor enemigo... la reescritura de URLs en WP :(. Realmente no puedo resolverlo y después de muchos días intentándolo y buscando, no logro que funcione.

Lo que estoy tratando de conseguir es un enlace permanente como este company.com/product/%nombre-del-producto%/ sin importar la profundidad del anidamiento /parent/child/. Necesito las funcionalidades de las páginas jerárquicas por otros motivos, pero no deberían aparecer en la URL.

Aquí está mi configuración:

  • Wordpress 4.4.1
  • Ajustes de enlaces permanentes establecidos en Nombre de la entrada

  • Creé un tipo de post personalizado jerárquico product con el parámetro rewrite establecido en false.

    $args = array(
        'label'                 => __( 'Product', 'domain' ),
        'description'           => __( 'Productos de la compañía', 'domain' ),
        'labels'                => $labels,
        'supports'              => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'page-attributes' ),
        'taxonomies'            => array( 'category', 'post_tag' ),
        'hierarchical'          => true,
        'public'                => true,
        'show_ui'               => true,
        'show_in_menu'          => true,
        'menu_position'         => 5,
        'show_in_admin_bar'     => true,
        'show_in_nav_menus'     => false,
        'can_export'            => true,
        'has_archive'           => true,
        'exclude_from_search'   => false,
        'publicly_queryable'    => true,
        'rewrite'               => false,
        'capability_type'       => 'page',
    );
    register_post_type( 'product', $args );
    

En este punto los productos no tienen un enlace permanente bonito, se ven así y ambos funcionan:

  1. Primer nivel: company.com/?product=mi-primer-producto -> ok
  2. Segundo nivel: company.com/?product=mi-primer-producto/producto-hijo -> ok

Después de eso, registré la regla de reescritura y el permastruct luego limpié las reglas de reescritura guardando cambios en la página de ajustes de enlaces permanentes

    function bvt_product_rewrite_rule() {
        add_rewrite_rule( 
            '^product/([^/]+)/?$',
            'index.php?post_type=product&pagename=$matches[1]',
            'top'
        );
        add_permastruct( 'product', '/product/%product%/' );
    }
    add_action( 'init', 'bvt_product_rewrite_rule', 10 );

Ahora los enlaces permanentes se mostraban correctamente para la página de primer nivel pero no para la página hija. Además ambos niveles me daban un error 404.

  1. Primer Nivel: company.com/product/mi-primer-producto/ -> 404
  2. Segundo Nivel: company.com/product/mi-primer-producto/producto-hijo/ -> 404

También probé una solución dada aquí https://wordpress.stackexchange.com/a/101077/86838 donde permitía eliminar el slug del padre del enlace permanente del hijo, pero aún no tuve éxito

function bvt_product_flatten_hierarchies( $post_link, $post ) {
    if ( 'product' != $post->post_type ) {
        return $post_link;
    }

    $uri = '';
    foreach ( $post->ancestors as $parent ) {
        $uri = get_post( $parent )->post_name . "/" . $uri;
    }

    return str_replace( $uri, '', $post_link );
}
add_filter( 'post_type_link', 'bvt_product_flatten_hierarchies', 10, 2 );

Instalé dos plugins debug-bar y monkeyman-rewrite-analyzer para analizar la reescritura y la consulta, pero sigo sin poder hacer que funcione.

Mi último intento fue con el plugin wp-permastructure que permite establecer enlaces permanentes personalizados para tipos de post personalizados. Lo cual habilita el ajuste personalizado permastruct en el array de opciones rewrite de register_post_type.

[...]
'publicly_queryable'    => true,
'rewrite'               => array(
    'permastruct'   => '/%postname%/',
),
'capability_type'       => 'page',
[...]

Con este ajuste y deshabilitando tanto la reescritura/permatruct personalizada anterior como flatten_hierarchies, funcionó como se describe en el plugin

  1. Primer Nivel: company.com/mi-primer-producto/ -> ok
  2. Segundo Nivel: company.com/producto-hijo/ -> ok

Pero ahora falta el nivel /product/, y si intento ponerlo en la configuración 'permastruct' => '/product/%postname%/' me devuelve un error 404.

Lo que me lleva a mi pregunta principal.

¿Es posible hacer lo que necesito o solo tan cerca como pude llegar con el último plugin?

Gracias de antemano a cualquiera que se tome el tiempo de responderme, y quizás salvarme de mi pobre situación. Gracias

PD: No puedo insertar más de dos enlaces en el cuerpo así que aquí están los enlaces mencionados de los plugins:

https://wordpress.org/plugins/debug-bar/
https://wordpress.org/plugins/monkeyman-rewrite-analyzer/
https://wordpress.org/plugins/wp-permastructure/
2
Comentarios

Una cosa que afectará las reescrituras son las templates. Asegúrate de que nada se interponga en tu camino ahí también. Personalmente, me mantendría alejado de los plugins al principio e intentaría que tu reescritura funcione con el argumento rewrite del CPT.

Nathan Powell Nathan Powell
15 ene 2016 04:49:10

Hola Nathan, estoy de acuerdo contigo acerca de los plugins. Siempre trato de hacerlo lo más simple posible pero esta vez estaba bastante desesperado.

Kuuak Kuuak
15 ene 2016 10:54:12
Todas las respuestas a la pregunta 1
1

Estás muy cerca. Tu regla de reescritura está usando la variable de consulta incorrecta, pagename debería ser simplemente name.

Aquí tienes una versión que funciona para mí en una instalación limpia de WordPress 4.4.1 con el tema Twenty Sixteen:

function bvt_product_init() {
    $args = array(
        'label'                 => __( 'Producto', 'domain' ),
        'description'           => __( 'Productos de la empresa', 'domain' ),
        'supports'              => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'page-attributes' ),
        'taxonomies'            => array( 'category', 'post_tag' ),
        'hierarchical'          => true,
        'public'                => true,
        'show_ui'               => true,
        'show_in_menu'          => true,
        'menu_position'         => 5,
        'show_in_admin_bar'     => true,
        'show_in_nav_menus'     => false,
        'can_export'            => true,
        'has_archive'           => true,
        'rewrite'               => array( 'slug' => 'producto' ),
    );
    register_post_type( 'product', $args );

    add_rewrite_rule( 
        '^producto/([^/]+)/?$',
        'index.php?post_type=product&name=$matches[1]',
        'top'
    );
}
add_action( 'init', 'bvt_product_init' );

function bvt_product_flatten_hierarchies( $post_link, $post ) {
    if ( 'product' != $post->post_type ) {
        return $post_link;
    }
    $uri = '';
    foreach ( $post->ancestors as $parent ) {
        $uri = get_post( $parent )->post_name . "/" . $uri;
    }
    return str_replace( $uri, '', $post_link );
}
add_filter( 'post_type_link', 'bvt_product_flatten_hierarchies', 10, 2 );

Un problema potencial a tener en cuenta: los tipos de post jerárquicos te permiten crear posts con el mismo slug que tienen diferentes padres. Normalmente esto funciona porque se consultan por sus rutas padre/hijo. Sin tener esa relación padre/hijo en la estructura de URLs, puedes crear posts que nunca podrán ser consultados en el front-end si el slug coincide con un post existente. Solo algo para tener presente.

15 ene 2016 07:44:05
Comentarios

Hola Milo, muchas gracias, de hecho estuvo muy cerca. Cambiando solo pagename por name funcionó. Sobre el problema del mismo slug, me encargué de eso forzando un número sku único en el slug. Gracias de nuevo

Kuuak Kuuak
15 ene 2016 10:56:40