wake-up-neo.com

So erstellen Sie eine Permalink-Struktur für Posts in einer bestimmten Kategorie

Ich erstelle ein Blog für einen einzelnen Autor, das mehrere Kategorien enthält. Eine Hauptkategorie ist "Filmkritiken".

Der Autor wird jedes Mal eine kurze Rezension hinterlassen, wenn er einen Film ansieht - auch wenn er den Film in der Vergangenheit gesehen und auf seiner Website in der Vergangenheit rezensiert hat.

Für die Kategorie "Filmkritiken" (nur diese Kategorie) muss ich eine Permalink-Struktur einrichten, z. B .:

  • /% category% /% postname% -% day %% monthnum %% year% /
  • / filmkritik/the-hateful-eight-01192016 /

Dadurch erhält jede Rezension desselben Films eine eindeutige URL.

Die restlichen Kategorien verwenden einfach /% postname% /

Ich bin mir zu 99% sicher, dass ich genau das vor ein paar Jahren getan habe, aber diese Website ist nicht mehr aktiv. Ich habe nichts über den Vorgang in meinen Notizen und kann über die Google-Suche WordPress keine Richtung finden .org Foren oder WordPress Answers.

3
Travis Pflanz

Ich möchte Ihnen keinen "alternativen" Ansatz geben. Ich bin mir ziemlich sicher, dass Sie dem nicht folgen werden, aber ich finde es interessant zu lesen.

OOP "Routing" -Ansatz

In WordPress werden "hübsche" URLs zu "hässlichen" URLs berechnet.

Die meisten Webframeworks (nicht nur PHP) verwenden jedoch das Konzept des "Routings", um eine URL einer "Aktion" (oder einem Controller) zuzuordnen.

Ich möchte Ihnen eine Idee geben, wie Sie diese rotierende Technik auf WordPress anwenden können, indem Sie einen OOP -Ansatz verwenden.

Die Schnittstelle

Zunächst schreiben wir eine Schnittstelle, um zu verdeutlichen, was ein Routenobjekt tun soll:

namespace MySite;

interface RouteInterface
{
    /**
     * Returns true when the route matches for the given url
     *
     * @return bool
     */
    public function matched();

    /**
     * Returns the WP_Query variables connected to the route for current url.
     * Should be empty if route not matched
     *
     * @return array
     */
    public function getQueryArgs();

    /**
     * Return the url for the route.
     * Variable parts of the url should be provided via $args param.
     *
     * @param array $args
     * @return string
     */
    public function getUrl(array $args = []);
}

Sehr einfach.

Bitte lesen Sie die Dokumentationsblöcke für Details.

Das URL-Objekt

Um eine URL "abzugleichen", müssen wir sie zuerst kennen. WordPress bietet keine Funktion oder Methoden dafür.

add_query_arg() ist nahe genug, wenn ein leeres Array übergeben wird.

Wenn WordPress jedoch in einem Unterordner installiert ist, wird auch der Unterordnerpfad zurückgegeben. Wir sollten ihn entfernen, da er nicht Teil der URL ist, mit der wir übereinstimmen möchten.

Lassen Sie uns ein Objekt für den Bereich schreiben.

namespace MySite;

class WordPressUri
{

    public function getUrl()
    {
        $url = trim(esc_url_raw(add_query_arg([])), '/');
        // check if wp is in a sub folder
        $homePath = trim(parse_url(home_url(), PHP_URL_PATH), '/');
        // remove WordPress subfolder if any
        if ($homePath) {
           $url = preg_replace('~^('.preg_quote($homePath, '~').'){1}~', '', $url);
        }

        return trim($url, '/');
    }
}

Ganz einfach, denke ich.

Das konkrete Routenobjekt

Jetzt haben wir alles, um mit dem Schreiben des konkreten Routenobjekts zu beginnen, das die Routenschnittstelle implementiert.

namespace MySite;

final class MovieReviewRoute implements RouteInterface {

    const REGEX = '^movie-review/([\w]+)-([0-9]{2})([0-9]{2})([0-9]{4})$';

    private $uri;
    private $postname = '';

