it-swarm.com.de

Meta_Query als Methode zum Einrichten von CPT-Permalinks - ist das eine gute Sache?

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:

  1. URLs geben die für CPTs relevanten Daten wieder, z. B. http://example.com/event/%eventyear%/%eventmonth%/%postname% und http://example.com/event/%year % /% issue-num%
  2. Der letzte Teil der URL kann dupliziert werden, z. B. http://example.com/issue/2016/5 und http://example.com/issue/2015/5
  3. CPT hat Archive - das Archiv der 2012 Ausgaben 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 (Meta-Wert vielleicht).

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_querycibulka_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:

  1. WP erkennt Inhalte auf http://example.com/issue/5 als 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!


Bearbeiten 1

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. :)

6
Petr Cibulka

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);
}
3
majick