Спеціальні сторінки із плагіном


13

Я розробляю плагін, де я хочу ввімкнути спеціальні сторінки. У моєму випадку деяка спеціальна сторінка містила б форму, наприклад контактну форму (не буквально). Коли користувач заповнить цю форму та надішле її, слід зробити наступний крок, який потребує додаткової інформації. Скажімо, що перша сторінка з формою буде розміщена в www.domain.tld/custom-page/і після успішного подання форми, користувач повинен бути перенаправлений на www.domain.tld/custom-page/second. Шаблон з елементами HTML та кодом PHP також повинен бути спеціальним.

Я думаю, що частину проблеми можливо досягти за допомогою користувацьких переписувань URL-адрес, але інші частини мені наразі невідомі. Я насправді не знаю, з чого слід почати шукати і як правильно називати цю проблему. Будь-яка допомога буде дуже вдячна.


Ви хочете, щоб ці сторінки зберігалися в WordPress або "віртуально"?
Вельчер

Вам доведеться використовувати переписати api. Це не повинно бути надто складно. Не забудьте опублікувати дані на другій сторінці, і вам слід буде добре.
сетерГеттер

@Welcher: Ці сторінки не збігаються з пропозиціями WordPress на інформаційній панелі. Вони повинні просто зберігати дані в базі даних, але це не проблема. @ .setterGetter: Чи є у вас приклад, як передавати дані з першої сторінки на другу і куди (дія?) включити файл PHP, який показує форму?
користувач1257255

Чи розглядали ви за допомогою форми однієї сторінки з декількома слайдами (javascript та / або css) полів введення?
birgire

Відповіді:


57

Коли ви відвідуєте сторінку на передній панелі, WordPress запитує базу даних, і якщо ваша сторінка не існує в базі даних, цей запит не потрібен і є лише марною витратою ресурсів.

На щастя, WordPress пропонує спосіб обробляти запити на замовлення на замовлення. Це робиться завдяки 'do_parse_request'фільтру.

Повернувшись falseна цей гачок, ви зможете зупинити WordPress від обробки запитів і зробити це власним способом.

З цього приводу я хочу поділитися способом побудови простого плагіна OOP, який може обробляти віртуальні сторінки простим у використанні (та повторному використанні) способом.

Що нам потрібно

  • Клас для об’єктів віртуальних сторінок
  • Клас контролера, який буде розглядати запит, і якщо він призначений для віртуальної сторінки, покаже його за допомогою відповідного шаблону
  • Клас для завантаження шаблонів
  • Основні файли плагінів, щоб додати гачки, які змусять все працювати

Інтерфейси

Перш ніж будувати класи, давайте запишемо інтерфейси для 3 перелічених вище об’єктів.

Спочатку інтерфейс сторінки (файл 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();
}

Більшість методів - це лише геттери і сетери, пояснення не потребують. Останній метод слід використовувати для отримання WP_Postоб’єкта з віртуальної сторінки.

Інтерфейс контролера (файл 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 ); 
}

та інтерфейс (файл 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 повинні бути досить чіткими для цих інтерфейсів.

План

Тепер, коли у нас є інтерфейси, і перш ніж писати конкретні класи, давайте розглянемо наш робочий процес:

  • Спочатку ми інстанціюємо Controllerклас (реалізує ControllerInterface) та вводимо (можливо, у конструктор) екземпляр TemplateLoaderкласу (реалізує TemplateLoaderInterface)
  • На initгачку ми називаємо ControllerInterface::init()метод встановлення контролера та запуску гака, який споживчий код використовуватиме для додавання віртуальних сторінок.
  • На "do_parse_request" ми зателефонуємо ControllerInterface::dispatch(), і там ми перевіримо всі додані віртуальні сторінки, і якщо одна з них має ту саму URL-адресу поточного запиту, відобразимо її; після встановлення всіх основних глобальних змінних ( $wp_query, $post). Ми також будемо використовувати TemplateLoaderклас для завантаження потрібного шаблону.

Під час цього робочого процесу ми будемо запускати деякі основні крюки, як wp, template_redirect, template_include... щоб зробити плагін більш гнучким і забезпечити сумісність з ядром та іншими плагінами, або , по крайней мере , з хорошим кількістю їх.

Окрім попереднього робочого процесу, нам також потрібно буде:

  • Очистіть гачки та глобальні змінні після запуску основного циклу, щоб покращити сумісність з основним та стороннім кодом
  • Додайте фільтр, the_permalinkщоб він міг повернути потрібну URL-адресу віртуальної сторінки при необхідності.

Бетонні класи

Тепер ми можемо кодувати наші конкретні класи. Почнемо з класу сторінки (файлу 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;
    }
}

Не що інше, як реалізація інтерфейсу.

Тепер клас контролера (файл 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();
    }
}

По суті, клас створює SplObjectStorageоб'єкт, де зберігаються всі додані сторінки.

Увімкнено 'do_parse_request'клас контролера циклічно зберігає цю пам’ять, щоб знайти відповідність для поточної URL-адреси на одній із доданих сторінок.

Якщо він знайдений, клас виконує саме те, що ми планували: запустити деякі гачки, встановити змінні та завантажити шаблон через клас, що розширюється TemplateLoaderInterface. Після цього просто exit().

Тож давайте напишемо останній клас:

<?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;
        }
    }
}