    public function __construct(WordPressUri $uri = null) {
        $this->uri = $uri ? : new WordPressUri();
    }

    public function matched() {
        $matches = [];
        if (preg_match(self::REGEX, $this->uri->getUrl(), $matches) !== 1) {
            return false;
        }
        list(, , $day, $month, $year) = array_map('intval', $matches);
        if (checkdate($month, $day, $year)) {
            $this->postname = $matches[1];
            return true;
        }
        return false;
    }

    public function getQueryArgs() {
        return $this->postname ? ['name' => $this->postname] : [];
    }

    public function getUrl(array $args = []) {
        // check if postname was given, or as alternative a post object / post id
        $post = empty($args['post']) ? '' : get_post($args['post']);
        $postname = empty($args['postname']) ? '' : $args['postname'];
        if ( ! $postname && $post instanceof \WP_Post) {
            $postname = $post->post_name;
        }
        // if no date given, use post date if post was given, or just today
        if (empty($args['date'])) {
            $timestamp = $post instanceof \WP_Post
                ? strtotime($post->post_date)
                : current_time('timestamp');
            $args['date'] = date('dmY', $timestamp);
        }
        return home_url("movie-review/{$postname}-{$args['date']}");
    }
}

Es tut uns leid, wenn sich eine Menge Code an einem Ort befindet, aber es macht nichts "Besonderes".

Es wird nur das getan, was die Schnittstellen für den jeweiligen Fall vorgeben.

Einige Details zu den Methoden:

  • matched() verwendet einen regulären Ausdruck, um die URL mit dem gewünschten Format abzugleichen. Es wird auch geprüft, ob das Übereinstimmungsdatum ein gültiges Datum ist. Während des Vorgangs wird die Objektvariable postname gespeichert, damit ermittelt werden kann, welcher Beitrag übereinstimmt.

  • getQueryArgs() gibt nur die Abfrage var "name" zurück, die ausreicht, um einen Beitrag zu finden

  • getUrl() kombiniert die angegebenen Argumente, um eine URL zu erstellen, die der Route entspricht. Genau das, was die Schnittstelle will.

Der richtige Haken

Wir sind fast fertig. Wir haben alle Objekte, jetzt müssen wir sie verwenden. Das erste, was wir brauchen, ist ein Haken, um die Anfrage abzufangen.

Der richtige Ort ist 'do_parse_request' .

Wenn Sie an diesem Haken false zurückgeben, können wir WordPress daran hindern, die URL mithilfe von Umschreiberegeln zu analysieren. Darüber hinaus übergibt der Hook als zweites Argument eine Instanz der Klasse WP: Mit ihr können wir die Abfragevariablen festlegen, die wir benötigen und die unsere Route bei Übereinstimmung bereitstellen kann.

In Aktion

Der Code, den wir brauchen, um die Route abzugleichen:

namespace MySite;

add_filter('do_parse_request', function ($bool, \WP $wp) {

    $movieRoute = new MovieReviewRoute();
    // if route matched, let's set query vars and stop WP to parse rules
    if ($movieRoute->matched()) {
        $wp->query_vars = $movieRoute->getQueryArgs();
        $wp->matched_rule = MovieReviewRoute::REGEX;

        // avoid WordPress to apply canonical redirect
        remove_action('template_redirect', 'redirect_canonical');

        // returning false WP will not parse the url
        return false;
    }

    return $bool;
}, 10, 2);

Ich denke, dass es ziemlich einfach zu verstehen sein sollte.

Zwei Dinge zu beachten:

  • Ich habe die Funktion "redirect_canonical" aus 'template_redirect' entfernt. Andernfalls kann WordPress (das nichts über unsere Routen weiß) den Beitrag zu seiner "kanonischen" URL umleiten, die in der Standard-Permalink-Struktur festgelegt ist.

  • Ich setze $wp->matched_rule auf etwas Vorhersehbares: Dies ermöglicht es uns zu wissen, wann wir über unser Routenobjekt Abfrageargumente setzen.

URLs generieren

Wir haben die Route funktioniert, aber wir müssen Benutzer zu unserer Route senden. Also müssen wir Permalinks filtern. Das rote Objekt hat eine Methode, die die URL generiert, die wir für den Geltungsbereich nutzen können.

