web-dev-qa-db-fra.com

Page d'archive personnalisée basée sur un tableau de catégories et de balises

Je cherche à créer une page d'archive personnalisée basée sur des tableaux passés contenant des catégories et des balises, par exemple:

http://www.mydomain.com/archives-custom.php?c=cat1,cat2,cat3&t=tag1,tag2,tag3

La question est la suivante: dois-je créer des taxonomies personnalisées ou des requêtes personnalisées pour avoir généré une page d'archive associée?

À titre d’exemple, le message "Test" a été classé dans les catégories "Actualités" et "Entreprises" et étiqueté "Asie" et "Chine". À partir de ce message, je souhaite créer un lien vers des archives contenant d'autres messages classés comme "Actualités" et "Entreprises" et étiquetés "Asie" et "Chine".

Dans ce cas, le lien ressemblera à

http://www.mydomain.com/archives-custom.php?c=news+business&t=asia+china

Je pense que l'exemple clarifiera davantage ce que je veux réaliser.

1
JackTheKnife

Cela s'est avéré être un petit projet assez vaste pour moi.

IDÉE BASIQUE

Mon approche consistait à utiliser une page d'archive personnalisée, car cela semble être la meilleure approche ici. Le problème avec les taxonomies personnalisées est que si vous aviez une structure en place, le changement deviendrait une véritable expédition en désordre. En outre, j'essaie toujours de rester à l'écart des requêtes personnalisées si la requête principale peut effectuer quelque chose.

Le gros problème avec une page d’archive personnalisée (que j’ai accompagnée) est que Wordpress ne propose pas cette configuration par défaut. J'ai dû écrire beaucoup de code personnalisé (et obtenir de l'aide pour la partie réécriture, grâce à @Milo ici) pour atteindre l'objectif final.

FLUX DE TRAVAIL

