Пользовательские страницы с плагином

Я разрабатываю плагин, где нужно включить пользовательские страницы. В моем случае некоторые пользовательские страницы будут содержать контактную форму. Когда пользователь заполнит эту форму и отправит ее, должен быть следующий шаг, который потребует дополнительной информации. Допустим, первая страница с формой будет расположена по адресу www.domain.tld/custom-page/, и после успешной отправки формы пользователь должен быть перенаправлен на www.domain.tld/custom-page/second. Шаблон с элементами HTML и PHP-кодом также должен быть индивидуальным.

Я думаю, что часть проблемы можно решить с помощью пользовательских перезаписей URL, но другие части в настоящее время мне неизвестны. Я действительно не знаю, с чего мне начать искать и как правильно описать эту проблему. Любая помощь могла бы быть полезна.

Понравился вопрос? Нужен ответ? Поддержите проект
WPAsk
Ответов: 1
  1. gmazzap

    Когда вы посещаете страницу веб-интерфейса, WordPress запрашивает базу данных, и если ваша страница не существует в базе данных, этот запрос не нужен и является пустой тратой ресурсов.

    К счастью, WordPress предлагает способ пользовательской обработки запросов к веб-интерфейсу. Это делается благодаря хуку 'do_parse_request' .

    Вернув false на этот хук, вы сможете запретить WordPress обрабатывать запросы и делать это по-своему.

    Тем не менее, я хочу поделиться способом создания простого плагина ООП, который может обрабатывать виртуальные страницы простым в использовании (и повторно использовать) способом.

    Вот что нужно

    • Класс для объектов виртуальной страницы
    • Класс контроллера, который будет смотреть на запрос и, если он для виртуальной страницы, показывать его, используя соответствующий шаблон
    • Класс для загрузки шаблона
    • Основные файлы плагинов для добавления хуков, которые заставят все работать

    Интерфейсы

    Перед созданием классов давайте напишем интерфейсы для 3 объектов, перечисленных выше.

    Сначала интерфейс страницы (файл PageInterface.php ):

    <?php
    namespace GMVirtualPages;
    
    interface PageInterface {
    
        function getUrl();
    
        function getTemplate();
    
        function getTitle();
    
        function setTitle( $title );
    
        function setContent( $content );
    
        function setTemplate( $template );
    
        /**
         * Получить WP_Post с использованием объекта виртуальной страницы
         *
         * @return WP_Post
         */
        function asWpPost();
    }
    

    Большинство методов — просто геттеры и сеттеры, объяснений не требуется. Последний метод должен использоваться для получения WP_Post объекта из виртуальной страницы.

    Интерфейс контроллера (файл ControllerInterface.php ):

    <?php
    namespace GMVirtualPages;
    
    interface ControllerInterface {
    
        /**
         * Init контроллер, который запускает хук, который позволяет пользователю добавлять страницы
         */
        function init();
    
        /**
         * Зарегистрировать объект страницы в контроллере
         *
         * @param  GMVirtualPagesPage $page
         * @return GMVirtualPagesPage
         */
        function addPage( PageInterface $page );
    
        /**
         *
         * @param boolean $bool Значение логического флага, переданное 'do_parse_request'
         * @param WP $wp       Глобальный объект wp, переданный do_parse_request
         */  
        function dispatch( $bool, WP $wp ); 
    }
    

    и интерфейс загрузчика шаблонов (файл TemplateLoaderInterface.php ):

    <?php
    namespace GMVirtualPages;
    
    interface TemplateLoaderInterface {
    
        /**
         * Настройка загрузчика для страницы объектов
         *
         */
        public function init( PageInterface $page );
    
        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 GMVirtualPages;
    
    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 GMVirtualPages;
    
    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 GMVirtualPages;
    
    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 GMVirtualPages;
    
    /*
      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;
    } );
    

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

    Добавление страниц

    Плагин готов и работает, но мы не добавили ни одной страницы.

    Это можно сделать внутри самого плагина, внутри темы functions.php , в другом плагине и т.д.

    Добавить страницы:

    <?php
    add_action( 'gm_virtual_pages', function( $controller ) {
    
        // первая страница
        $controller->addPage( new GMVirtualPagesPage( '/custom/page' ) )
            ->setTitle( 'My First Custom Page' )
            ->setTemplate( 'custom-page-form.php' );
    
        // вторая
        $controller->addPage( new GMVirtualPagesPage( '/custom/page/deep' ) )
            ->setTitle( 'My Second Custom Page' )
            ->setTemplate( 'custom-page-deep.php' );
    
    } );
    

    И так далее. Вы можете добавить все нужные вам страницы, просто не забудьте использовать относительные URL для этих страниц.

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

    Глобальный объект post заполнен данными, поступающими с нашей виртуальной страницы. К самой виртуальной странице можно получить доступ через переменную $ wp_query->gt; 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 GMVirtualPagesPageInterface
        ) {
    
            $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>
    

Добавить ответ

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: