Wenn Sie eine Frontend-Seite besuchen, fragt WordPress die Datenbank ab. Wenn Ihre Seite nicht in der Datenbank vorhanden ist, wird diese Abfrage nicht benötigt und ist nur eine Verschwendung von Ressourcen.
Glücklicherweise bietet WordPress eine Möglichkeit, Frontend-Anfragen auf eine benutzerdefinierte Weise zu bearbeiten. Dies geschieht dank des 'do_parse_request'
Filters.
Wenn Sie false
auf diesen Haken zurückkehren, können Sie WordPress daran hindern, Anforderungen zu verarbeiten, und dies auf Ihre eigene Weise tun.
Trotzdem möchte ich eine Möglichkeit vorstellen, ein einfaches OOP-Plugin zu erstellen, das mit virtuellen Seiten auf einfach zu verwendende (und wiederverwendende) Weise umgehen kann.
Was wir brauchen
- Eine Klasse für virtuelle Seitenobjekte
- Eine Controller-Klasse, die eine Anfrage betrachtet und, falls es sich um eine virtuelle Seite handelt, diese mit der richtigen Vorlage anzeigt
- Eine Klasse zum Laden von Vorlagen
- Haupt-Plugin-Dateien zum Hinzufügen der Hooks, mit denen alles funktioniert
Schnittstellen
Schreiben Sie vor dem Erstellen von Klassen die Schnittstellen für die drei oben aufgeführten Objekte.
Zuerst das Seiteninterface (Datei PageInterface.php
):
<?php
namespace GM\VirtualPages;
interface PageInterface {
function getUrl();
function getTemplate();
function getTitle();
function setTitle( $title );
function setContent( $content );
function setTemplate( $template );
/**
* Get a WP_Post build using virtual Page object
*
* @return \WP_Post
*/
function asWpPost();
}
Die meisten Methoden sind nur Trickser und Trickser, ohne dass es einer Erklärung bedarf. Die letzte Methode sollte verwendet werden, um ein WP_Post
Objekt von einer virtuellen Seite abzurufen.
Die Controller-Schnittstelle (Datei ControllerInterface.php
):
<?php
namespace GM\VirtualPages;
interface ControllerInterface {
/**
* Init the controller, fires the hook that allows consumer to add pages
*/
function init();
/**
* Register a page object in the controller
*
* @param \GM\VirtualPages\Page $page
* @return \GM\VirtualPages\Page
*/
function addPage( PageInterface $page );
/**
* Run on 'do_parse_request' and if the request is for one of the registered pages
* setup global variables, fire core hooks, requires page template and exit.
*
* @param boolean $bool The boolean flag value passed by 'do_parse_request'
* @param \WP $wp The global wp object passed by 'do_parse_request'
*/
function dispatch( $bool, \WP $wp );
}
und das Template Loader Interface (Datei TemplateLoaderInterface.php
):
<?php
namespace GM\VirtualPages;
interface TemplateLoaderInterface {
/**
* Setup loader for a page objects
*
* @param \GM\VirtualPagesPageInterface $page matched virtual page
*/
public function init( PageInterface $page );
/**
* Trigger core and custom hooks to filter templates,
* then load the found template.
*/
public function load();
}
phpDoc-Kommentare sollten für diese Schnittstellen ziemlich klar sein.
Der Plan
Nachdem wir nun Schnittstellen haben, und bevor wir konkrete Klassen schreiben, wollen wir unseren Workflow überprüfen:
- Zuerst instanziieren wir eine
Controller
Klasse (implementierend ControllerInterface
) und injizieren (wahrscheinlich in einem Konstruktor) eine Instanz der TemplateLoader
Klasse (implementierend TemplateLoaderInterface
)
- Am
init
Haken rufen wir die ControllerInterface::init()
Methode zum Einrichten der Steuerung und den Haken zu feuern , dass Code Verbraucher virtuelle Seiten hinzufügen verwenden.
- Bei 'do_parse_request' rufen
ControllerInterface::dispatch()
wir auf und überprüfen dort alle hinzugefügten virtuellen Seiten. Wenn eine davon dieselbe URL der aktuellen Anfrage hat, zeigen Sie sie an. Nachdem Sie alle globalen Kernvariablen ( $wp_query
, $post
) gesetzt haben. Wir werden auch TemplateLoader
Klasse verwenden, um die richtige Vorlage zu laden.
Während dieses Workflow werden wir einige Kernhaken auslösen, wie wp
, template_redirect
, template_include
... machen das Plugin flexibler und die Kompatibilität mit Kern und anderen Plugins oder zumindest mit einer guten Anzahl von ihnen.
Abgesehen von den vorherigen Arbeitsabläufen müssen wir auch:
- Bereinigen Sie Hooks und globale Variablen nach dem Ausführen der Hauptschleife, um die Kompatibilität mit Kerncode und Code von Drittanbietern zu verbessern
- Fügen Sie einen Filter hinzu
the_permalink
, damit er bei Bedarf die richtige URL der virtuellen Seite zurückgibt.
Konkrete Klassen
Jetzt können wir unsere konkreten Klassen codieren. Beginnen wir mit der Seitenklasse (Datei Page.php
):
<?php
namespace GM\VirtualPages;
class Page implements PageInterface {
private $url;
private $title;
private $content;
private $template;
private $wp_post;
function __construct( $url, $title = 'Untitled', $template = 'page.php' ) {
$this->url = filter_var( $url, FILTER_SANITIZE_URL );
$this->setTitle( $title );
$this->setTemplate( $template);
}
function getUrl() {
return $this->url;
}
function getTemplate() {
return $this->template;
}
function getTitle() {
return $this->title;
}
function setTitle( $title ) {
$this->title = filter_var( $title, FILTER_SANITIZE_STRING );
return $this;
}
function setContent( $content ) {
$this->content = $content;
return $this;
}
function setTemplate( $template ) {
$this->template = $template;
return $this;
}
function asWpPost() {
if ( is_null( $this->wp_post ) ) {
$post = array(
'ID' => 0,
'post_title' => $this->title,
'post_name' => sanitize_title( $this->title ),
'post_content' => $this->content ? : '',
'post_excerpt' => '',
'post_parent' => 0,
'menu_order' => 0,
'post_type' => 'page',
'post_status' => 'publish',
'comment_status' => 'closed',
'ping_status' => 'closed',
'comment_count' => 0,
'post_password' => '',
'to_ping' => '',
'pinged' => '',
'guid' => home_url( $this->getUrl() ),
'post_date' => current_time( 'mysql' ),
'post_date_gmt' => current_time( 'mysql', 1 ),
'post_author' => is_user_logged_in() ? get_current_user_id() : 0,
'is_virtual' => TRUE,
'filter' => 'raw'
);
$this->wp_post = new \WP_Post( (object) $post );
}
return $this->wp_post;
}
}
Nichts weiter als die Implementierung der Schnittstelle.
Nun die Controller-Klasse (Datei Controller.php
):
<?php
namespace GM\VirtualPages;
class Controller implements ControllerInterface {
private $pages;
private $loader;
private $matched;
function __construct( TemplateLoaderInterface $loader ) {
$this->pages = new \SplObjectStorage;
$this->loader = $loader;
}
function init() {
do_action( 'gm_virtual_pages', $this );
}
function addPage( PageInterface $page ) {
$this->pages->attach( $page );
return $page;
}
function dispatch( $bool, \WP $wp ) {
if ( $this->checkRequest() && $this->matched instanceof Page ) {
$this->loader->init( $this->matched );
$wp->virtual_page = $this->matched;
do_action( 'parse_request', $wp );
$this->setupQuery();
do_action( 'wp', $wp );
$this->loader->load();
$this->handleExit();
}
return $bool;
}
private function checkRequest() {
$this->pages->rewind();
$path = trim( $this->getPathInfo(), '/' );
while( $this->pages->valid() ) {
if ( trim( $this->pages->current()->getUrl(), '/' ) === $path ) {
$this->matched = $this->pages->current();
return TRUE;
}
$this->pages->next();
}
}
private function getPathInfo() {
$home_path = parse_url( home_url(), PHP_URL_PATH );
return preg_replace( "#^/?{$home_path}/#", '/', esc_url( add_query_arg(array()) ) );
}
private function setupQuery() {
global $wp_query;
$wp_query->init();
$wp_query->is_page = TRUE;
$wp_query->is_singular = TRUE;
$wp_query->is_home = FALSE;
$wp_query->found_posts = 1;
$wp_query->post_count = 1;
$wp_query->max_num_pages = 1;
$posts = (array) apply_filters(
'the_posts', array( $this->matched->asWpPost() ), $wp_query
);
$post = $posts[0];
$wp_query->posts = $posts;
$wp_query->post = $post;
$wp_query->queried_object = $post;
$GLOBALS['post'] = $post;
$wp_query->virtual_page = $post instanceof \WP_Post && isset( $post->is_virtual )
? $this->matched
: NULL;
}
public function handleExit() {
exit();
}
}
Im Wesentlichen erstellt die Klasse ein SplObjectStorage
Objekt, in dem alle hinzugefügten Seitenobjekte gespeichert werden.
Ein 'do_parse_request'
Die Controller-Klasse durchläuft diesen Speicher, um auf einer der hinzugefügten Seiten eine Übereinstimmung für die aktuelle URL zu finden.
Wenn es gefunden wird, macht die Klasse genau das, was wir geplant haben: Triggern Sie einige Hooks, richten Sie Variablen ein und laden Sie die Vorlage über die Klassenerweiterung TemplateLoaderInterface
. Danach einfach exit()
.
Schreiben wir also die letzte Klasse:
<?php
namespace GM\VirtualPages;
class TemplateLoader implements TemplateLoaderInterface {
public function init( PageInterface $page ) {
$this->templates = wp_parse_args(
array( 'page.php', 'index.php' ), (array) $page->getTemplate()
);
}
public function load() {
do_action( 'template_redirect' );
$template = locate_template( array_filter( $this->templates ) );
$filtered = apply_filters( 'template_include',
apply_filters( 'virtual_page_template', $template )
);
if ( empty( $filtered ) || file_exists( $filtered ) ) {
$template = $filtered;
}
if ( ! empty( $template ) && file_exists( $template ) ) {
require_once $template;
}
}
}
Auf der virtuellen Seite gespeicherte Vorlagen werden mit den Standardeinstellungen in einem Array zusammengeführt, page.php
und index.php
vor dem Laden der Vorlage 'template_redirect'
wird die Flexibilität erhöht und die Kompatibilität verbessert.
Danach durchläuft die gefundene Vorlage den benutzerdefinierten Filter 'virtual_page_template'
und den Kernfilter. 'template_include'
Dies dient wiederum der Flexibilität und Kompatibilität.
Zuletzt wird die Vorlagendatei geladen.
Haupt-Plugin-Datei
An diesem Punkt müssen wir die Datei mit Plugin-Headern schreiben und sie verwenden, um die Hooks hinzuzufügen, die unseren Workflow ermöglichen:
<?php namespace GM\VirtualPages;
/*
Plugin Name: GM Virtual Pages
*/
require_once 'PageInterface.php';
require_once 'ControllerInterface.php';
require_once 'TemplateLoaderInterface.php';
require_once 'Page.php';
require_once 'Controller.php';
require_once 'TemplateLoader.php';
$controller = new Controller ( new TemplateLoader );
add_action( 'init', array( $controller, 'init' ) );
add_filter( 'do_parse_request', array( $controller, 'dispatch' ), PHP_INT_MAX, 2 );
add_action( 'loop_end', function( \WP_Query $query ) {
if ( isset( $query->virtual_page ) && ! empty( $query->virtual_page ) ) {
$query->virtual_page = NULL;
}
} );
add_filter( 'the_permalink', function( $plink ) {
global $post, $wp_query;
if (
$wp_query->is_page && isset( $wp_query->virtual_page )
&& $wp_query->virtual_page instanceof Page
&& isset( $post->is_virtual ) && $post->is_virtual
) {
$plink = home_url( $wp_query->virtual_page->getUrl() );
}
return $plink;
} );
In der realen Datei werden wir wahrscheinlich weitere Header wie Plugin- und Autorenlinks, Beschreibung, Lizenz usw. hinzufügen.
Plugin Gist
Ok, wir sind mit unserem Plugin fertig. Den gesamten Code finden Sie in einer Übersicht hier .
Seiten hinzufügen
Das Plugin ist fertig und funktioniert, aber wir haben keine Seiten hinzugefügt.
Dies kann innerhalb des Plugins selbst, innerhalb des Themes functions.php
, in einem anderen Plugin usw. erfolgen.
Das Hinzufügen von Seiten ist nur eine Frage von:
<?php
add_action( 'gm_virtual_pages', function( $controller ) {
// first page
$controller->addPage( new \GM\VirtualPages\Page( '/custom/page' ) )
->setTitle( 'My First Custom Page' )
->setTemplate( 'custom-page-form.php' );
// second page
$controller->addPage( new \GM\VirtualPages\Page( '/custom/page/deep' ) )
->setTitle( 'My Second Custom Page' )
->setTemplate( 'custom-page-deep.php' );
} );
Und so weiter. Sie können alle benötigten Seiten hinzufügen. Denken Sie jedoch daran, relative URLs für die Seiten zu verwenden.
In der Vorlagendatei können Sie alle WordPress-Vorlagen-Tags verwenden und alle benötigten PHP- und HTML-Dateien schreiben.
Das globale Post-Objekt ist mit Daten gefüllt, die von unserer virtuellen Seite stammen. Auf die virtuelle Seite selbst kann über eine $wp_query->virtual_page
Variable zugegriffen werden .
Das Abrufen der URL für eine virtuelle Seite ist so einfach wie das Übergeben home_url()
des Pfads, der zum Erstellen der Seite verwendet wurde:
$custom_page_url = home_url( '/custom/page' );
Beachten Sie, dass in der Hauptschleife in der geladenen Vorlage der the_permalink()
richtige Permalink zur virtuellen Seite zurückgegeben wird.
Hinweise zu Stilen / Skripten für virtuelle Seiten
Wahrscheinlich ist es beim Hinzufügen virtueller Seiten auch wünschenswert, benutzerdefinierte Stile / Skripte in die Warteschlange zu stellen und dann nur wp_head()
in benutzerdefinierten Vorlagen zu verwenden.
Das ist sehr einfach, da virtuelle Seiten anhand von $wp_query->virtual_page
Variablen leicht erkannt werden und virtuelle Seiten anhand ihrer URLs voneinander unterschieden werden können.
Nur ein Beispiel:
add_action( 'wp_enqueue_scripts', function() {
global $wp_query;
if (
is_page()
&& isset( $wp_query->virtual_page )
&& $wp_query->virtual_page instanceof \GM\VirtualPages\PageInterface
) {
$url = $wp_query->virtual_page->getUrl();
switch ( $url ) {
case '/custom/page' :
wp_enqueue_script( 'a_script', $a_script_url );
wp_enqueue_style( 'a_style', $a_style_url );
break;
case '/custom/page/deep' :
wp_enqueue_script( 'another_script', $another_script_url );
wp_enqueue_style( 'another_style', $another_style_url );
break;
}
}
} );
Hinweise zum OP
Das Übergeben von Daten von einer Seite zu einer anderen bezieht sich nicht auf diese virtuellen Seiten, sondern ist nur eine allgemeine Aufgabe.
Wenn Sie jedoch ein Formular auf der ersten Seite haben und Daten von dort an die zweite Seite übergeben möchten, verwenden Sie einfach die URL der zweiten Seite im Formular action
.
ZB in der ersten Seitenvorlagendatei können Sie:
<form action="<?php echo home_url( '/custom/page/deep' ); ?>" method="POST">
<input type="text" name="testme">
</form>
und dann in der zweiten Seitenvorlagendatei:
<?php $testme = filter_input( INPUT_POST, 'testme', FILTER_SANITIZE_STRING ); ?>
<h1>Test-Me value form other page is: <?php echo $testme; ?></h1>