( J'ai bien commenté le code, donc je ne vais pas approfondir ici, je vais juste aborder des points importants )

Il s'agit d'un flux de travail très basique de l'ensemble du processus

  • Obtenez les balises et les catégories de l'article unique en cours. Pour une opération plus simple et plus rapide, obtenez uniquement les identifiants des balises et des catégories.

  • Ajoutez la balise et les identifiants de catégorie à deux chaînes distinctes. Ceci est important car ces deux chaînes seront utilisées ultérieurement dans un tax_query pour afficher les publications pertinentes.

  • Construisez un lien vers la page d'archive virtuelle. Je suis allé avec une URL qui ressemblera à ceci: http://mysite.com/custom-archive/.

  • Définissez deux nouveaux query_vars, cq et tq qui contiendront respectivement une chaîne d'ID de catégorie et d'ID de balise. Ce query_vars ainsi que la valeur respective seront ajoutés à l'URL du lien. Ces valeurs seront ensuite utilisées pour construire notre requête

  • Ajoutez le query_vars personnalisé à l'URL du lien vers notre page d'archive personnalisée.

  • Réécrivez l'URL afin que Wordpress puisse l'interpréter. ( Cette section, j’ai reçu de l’aide de @Milo. Veuillez consulter sa réponse à ma question ici)

  • Définissez la condition parse_query sur false (il n'est pas vraiment nécessaire de toutes les exécuter, mais c'est ce que je fais, il s'agit simplement de mieux prévenir que guérir). Le plus important ici et celui que vous devez définir est is_home() car les règles de réécriture personnalisées réécrivent l'URL à la maison. is_home() renvoie true sur cette page personnalisée, ce que nous ne souhaitons pas.

  • Pas nécessaire, mais très pratique, définissez une nouvelle balise conditionnelle avec parse_query et créez également une fonction conditionnelle, is_custom_archive() pour économiser sur le code inutile.

  • Pas nécessaire, mais définissez un modèle personnalisé à utiliser lorsque l'un des deux query_vars est présent dans l'URL. J'ai utilisé custom-archive.php. Vous pouvez simplement faire une copie de index.php et le renommer en custom-archive.php. Si vous voulez l'omettre, Wordpress utilisera simplement index.php pour afficher les posts

  • Utilisez pre_get_posts pour définir le tax_query sur la requête principale afin d’obtenir des publications en fonction des valeurs des deux query_vars personnalisés. Cela modifiera la requête principale en conséquence

  • Pour un meilleur intérêt, définissez un 404 si http://mysite.com/custom-archive/ est directement visité, cela aura le même effet que si vous visitiez http://mysite.com/category/. Si vous omettez cette partie et que vous visitez directement http://mysite.com/custom-archive/, vous obtiendrez tous les articles comme vous le feriez sur la page d'accueil, cette page fonctionnant alors de la même manière que la page d'accueil.

QUELQUES NOTES IMPORTANTES

  • Vous devez vider vos permaliens une fois le processus terminé, sinon cela ne fonctionnera pas

  • Modifiez le nom du modèle, le nom du lien et les noms query_vars en fonction de vos besoins.

  • Dans l'action pre_get_posts, ajoutez tous les autres paramètres nécessaires. Voir WP_Query pour tous les paramètres disponibles

  • Les règles de réécriture personnalisées définissent post comme type de publication par défaut. Vous pouvez le modifier à votre guise. En outre, le code utilise des balises et des catégories par défaut, mais peut très facilement être modifié pour utiliser d'autres taxonomies. J'ai essayé de garder le code aussi général que possible pour rendre la personnalisation aussi facile que possible

  • Plus important encore, ce code nécessite au moins PHP 5.4. Cela ne fonctionnera pas sans modification sur les anciennes versions. J'ai utilisé beaucoup de fermetures qui n'ont été introduites que dans PHP 5.3. L'autre syntaxe significative est l'utilisation de balises de type tableau ([]) introduites dans PHP 5.4. J'ai utilisé ceci à la place de array()

LE CODE

/**
 * Function to get the categories and tags from the current single post
 *
 * @uses wp_get_post_terms()
 * @see http://codex.wordpress.org/Function_Reference/wp_get_post_terms
 *
 * The category and tag ID's are returned which will be used as values for the new query_vars
 *
 * @return (array) $query_string Array of category and tag ID's, each in string format
*/

function get_category_and_tag_ids() {

    /**
     * Get the current single post ID.
     *
     * @uses get_queried_object_id();
     * 
     * $post global is omitted as it is not reliable. Rather use get_queried_object_id()
     * See below link for a complete explanation
     *
     * @link https://wordpress.stackexchange.com/q/167706/31545
    */ 
    $post_id = get_queried_object_id();

    // Set the taxonomies to use, in this case category and post_tag
    $taxonomies = ['category', 'post_tag'];

    foreach( $taxonomies as $taxonomy ) {

            // Use wp_get_post_terms() to get the terms (categories and tags)
        $terms = wp_get_post_terms( $post_id, $taxonomy, ['orderby' => 'id', 'fields' => 'ids'] );

        if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {

            foreach( $terms as $term ) {

                if( $taxonomy == 'category' ) {

                    $categories[] = $term;

                }else{

                    $tags[] = $term;

                }

            }

        }

    }

    // Check if $categories is set, then convert the array into a comma separated string
    if( isset( $categories ) ) {

        $category_string_ids = implode( ',', $categories );

    }

    // Check if $tags is set, then convert the array into a comma separated string
    if( isset( $tags ) ) {

        $tag_string_ids = implode( ',', $tags );

    }

    // Returns $category_string_ids if it is set or empty string on failure 
    $category_query_string = ( isset( $category_string_ids ) ) ? $category_string_ids : '';

    // Returns $tag_string_ids if it is set or empty string on failure 
    $tag_query_string = ( isset( $tag_string_ids ) ) ? $tag_string_ids : '';

    //Create an array of category ids and tag ids to be returned
    $query_string = [
        'category_ids' => $category_query_string,
        'tags_ids' => $tag_query_string 
    ];

    return $query_string;

}

/**
 * Create a link to the virtual page which will be used to display the posts
 *
 * @uses get_home_url()
 * @return $permalink
 *
*/
function get_virtual_page_url() {

    $home_url = get_home_url();
    $slash = '/';
    $virtual_page = 'custom-archive';
    $permalink = get_home_url() . $slash . $virtual_page . $slash;

    return $permalink;

}

/**
 * Register two new query variables cq, tq 
 *
 * @see http://codex.wordpress.org/WordPress_Query_Vars
 *
*/ 
add_filter( 'query_vars', function ( $qvars ) {

    $qvars[] = 'cq';
    $qvars[] = 'tq';

    return $qvars;

});

/**
 * Gets and sets the category and/or tag id as values to the custom query_vars
 *
 * Values are only set if they exists, if no value exists, the query_vars in not set
 *
 * @return $qv
*/
function get_query_vars_values() {

    $custom_query_values = get_category_and_tag_ids();
    $cat_values = $custom_query_values['category_ids'];
    $tag_values = $custom_query_values['tags_ids'];

    switch ( true ) {

        case ( $cat_values && $tag_values ):

            $qv = [
            'cq' => $cat_values,
            'tq' => $tag_values
            ]; 

            break;

        case ( $cat_values && !$tag_values ):

            $qv = [
            'cq' => $cat_values,
            ]; 

            break;

        default: 

            $qv = [];

            break;

    }

    return $qv;

}

/**
 * Build our link to the virtual page, custom-archive
 *
 * Sets the query_vars plus the respective values to the virtual page link
 *
 * @uses add_query_arg()
 * @see http://codex.wordpress.org/Function_Reference/add_query_arg
 *
 * Returns the link if we have values returned by get_query_vars_values()
 * Returns an empty string if get_query_vars_values() have no values
 * @return (string) $link 
*/
function get_create_link_virtual_page() {

    $get_virtual_page_link = get_virtual_page_url();
    $get_query_values = get_query_vars_values();

    $link = ( $get_query_values ) ? add_query_arg( $get_query_values, $get_virtual_page_link ) : '';

return $link;

}

/**
 * Wrapper function to echo the returned value set by get_create_link_virtual_page() 
 * Use display_virtual_page_link() in single.php if you need a plain link
*/
function display_virtual_page_link() {

    echo get_create_link_virtual_page();

}

/**
 * Special thanks to @Milo for the rewrite rule
 * @see https://wordpress.stackexchange.com/a/174243/31545
 *
 * Create a new rewrite rule so that Wordpress can read interpret the page and adjust the main
 * query accordingly. This page will be rewritten to the homepage. Just a note, is_home() will
 * return true on this page. To counter act this, see function below
 *
 * @uses add_rewrite_rule()
 * @link http://codex.wordpress.org/Rewrite_API/add_rewrite_rule
 *
 * You should flush your permalinks for this to take effect or add flush_rewrite_rules()
 * inside a function that is hooked to 'after_switch_theme' or `register_activation_hook`
 * Never use flush_rewrite_rules() outside these two hooks, as it is a really expensive operation
*/ 
add_action( 'init', function () {

    add_rewrite_rule( 'custom-archive/?', 'index.php?post_type=post', 'top' );

});

/**
 * is_home() will return true on this page, and so might any other condition. This is annoying
 * as all functions meant for the home page will effect this custom page if you don't also check
 * for the custom query_vars
 *
 * To avoid this, set these conditions in the main query to false. This will avoid any extract
 * code and eliminate any unexpected output
 *
 * Set a new conditional tag, $wp_query->is_custom_archive if either of the two query_vars is present
 * @link http://codex.wordpress.org/Plugin_API/Action_Reference/parse_query
*/ 
add_filter( 'parse_query', function () {

    global $wp_query;

    //Set new conditional tag
    $wp_query->is_custom_archive = false;

    if( isset( $_GET['cq'] ) || isset( $_GET['tq'] ) ) {

        $wp_query->is_single = false;
        $wp_query->is_page = false;
        $wp_query->is_archive = false;
        $wp_query->is_search = false;
        $wp_query->is_home = false;
        $wp_query->is_custom_archive = true;

    }

}); 

/**
 * Wrapper function for $wp_query->is_custom_archive
 *
 * @return (bool) true on success, false on failure
*/ 
function is_custom_archive() {

    global $wp_query;
    return $wp_query->is_custom_archive;

}

/** 
 * Use a custom template to display our posts if needed. This can be omitted if you wish
 * In this case, index.php will be used if no template is set
 *
 * @uses template_include filter
 * @link http://codex.wordpress.org/Plugin_API/Filter_Reference/template_include
 *
 * @return $template
*/ 
add_filter( 'template_include', function ( $template ) {

    // Checks if we have one of the query_vars in the URL, if so, include custom_archive.php
    if( is_custom_archive() ) {

        $template = get_stylesheet_directory() . '/custom-archive.php';

    }
    return $template;

}, PHP_INT_MAX, 2 );

/**
 * Alter the main query on the custom archive page. We need to add a tax_query and get our custom
 * query_vars and use that in our tax query to get the correct posts
 *
 * @uses pre_get_posts action
 * @link http://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts
 *
*/ 
add_action( 'pre_get_posts', function ( $q ) {

        if ( !is_admin() && is_custom_archive() && $q->is_main_query() ) {

            if( isset( $_GET['cq'] ) && isset( $_GET['tq'] ) ) {

                $tax_query =  [
                    [
                        'taxonomy'          => 'category',
                        'terms'             => $_GET['cq'],
                        'include_children'  => false,

                    ],
                    [
                        'taxonomy'  => 'post_tag',
                        'terms'     => $_GET['tq'],
                    ]
                ];

            }elseif( isset( $_GET['cq'] ) && !isset( $_GET['tq'] )) {

                $tax_query = [
                    [
                        'taxonomy'          => 'category',
                        'terms'             => $_GET['cq'],
                        'include_children'  => false,
                    ]
                ];

            }

            $q->set( 'tax_query', $tax_query );

        }

    return $q;

});

/**
 * Gets the current page URL. Will be used to set a 404 if http://mysite.com/custom-archive
 * is visited
 *
 * @uses get_home_url
 * @return (string) $current_url
*/
function get_current_page_url() {

    global $wp;
    $trail_slash = '/';
    $current_url = home_url( add_query_arg( [], $wp->request ) ) . $trail_slash;

    return $current_url;

}

/**
 * Sets a 404 and redirects to a 404 page if http://mysite.com/custom-archive
 * is directly visited
*/ 
add_action( 'template_redirect', function () {

    global $wp_query; 

    if( !is_custom_archive() && get_current_page_url() == get_virtual_page_url() ) {

        $wp_query->set_404();
        status_header( 404 );
        nocache_headers();

    }

});

USAGE

Pour afficher votre lien dans single.php, vous pouvez procéder comme suit:

<?php echo '<a href="' .get_create_link_virtual_page() . '">Custom link</a>'; ?>
4
Pieter Goosen