web-dev-qa-db-fra.com

Meta_Query comme un moyen de configurer les permaliens CPT - est-ce une bonne chose?

Je crée un site Web assez complexe pour un magazine littéraire. Pour ses types de publication personnalisés, Issue et Event, je voulais une structure de lien permanent différente de celle utilisée pour les publications (qui est basique http://example.com/%postname% ).

Ma tâche n’était pas facile et j’ai réussi à trouver une solution, même s’il s’agissait d’une solution assez complexe. Comme je ne suis pas sûr que ce soit idéal, j'aimerais connaître l’opinion de WP utilisateurs, qui comprennent mieux que moi le cœur sombre du $wp_query interne. Si WP Stackexchange n'est pas le lieu approprié pour une révision de code, veuillez m'envoyer ailleurs.

Je devais atteindre les objectifs suivants:

  1. Les URL reflètent les dates pertinentes pour les TPC - telles que http://example.com/event/%eventyear%/%eventmonth%/%postname% et http://example.com/event/%year % /% issue-num%
  2. La dernière partie de l'URL peut être dupliquée - telle que http://example.com/issue/2016/5 et http://example.com/issue/2015/5
  3. Les CPT ont des archives - les archives des numéros de 2012 seront disponibles sur http://example.com/issue/2012 .
  4. La dernière partie de l'URL devrait être générée à partir de quelque chose d'autre que le titre (peut-être une méta-valeur).

D'abord, j'ai enregistré mon CPT:

$cpt_args = array(
  'label' => 'Issue',
  'hierarchical' => false,
  'has_archive' => true,
  'rewrite' => array('slug' => 'issue/%issueyear%'),
);

Ensuite, j'ai enregistré mes tags de réécriture:

add_action('init', function() use ($tags) {
    add_rewrite_tag("%issueyear%");
});

Ensuite, j'ai enregistré ma "traduction rewrite_tag" de `% issueyear% dans une méta valeur personnalisée:

$func = function($permalink, $post) {

  if (strpos($permalink, "%issueyear%")) {
    $tags_used[] = $t;
  }

  $issue_start = get_post_meta($post->ID, 't_issue_start', true);

  $old = basename($permalink);
  $new = $this->date->get_time('Y', $issue_start);
  $permalink = str_replace($old, $new, $permalink);

  return $permalink;

};

add_action('post_link', $func, 10, 2);
add_action('post_type_link', $func, 10, 2);

Ensuite, j'ai ajouté mes règles de réécriture, en transmettant ma méta-valeur à l'URL.

add_action('init', function() {
    add_rewrite_rule(
        "^issue/([0-9]{4})/([0-9]{1,})/?",
        'index.php?post_type=issue&year=$matches[1]&cibulka_key=cibulka_slug&cibulka_val=$matches[2]',
        'top'
    );
};

add_action('query_vars', function($vars) {
    $vars[] = 'cibulka_key';
    $vars[] = 'cibulka_val';
    return $vars;
});

L'URL http://example.com/2016/5 me donnerait le modèle archive.php. Donc là, j'ai inclus un seul modèle, si $wp_query a cibulka_key défini. include_template_with_var est ma fonction, permettant de passer des paramètres à des modèles.

/** Archive.php */

global $wp_query;
if (isset($wp_query->query['cibulka_key'])) {
    $meta_value = $wp_query->query['cibulka_val'] . '_' . $wp_query->query['year'];
    $args = array(
        'post_type' => $wp_query->query['post_type'],
        'meta_key' => $wp_query->query['cibulka_key'],
        'meta_value' => $meta_value,
        'posts_per_page' => 1
    );
    $posts = get_posts($args);
    if (!empty($posts)) {
        $data = array(
            'id' => $posts[0]->ID,
            'post' => $posts[0]
        );
    } else {
        $data = array();
    }
    include_template_with_var('single.php', $data);
} else {
    // Do normal archive stuff
}

Un seul modèle de publication avec $data['id'] est servi à la place de archive.php.

Note: Ce n'est pas mon code actuel, je l'ai beaucoup simplifié pour les besoins de ma question.


Cela donne les 4 points dont j'avais besoin, mais je ne suis pas sûr que ce soit la bonne façon d'aborder cette question: performance (il y aura BEAUCOUP d'événements), norme, etc. avec cette sollicitation (car cela aura des conséquences assez lourdes, comme la configuration du schéma d'URL a), j'aimerais entendre quelques opinions.

Jusqu'ici, j'ai découvert ces mises en garde:

  1. WP reconnaît le contenu de http://example.com/issue/5 en tant que archive, même si ce doit être single. Donc is_single(), is_single('issue') return false et le résultat de body_class() prête à confusion.

Je vais les ajouter ici car d'autres apparaissent.


Merci beaucoup d'avance!


Modifier 1

J'ai supprimé les conditions de Archive.php et l'ai remplacé par des filtres. Cette mise en garde a résolu 1 et a supprimé la logique de mes modèles. Yay!

// Change global `wp_query` to retrieving the post by meta query AND mark it as single (not archive)
add_action('pre_get_posts', function() {
    global $wp_query;
    if (!isset($wp_query->query['cibulka_key'])) { return; }

    switch ($wp_query->query['post_type']) {
        case 'issue':
            $meta_value =  $wp_query->query['cibulka_val'] . '_' . $wp_query->query['year'];
        break;
    }

    $wp_query->set('meta_key', $wp_query->query['cibulka_key']);
    $wp_query->set('meta_value', $meta_value);

    $wp_query->is_singular = true;
    $wp_query->is_single = true;
    $wp_query->is_archive = false;

    remove_all_actions ( '__after_loop');

});

// Use single.php rather than archive.php, if global `$wp_query` contains my custom properties
add_filter('template_include', function($template) {

    global $wp_query;
    if (!isset($wp_query->query['cibulka_key'])) { return $template; }

    $single_tmplt = locate_template('single.php');
    return $single_tmplt;

});

Pour une raison quelconque, body_class() n'ajoute pas single-issue classe CSS de cette façon, mais ce n'est rien ...

add_filter('body_class', function($body_class) {
    if (is_single() && get_post_type() === 'issue') {
        $body_class[] = 'single-issue';
    }
    return $body_class;
}, 11, 1);

... ne peut pas réparer. :)

6
Petr Cibulka

Comme il s’agit d’une question de performance, vous pourriez peut-être éviter d’utiliser des clés méta en stockant/récupérant ces données d’une autre manière et en évitant de définir un champ méta distinct pour le faire correspondre à ...

une. Vous pouvez obtenir l'année à partir du $post->post_date... publié. Ainsi, lorsque vous effectuez la requête, utilisez simplement l'argument datename__:

$args = array(
    'post_type' => $wp_query->query['post_type'],
    'date_query' => array( array('year' => $wp_query->query['year']) ),
    'posts_per_page' => -1
);
$posts = get_posts($args);

b. Vous pouvez définir le numéro du problème à l'aide du champ d'attributs de page $post->menu_order. Cela aurait l’avantage supplémentaire de donner sa propre métabox d’écran de post-écriture (et même la modification rapide sur l’écran de liste de post) sans aucun code supplémentaire, et correspond bien à l’objet du champ (classer les questions comme vous le feriez avec des pages). ajouter un support au type de message lors de son inscription, ou vous pouvez également faire:

add_post_type_support('issue','page-attributes');

... alors, en suivant le code ci-dessus, vous auriez:

if (!empty($posts)) {
    foreach ($posts as $post) {
        if ($post->menu_order == $wp_query->query['cibulka_val']) {
            $data['id'] = $post->ID;
        }
    }
}

L'avantage est que post_date et menu_order sont tous deux dans la rangée de la table posts(et donc également de l'objet $post également), de sorte que SQL n'a pas besoin d'accéder à la table postmetade cette façon pour correspondre aux données ... bien que ce soit probablement une jolie petit gain si on le multiplie par des milliers qui sait ... vous pourriez toujours recevoir des centaines de messages pour cette année et les boucler de cette façon.

Vous pouvez donc utiliser les codes menu_order et post_date tels que mentionnés, mais possédez-vous une requête personnalisée uniquement pour obtenir l'ID de publication - qui est vraiment est un moyen extrêmement efficace de le faire - rien de plus rapide ici. par exemple:

if (isset($wp_query->query['cibulka_key'])) {
    global $wpdb;
    $query = "SELECT ID FROM ".$wpdb->prefix."posts 
              WHERE YEAR(post_date) = '".$wp_query->query['year']."' 
              AND menu_order = '".$wp_query->query['cibulka_val']."' 
              AND post_status = 'publish'";
    $postid = $wpdb->get_var($query);
    if ($postid) {
        $data['id'] = $postid;
        // if this is really needed here?
        $data['post'] = get_post($postid);
    } else {$data = array();}
    include_template_with_var('single.php', $data);
}
3
majick