Шаблони, що зберігаються на віртуальній сторінці, об’єднуються в масив із за замовчуванням page.phpі index.phpперед запуском шаблону для завантаження 'template_redirect'для додання гнучкості та покращення сумісності.

Після цього знайдений шаблон проходить через спеціальні 'virtual_page_template'та основні 'template_include'фільтри: знову ж таки для гнучкості та сумісності.

Нарешті файл шаблону щойно завантажується.

Основний файл плагіна

У цей момент нам потрібно написати файл із заголовками плагінів і використовувати його, щоб додати гачки, які сприятимуть нашому робочому процесу:

<?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;
} );

У реальний файл ми, мабуть, додамо більше заголовків, таких як посилання на плагін та автор, опис, ліцензія тощо.

Plugin Gist

Гаразд, ми зробили наш плагін. Весь код ви можете знайти в Gist тут .

Додавання сторінок

Плагін готовий і працює, але ми не додали жодної сторінки.

Це можна зробити всередині самого плагіна, всередині теми functions.php, в іншому додатку тощо.

Додавання сторінок - це лише питання:

<?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' );

} );

І так далі. Ви можете додати всі потрібні сторінки, просто не забудьте використати відносні URL-адреси для сторінок.

Всередині файлу шаблону ви можете використовувати всі теги шаблонів WordPress, і ви можете написати всі необхідні PHP та HTML.

Об'єкт глобальної пошти заповнений даними, що надходять з нашої віртуальної сторінки. До самої віртуальної сторінки можна отримати доступ через $wp_query->virtual_pageзмінну.

Отримати URL-адресу для віртуальної сторінки так само просто, як перейти до home_url()того ж шляху, який використовується для створення сторінки:

$custom_page_url = home_url( '/custom/page' );

Зауважте, що в основному циклі завантаженого шаблону the_permalink()повернеться правильна посилання на віртуальну сторінку.

Примітки до стилів / сценаріїв для віртуальних сторінок

Можливо, коли додаються віртуальні сторінки, бажано також, щоб користувацькі стилі / сценарії були заповнені, а потім просто використовувались wp_head()у користувацьких шаблонах.

Це дуже просто, адже віртуальні сторінки легко розпізнати, переглядаючи $wp_query->virtual_pageзмінні, а віртуальні сторінки можна відрізнити одна від іншої, переглядаючи їх URL.

Просто приклад:

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;
        }
    }

} );

Примітки до ОП

Передача даних зі сторінки на іншу не пов’язана з цими віртуальними сторінками, а є лише загальним завданням.

Однак якщо у вас є форма на першій сторінці та ви хочете передати дані звідти на другу сторінку, просто скористайтеся URL-адресою другої сторінки у actionвласності форми .

Наприклад, у файлі шаблону на першій сторінці ви можете:

<form action="<?php echo home_url( '/custom/page/deep' ); ?>" method="POST">
    <input type="text" name="testme">
</form>

а потім у файлі шаблону на другій сторінці:

<?php $testme = filter_input( INPUT_POST, 'testme', FILTER_SANITIZE_STRING ); ?>
<h1>Test-Me value form other page is: <?php echo $testme; ?></h1>

9
Дивовижна вичерпна відповідь не тільки на саму проблему, але й на створення плагіна в стилі OOP тощо. Ви точно отримали мою пропозицію, уявіть собі більше, по одному на кожен рівень, що відповідає.
Миколай

2
Дуже гладке та прямолінійне рішення. Оновлено, твітнув.
кайзер

Код у Controller трохи помиляється ... checkRequest () отримує інформацію про шлях від home_url (), який повертає localhost / wordpress. Після preg_replace та add_query_arg, цей URL стає / wordpress / virtual-page. А після обрізки в checkRequest ця URL-адреса має wordpress / virtual. Це спрацює, якщо wordpress буде встановлений у кореневій папці домену. Чи можете ви, будь ласка, надати вирішення цієї проблеми, оскільки я не можу знайти відповідну функцію, яка б повернула правильний URL. Дякую тобі за все! (Я прийму відповідь після того, як вона стане досконалою :)
користувач1257255

2
Вітаю, приємна відповідь, і мені потрібно бачити цю роботу як безкоштовне рішення.
буелге

@GM: У моєму випадку WordPress встановлений у ... / htdocs / wordpress /, і сайт доступний на localhost / wordpress . home_url () повертає localhost / wordpress, а add_query_arg (array ()) повертає / wordpress / virtual-page /. Коли ми порівнюємо $ path та обрізані $ this-> pages-> current () -> getUrl () у checkRequest (), це проблема, оскільки $ path є, wordpress/virtual-pageа сторінка оброблена URL-адресою virtual-page.
користувач1257255

0

Я колись використовував описане тут рішення: http://scott.sherrillmix.com/blog/blogger/creating-a-better-fake-post-with-a-wordpress-plugin/

Насправді, коли я ним користувався, я розширюю рішення таким чином, що можу реєструвати більше однієї сторінки одночасно (решта коду +/- подібно до рішення, яке я посилаю з пункту вище).

Рішення вимагає, щоб ви мали приємні постійні посилання, дозволені ...

<?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();

Що з користувацьким шаблоном, де я можу розмістити форму?
користувач1257255

contentв масиві під час реєстрації підробленої сторінки відображається в тілі сторінки - вона може містити HTML, а також простий текст або навіть короткий код.
david.binda
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.