Meta_Query als Möglichkeit, CPT-Permalinks einzurichten – ist das eine gute Sache?

Ich erstelle eine ziemlich komplexe Website für eine Literaturzeitschrift. Für die benutzerdefinierten Post-Typen “Issue” und “Event” wollte ich eine andere Permalink-Struktur als für Posts (das ist einfach http://example.com/%postname% ).

Meine Aufgabe war nicht die leichte und ich habe es geschafft, eine Lösung zu finden, obwohl sie ziemlich verdreht ist. Da ich mir nicht sicher bin, ob das ideal ist, würde ich gerne Meinungen von WP-Benutzern hören, die das dunkle Herz von internen $wp_query besser $wp_query als ich. Wenn WP Stackexchange nicht der geeignete Ort für eine Codeüberprüfung ist, verweisen Sie mich bitte woanders hin.

Ich musste Folgendes erreichen:

  1. URLs geben die für CPT relevanten Daten wieder – wie http://example.com/event/%eventyear%/%eventmonth%/%postname% und http://example.com/event/%year%/%issue-num %
  2. Der letzte Teil der URL kann dupliziert werden – beispielsweise http://example.com/issue/2016/5 und http://example.com/issue/2015/5
  3. CPT hat Archive – Archiv der Ausgaben 2012 wird unter http://example.com/issue/2012 verfügbar sein.
  4. Der letzte Teil der URL sollte aus etwas anderem als dem Titel generiert werden (vielleicht Metawert).

Zuerst habe ich meinen 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-Übersetzung” von `% issueyear% in 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); 

Dann habe ich meine Rewrite-Regeln hinzugefügt und übergebe meinen Meta-Wert an 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; }); 

URL http://example.com/2016/5 würde mir archive.php Vorlage geben. Also, ich habe eine einzelne Vorlage $wp_query , wenn $wp_query cibulka_key gesetzt hat. include_template_with_var ist meine function, die das Übergeben 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 } 

Eine einzelne Vorlage von Post mit $data['id'] wird anstelle von archive.php geliefert.

Hinweis : Dies ist nicht mein tatsächlicher Code, ich habe ihn für meine Frage sehr vereinfacht.


Damit werden alle 4 Punkte erreicht, die ich benötigt habe, ich bin mir nur nicht sicher, ob es der richtige Weg ist, dies zu erreichen – Performance-weise (es wird eine Menge Events geben), Standardweise usw. Also bevor ich bleibe Mit dieser Lösung (da es ziemlich schwere Konsequenzen hat, wie das Einrichten eines URL-Schemas hat), würde ich gerne einige Meinungen hören.

Bis jetzt habe ich diese Vorbehalte entdeckt:

  1. WP erkennt Inhalte unter http://example.com/issue/5 als archive , obwohl es sich um ein single archive sollte. is_single() , is_single('issue') false und das Ergebnis von body_class() ist verwirrend.

Ich werde sie hier hinzufügen, wenn mehr von ihnen erscheinen.


Vielen Dank im Voraus!


Bearbeiten 1

Ich habe Konditionale aus Archive.php entfernt und durch Filter ersetzt. Dies triggerse Vorbehalte 1 und entfernte 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 body_class() keine single-issue CSS-class 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. 🙂

Solutions Collecting From Web of "Meta_Query als Möglichkeit, CPT-Permalinks einzurichten – ist das eine gute Sache?"

Da es sich um eine Performance-Frage handelt, können Sie es möglicherweise vermeiden, überhaupt Meta-Schlüssel zu verwenden, indem Sie diese Daten auf andere Weise speichern und nicht ein separates Meta-Feld erstellen müssen, das mit …

ein. Sie könnten das Jahr aus dem veröffentlichten $post->post_date … ermitteln. $post->post_date also bei der Abfrage einfach das date Argument:

 $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 mit den $post->menu_order . Dies hätte den zusätzlichen Vorteil, dass es eine eigene Post-Writing-Screen-Metabox (und sogar die Schnellbearbeitung auf dem Post-List-Bildschirm) ohne zusätzlichen Code gibt und gut mit dem Zweck des Feldes übereinstimmt (Probleme wie Sie würden Seiten bestellen) Fügen Sie dem Post-Typ Unterstützung bei der Registrierung hinzu, oder Sie können auch Folgendes tun:

 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 sowohl post_date als auch menu_order beide in der Zeile der posts Tabelle sind (und damit auch automatisch das $post Objekt), so dass SQL nicht auf die postmeta Tabelle zugreifen muss, um den Daten zu entsprechen … obwohl es wahrscheinlich ein Ziemlich kleiner Gewinn, wenn er von Tausenden multipliziert wird, die wissen … du könntest immer noch Hunderte von Beiträgen für dieses Jahr bekommen und sie auf diese Weise durchlaufen.

Stattdessen könnten Sie menu_order und post_date wie erwähnt verwenden, aber besitzen Sie eine benutzerdefinierte Abfrage, nur um die Post-ID zu bekommen – was wirklich eine super effiziente Art ist, dies zu tun – es gibt wirklich 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); }