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:
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:
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!
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. :)
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 date
name__:
$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 postmeta
de 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);
}