it-swarm.com.de

Eigene Seiten mit Plugin

Ich entwickle ein Plugin, mit dem ich benutzerdefinierte Seiten aktivieren möchte. In meinem Fall würde eine benutzerdefinierte Seite ein Formular wie ein Kontaktformular enthalten (nicht wörtlich). Wenn der Benutzer dieses Formular ausfüllt und sendet, sollte es den nächsten Schritt geben, der weitere Informationen erfordert. Nehmen wir an, dass sich die erste Seite mit dem Formular unter www.domain.tld/custom-page/ befindet und der Benutzer nach erfolgreicher Übermittlung des Formulars zu www.domain.tld/custom-page/second umgeleitet werden sollte. Vorlage mit HTML-Elementen und PHP-Code sollte ebenfalls benutzerdefiniert sein.

Ich denke, dass ein Teil des Problems mit benutzerdefinierten URL-Umschreibungen erreicht werden kann, aber die anderen Teile sind mir derzeit unbekannt. Ich weiß wirklich nicht, wo ich anfangen soll zu suchen und wie das Problem richtig benannt ist. Jede Hilfe wäre sehr dankbar.

12
user1257255

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 Filters 'do_parse_request' .

Wenn Sie false an diesen Haken zurückgeben, können Sie WordPress daran hindern, Anforderungen zu verarbeiten, und dies auf Ihre eigene Weise tun.

Das heißt, ich möchte eine Möglichkeit zum Erstellen eines einfachen OOP-Plugins vorstellen, mit dem virtuelle Seiten auf einfach zu verwendende (und wiederverwendende) Weise verarbeitet werden können.

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 (implementieren ControllerInterface) und injizieren (wahrscheinlich in einem Konstruktor) eine Instanz der TemplateLoader-Klasse (implementieren TemplateLoaderInterface)
  • Bei init hook rufen wir die Methode ControllerInterface::init() auf, um den Controller einzurichten und den Hook auszulösen, den der Consumer-Code zum Hinzufügen virtueller Seiten verwendet.
  • Auf 'do_parse_request' rufen wir ControllerInterface::dispatch() auf, und dort überprüfen wir alle hinzugefügten virtuellen Seiten und zeigen sie an, wenn eine von ihnen dieselbe URL der aktuellen Anfrage hat. Nachdem Sie alle globalen Kernvariablen ($wp_query, $post) festgelegt haben. Wir werden auch die Klasse TemplateLoader verwenden, um die richtige Vorlage zu laden.

Während dieses Workflows werden wir einige Core-Hooks auslösen, wie wp , template_redirect , template_include ..., um das Plugin flexibler zu machen und die Kompatibilität mit Core- und anderen Plugins oder zumindest mit sicherzustellen eine gute 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 für the_permalink hinzu, 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 sind.

Am 'do_parse_request' durchläuft die Controller-Klasse diesen Speicher, um auf einer der hinzugefügten Seiten eine Übereinstimmung mit der aktuellen 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 Klasse extension TemplateLoaderInterface. Danach nur noch 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 vor dem Laden der Vorlage page.php in einem Array mit den Standardwerten index.php und 'template_redirect' zusammengeführt, um die Flexibilität zu erhöhen und die Kompatibilität zu verbessern.

Danach durchläuft die gefundene Vorlage den benutzerdefinierten 'virtual_page_template' und den Kernfilter 'template_include' : erneut aus Gründen 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 einem Gist hier .

Seiten hinzufügen

Das Plugin ist fertig und funktioniert, aber es wurden keine Seiten hinzugefügt.

Dies kann innerhalb des Plugins selbst, innerhalb des Themas 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 erforderlichen PHP- und HTML-Elemente schreiben.

Das globale Post-Objekt ist mit Daten gefüllt, die von unserer virtuellen Seite stammen. Auf die virtuelle Seite selbst kann über die Variable $wp_query->virtual_page zugegriffen werden.

Das Abrufen der URL für eine virtuelle Seite ist so einfach wie das Übergeben an home_url() desselben 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 the_permalink() den korrekten Permalink zur virtuellen Seite zurückgibt.

Hinweise zu Stilen/Skripten für virtuelle Seiten

