Ich erstelle eine recht komplexe Website für ein Literaturmagazin. Für die benutzerdefinierten Post-Typen Issue und Event wollte ich eine andere Permalink-Struktur als für Posts (dies ist basic http://example.com/%postname% ).
Meine Aufgabe war nicht einfach und ich habe es geschafft, eine Lösung zu finden, obwohl es eine ziemlich verdrehte ist. Da ich nicht sicher bin, ob das ideal ist, würde ich gerne Meinungen von WP Benutzern hören, die das dunkle Herz von internem $wp_query
besser verstehen als ich. Wenn WP Stackexchange nicht der geeignete Ort für eine Codeüberprüfung ist, verweisen Sie mich bitte an eine andere Stelle.
Ich musste Folgendes erreichen:
Zuerst habe ich mein CPT registriert:
$cpt_args = array(
'label' => 'Issue',
'hierarchical' => false,
'has_archive' => true,
'rewrite' => array('slug' => 'issue/%issueyear%'),
);
Dann habe ich meine Rewrite-Tags registriert:
add_action('init', function() use ($tags) {
add_rewrite_tag("%issueyear%");
});
Dann habe ich meine "rewrite_tag translation" von `% issueyear% für einen benutzerdefinierten Meta-Wert registriert:
$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);
Anschließend habe ich meine Umschreiberegeln hinzugefügt und meinen Metawert an die URL übergeben.
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;
});
Die URL http://example.com/2016/5
würde mir die Vorlage archive.php geben. Dort habe ich eine einzelne Vorlage eingefügt, wenn für $wp_query
cibulka_key
festgelegt wurde. include_template_with_var
ist meine Funktion, die die Übergabe von Parametern an Vorlagen ermöglicht.
/** 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
}
Anstelle von archive.php wird eine einzelne Vorlage des Beitrags mit $data['id']
geliefert.
Hinweis: Dies ist nicht mein eigentlicher Code, ich habe ihn zum Zweck meiner Frage stark vereinfacht.
Dies erreicht alle 4 Punkte, die ich erreicht haben musste, ich bin mir jedoch nicht sicher, ob es der richtige Weg ist, dies anzugehen - in Bezug auf die Leistung (es wird VIELE Ereignisse geben), in Bezug auf den Standard usw. Also, bevor ich weitermache mit dieser sollution (da es ziemlich schwere folgen haben wird, wie das einrichten von urlschemata) würde ich gerne ein paar meinungen hören.
Bisher habe ich diese Vorbehalte entdeckt:
archive
, obwohl es single
sein sollte. Also is_single()
, is_single('issue')
geben false
zurück und das Ergebnis von body_class()
ist verwirrend.Ich werde sie hier hinzufügen, sobald mehr von ihnen erscheinen.
Vielen Dank im Voraus!
Ich habe Bedingungen aus Archive.php entfernt und durch Filter ersetzt. Dies löste den Vorbehalt 1 und entfernte die Logik aus meinen Vorlagen. 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;
});
Aus irgendeinem Grund fügt body_class()
die CSS-Klasse single-issue
nicht auf diese Weise hinzu, aber das ist nichts ...
add_filter('body_class', function($body_class) {
if (is_single() && get_post_type() === 'issue') {
$body_class[] = 'single-issue';
}
return $body_class;
}, 11, 1);
... kann nicht reparieren. :)
Da es sich um eine Leistungsfrage handelt, können Sie möglicherweise die Verwendung von Metaschlüsseln vermeiden, indem Sie diese Daten auf andere Weise speichern/abrufen und kein separates Metafeld festlegen, das mit ... übereinstimmt.
ein. Sie können das Jahr aus dem veröffentlichten $post->post_date
abrufen. Verwenden Sie also bei der Abfrage einfach das Argument date
:
$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. Sie können die Ausgabenummer über das Feld Seitenattribute $post->menu_order
festlegen. Dies hätte den zusätzlichen Vorteil, dass eine eigene Metabox für den Beitragsschreibbildschirm (und sogar die Schnellbearbeitung auf dem Beitragslistenbildschirm) ohne zusätzlichen Code zur Verfügung gestellt wird Fügen Sie dem Beitragstyp Unterstützung bei der Registrierung hinzu, oder tun Sie Folgendes:
add_post_type_support('issue','page-attributes');
... dann folgen Sie dem obigen Code:
if (!empty($posts)) {
foreach ($posts as $post) {
if ($post->menu_order == $wp_query->query['cibulka_val']) {
$data['id'] = $post->ID;
}
}
}
Der Vorteil ist, dass sich sowohl post_date
als auch menu_order
in der Zeile der Tabelle posts
befinden (und damit auch automatisch das Objekt $post
), sodass SQL nicht auf diese Weise auf die Tabelle postmeta
zugreifen muss, um mit den Daten übereinzustimmen kleiner Gewinn, wenn er mit Tausenden multipliziert wird, wer weiß ... Sie könnten immer noch Hunderte von Posts für dieses Jahr erhalten und diese auf diese Weise in einer Schleife wiedergeben.
Stattdessen könnten Sie den menu_order
und den post_date
wie erwähnt verwenden, aber haben Sie eine eigene benutzerdefinierte Abfrage, um die Post-ID zu erhalten - was wirklicheine supereffiziente Methode ist - es gibt wirklich eine nichts schneller hier. z.B:
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);
}