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 .:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Der Code ist völlig ungetestet . Aufgrund der kurzen Array-Syntax ist PHP 5.4+ erforderlich.
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!