Wenn virtuelle Seiten hinzugefügt werden, ist es wahrscheinlich auch wünschenswert, benutzerdefinierte Stile/Skripte in die Warteschlange zu stellen und dann einfach wp_head() in benutzerdefinierten Vorlagen zu verwenden.

Das ist sehr einfach, da virtuelle Seiten anhand der Variablen $wp_query->virtual_page 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 hat nichts mit diesen virtuellen Seiten zu tun, 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 in der Eigenschaft form action.

Z.B. 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>
54
gmazzap

Ich habe einmal eine hier beschriebene Lösung verwendet: http://scott.sherrillmix.com/blog/blogger/creating-a-better-fake-post-with-a-wordpress-plugin/

Tatsächlich erweitere ich die Lösung, als ich sie verwendete, so, dass ich mehr als eine Seite pro Seite registrieren kann (der Rest eines Codes ist +/- ähnlich der Lösung, die ich aus einem obigen Absatz verknüpfe).

Die Lösung erfordert, dass Sie Nizza Permalinks erlaubt haben, um ...

<?php

class FakePages {

    public function __construct() {
        add_filter( 'the_posts', array( $this, 'fake_pages' ) );
    }

    /**
     * Internally registers pages we want to fake. Array key is the slug under which it is being available from the frontend
     * @return mixed
     */
    private static function get_fake_pages() {
        //http://example.com/fakepage1
        $fake_pages['fakepage1'] = array(
            'title'   => 'Fake Page 1',
            'content' => 'This is a content of fake page 1'
        );
        //http://example.com/fakepage2
        $fake_pages['fakepage2'] = array(
            'title'   => 'Fake Page 2',
            'content' => 'This is a content of fake page 2'
        );

        return $fake_pages;
    }

    /**
     * Fakes get posts result
     *
     * @param $posts
     *
     * @return array|null
     */
    public function fake_pages( $posts ) {
        global $wp, $wp_query;
        $fake_pages       = self::get_fake_pages();
        $fake_pages_slugs = array();
        foreach ( $fake_pages as $slug => $fp ) {
            $fake_pages_slugs[] = $slug;
        }
        if ( true === in_array( strtolower( $wp->request ), $fake_pages_slugs )
             || ( true === isset( $wp->query_vars['page_id'] )
                  && true === in_array( strtolower( $wp->query_vars['page_id'] ), $fake_pages_slugs )
            )
        ) {
            if ( true === in_array( strtolower( $wp->request ), $fake_pages_slugs ) ) {
                $fake_page = strtolower( $wp->request );
            } else {
                $fake_page = strtolower( $wp->query_vars['page_id'] );
            }
            $posts                  = null;
            $posts[]                = self::create_fake_page( $fake_page, $fake_pages[ $fake_page ] );
            $wp_query->is_page      = true;
            $wp_query->is_singular  = true;
            $wp_query->is_home      = false;
            $wp_query->is_archive   = false;
            $wp_query->is_category  = false;
            $wp_query->is_fake_page = true;
            $wp_query->fake_page    = $wp->request;
            //Longer permalink structures may not match the fake post slug and cause a 404 error so we catch the error here
            unset( $wp_query->query["error"] );
            $wp_query->query_vars["error"] = "";
            $wp_query->is_404              = false;
        }

        return $posts;
    }

    /**
     * Creates virtual fake page
     *
     * @param $pagename
     * @param $page
     *
     * @return stdClass
     */
    private static function create_fake_page( $pagename, $page ) {
        $post                 = new stdClass;
        $post->post_author    = 1;
        $post->post_name      = $pagename;
        $post->guid           = get_bloginfo( 'wpurl' ) . '/' . $pagename;
        $post->post_title     = $page['title'];
        $post->post_content   = $page['content'];
        $post->ID             = - 1;
        $post->post_status    = 'static';
        $post->comment_status = 'closed';
        $post->ping_status    = 'closed';
        $post->comment_count  = 0;
        $post->post_date      = current_time( 'mysql' );
        $post->post_date_gmt  = current_time( 'mysql', 1 );

        return $post;
    }
}

new FakePages();
0
david.binda