namespace MySite;

add_filter('post_link', function ($permalink, \WP_Post $post) {

    if (has_category('movie-review', $post)) {
        $movieRoute = new MovieReviewRoute();
        $permalink = $movieRoute->getUrl(['post' => $post]);
    }

    return $permalink;

}, 10, 2);

Mit diesem Code erhält jeder Beitrag mit der Kategorie "Filmkritik" die URL, die unserer Route entspricht.

Letzter Schliff

Momentan kann der Beitrag in der Kategorie "Filmkritik" mit zwei verschiedenen URLs angezeigt werden, der Standard-URL und die URL, die unserer Route entspricht.

Das sollten wir verhindern, es ist unter anderem für SEO sehr schlecht.

namespace MySite;

add_action('template_redirect', function() {
    if (
        is_single()
        && has_category('movie-review', get_queried_object())
        && $GLOBALS['wp']->matched_rule !== MovieReviewRoute::REGEX
    ) {
        $movieRoute = new MovieReviewRoute();
        wp_redirect($movieRoute->getUrl(['post' => get_queried_object()]), 301);
        exit();
    }
});

Dank der Variablen, die wir für die Klasse WP festgelegt haben, wenn unsere Route übereinstimmt, können wir erkennen, wann ein Beitrag zur Filmkritik mit der Standard-URL angezeigt wird.

In diesem Fall leiten wir einfach zur Routen-URL weiter.

Anmerkungen

Die Hauptfrage lautet: "Lohnt es sich, die Mühe"? Ich kann sagen, dass dieser Ansatz keine Probleme mit dem Rewrite-Rule-Ansatz hat.

Beispielsweise können Sie den Post-Slug in der Admin-Oberfläche frei ändern und dennoch 2 verschiedene URL-Strukturen für Post in dieser bestimmten Kategorie haben.

Natürlich ist mehr Code als nur 2 Funktionen in 2 Hooks eingebunden. Und mehr Code ist nie eine gute Sache.

Sie sollten jedoch beachten, dass das Hinzufügen von mehr und mehr Routen nach dem Einrichten dieser Methode viel einfacher ist. Sie müssen lediglich eine Klasse schreiben, da die Schnittstelle und der entsprechende "Mechanismus" bereits vorhanden sind.

Zur Beantwortung der Frage: wahrscheinlich nein : für nur eine Route lohnt es sich wahrscheinlich nicht, aber wenn Sie ein paar haben, vielleicht.

Bedenken Sie, dass es da draußen coole Bibliotheken gibt, die Sie in diesen Ansatz integrieren können, um den Abgleichmechanismus viel einfacher und leistungsfähiger zu machen.

Erwischt

Der Code ist völlig ungetestet . Aufgrund der kurzen Array-Syntax ist PHP 5.4+ erforderlich.

6
gmazzap

Ich bin nicht sicher, ob dies die beste Lösung ist oder nicht, aber es funktioniert:

function movie_review_permalink( $url, $post, $leavename ) {
    $category = get_the_category($post->ID); 
    if (  !empty($category) && $category[0]->slug == "test" ) { //change 'test' to your category slug
        $date=date_create($post->post_date);
        $my_date = date_format($date,"dmY");
        $url= trailingslashit( home_url('/'. $category[0]->slug .'/'. $post->post_name .'-'. $my_date .'/' ) );
    }
    return $url;
}
add_filter( 'post_link', 'movie_review_permalink', 10, 3 );

Der obige Code macht Ihren Beitrag zu einem Permalink für die Struktur der Kategorie test to http://wpHomeURL/test/post-name-ddmmyyyy.

Jetzt müssen Sie eine Umschreiberegel hinzufügen, damit dies funktioniert.

function movie_review_rewrite_rules( $wp_rewrite ) {
    $new_rules['^test/([^/]+)-([0-9]+)/?'] = 'index.php?name=$matches[1]'; //change 'test' to your category slug
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
    return $wp_rewrite;
}
add_action('generate_rewrite_rules', 'movie_review_rewrite_rules');

Hoffe das hilft!

6
тнє Sufi