it-swarm-id.com

Halaman khusus dengan plugin

Saya sedang mengembangkan beberapa plugin di mana saya ingin mengaktifkan halaman khusus. Dalam kasus saya, beberapa halaman khusus akan berisi formulir seperti formulir kontak (tidak secara harfiah). Ketika pengguna akan mengisi formulir ini dan mengirimkannya, harus ada langkah berikutnya yang akan membutuhkan lebih banyak informasi. Katakanlah bahwa halaman pertama dengan formulir akan terletak di www.domain.tld/custom-page/ dan setelah pengiriman formulir berhasil, pengguna harus diarahkan ke www.domain.tld/custom-page/second. Templat dengan elemen HTML dan kode PHP juga harus dibuat khusus.

Saya pikir sebagian masalah mungkin terjadi dengan penulisan ulang URL khusus, tetapi bagian lain saat ini tidak saya kenal. Saya benar-benar tidak tahu di mana saya harus mulai mencari dan apa nama yang tepat untuk masalah itu. Bantuan apa pun akan sangat dihargai.

12
user1257255

Ketika Anda mengunjungi halaman frontend, WordPress akan menanyakan database dan jika halaman Anda tidak ada dalam database, permintaan itu tidak diperlukan dan hanya membuang-buang sumber daya.

Untungnya, WordPress menawarkan cara untuk menangani permintaan frontend dengan cara kustom. Itu dilakukan berkat filter 'do_parse_request' .

Mengembalikan false pada hook itu, Anda akan dapat menghentikan WordPress dari memproses permintaan dan melakukannya dengan cara kustom Anda sendiri.

Yang mengatakan, saya ingin berbagi cara untuk membangun plugin OOP sederhana yang dapat menangani halaman virtual dengan cara yang mudah digunakan (dan digunakan kembali).

Apa yang kita butuhkan

  • Kelas untuk objek halaman virtual
  • Kelas controller, yang akan melihat permintaan dan jika itu untuk halaman virtual, perlihatkan itu menggunakan templat yang tepat
  • Kelas untuk memuat template
  • File plugin utama untuk menambahkan kait yang akan membuat semuanya berfungsi

Antarmuka

Sebelum membangun kelas, mari kita menulis antarmuka untuk 3 objek yang tercantum di atas.

Pertama antarmuka halaman (file 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();
}

Sebagian besar metode hanya getter dan setter, tidak perlu penjelasan. Metode terakhir harus digunakan untuk mendapatkan objek WP_Post dari halaman virtual.

Antarmuka pengontrol (file 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 ); 
}

dan antarmuka pemuat template (file 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();
}

komentar phpDoc harus cukup jelas untuk antarmuka ini.

Rencana

Sekarang kita memiliki antarmuka, dan sebelum menulis kelas konkret, mari kita tinjau alur kerja kami:

  • Pertama kita instantiate kelas Controller (menerapkan ControllerInterface) dan menyuntikkan (mungkin dalam konstruktor) sebuah instance dari kelas TemplateLoader (mengimplementasikan TemplateLoaderInterface)
  • Pada init hook kita memanggil metode ControllerInterface::init() untuk mengatur controller dan mengaktifkan hook yang akan digunakan kode konsumen untuk menambah halaman virtual.
  • Pada 'do_parse_request' kami akan memanggil ControllerInterface::dispatch(), dan di sana kami akan memeriksa semua halaman virtual yang ditambahkan dan jika salah satu dari mereka memiliki URL yang sama dengan permintaan saat ini, tampilkan; setelah mengatur semua variabel global inti ($wp_query, $post). Kami juga akan menggunakan kelas TemplateLoader untuk memuat template yang tepat.

Selama alur kerja ini, kami akan memicu beberapa kait inti, seperti wp , template_redirect , template_include ... untuk membuat plugin lebih fleksibel dan memastikan kompatibilitas dengan core dan plugin lainnya, atau setidaknya dengan banyak dari mereka.

Selain dari alur kerja sebelumnya, kita juga perlu:

  • Bersihkan kait dan variabel global setelah loop utama berjalan, sekali lagi untuk meningkatkan kompatibilitas dengan kode inti dan pihak ketiga
  • Tambahkan filter pada the_permalink untuk membuatnya mengembalikan URL halaman virtual yang tepat bila diperlukan.

Kelas Beton

Sekarang kita bisa mengkodekan kelas konkret kita. Mari kita mulai dengan kelas halaman (file 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;
    }
}

Tidak lebih dari mengimplementasikan antarmuka.

Sekarang kelas controller (file 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();
    }
}

Pada dasarnya kelas membuat objek SplObjectStorage di mana semua objek halaman yang ditambahkan disimpan.

Pada 'do_parse_request' , kelas controller loop penyimpanan ini untuk menemukan kecocokan untuk URL saat ini di salah satu halaman yang ditambahkan.

Jika ditemukan, kelas melakukan persis apa yang kita rencanakan: memicu beberapa kait, mengatur variabel dan memuat templat melalui kelas yang memperpanjang TemplateLoaderInterface. Setelah itu, hanya exit().

Jadi mari kita tulis kelas terakhir:

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

Template yang disimpan di halaman virtual digabungkan dalam array dengan default page.php dan index.php, sebelum memuat template 'template_redirect' dipecat, untuk menambah fleksibilitas dan meningkatkan kompatibilitas.

Setelah itu, templat yang ditemukan melewati custom 'virtual_page_template' dan inti 'template_include' filter: lagi untuk fleksibilitas dan kompatibilitas.

Akhirnya file template baru saja dimuat.

File plugin utama

Pada titik ini kita perlu menulis file dengan header plugin dan menggunakannya untuk menambahkan kait yang akan membuat alur kerja kita terjadi:

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

Dalam file asli kita mungkin akan menambahkan lebih banyak header, seperti plugin dan tautan penulis, deskripsi, lisensi, dll.

Pengaya Plugin

Ok, kita selesai dengan plugin kita. Semua kode dapat ditemukan di Gist di sini .

Menambahkan Halaman

Plugin sudah siap dan berfungsi, tetapi kami belum menambahkan halaman apa pun.

Itu bisa dilakukan di dalam plugin itu sendiri, di dalam tema functions.php, di plugin lain, dll.

Tambahkan halaman hanya masalah:

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

} );

Dan seterusnya. Anda dapat menambahkan semua halaman yang Anda butuhkan, hanya ingat untuk menggunakan URL relatif untuk halaman tersebut.

Di dalam file template Anda dapat menggunakan semua tag template WordPress, dan Anda dapat menulis semua PHP dan HTML yang Anda butuhkan.

Objek posting global diisi dengan data yang berasal dari halaman virtual kami. Halaman virtual itu sendiri dapat diakses melalui variabel $wp_query->virtual_page.

Untuk mendapatkan URL untuk halaman virtual semudah meneruskan ke home_url() jalur yang sama yang digunakan untuk membuat halaman:

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

Perhatikan bahwa dalam loop utama dalam template yang dimuat, the_permalink() akan mengembalikan permalink yang benar ke halaman virtual.

Catatan tentang gaya/skrip untuk halaman virtual

Mungkin ketika halaman virtual ditambahkan, itu juga diinginkan untuk memiliki gaya khusus/skrip enqueued dan kemudian hanya menggunakan wp_head() dalam template khusus.

Itu sangat mudah, karena halaman virtual mudah dikenali melihat variabel $wp_query->virtual_page dan halaman virtual dapat dibedakan satu dengan yang lain melihat URL mereka.

Contoh saja:

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

} );

Catatan untuk OP

Melewati data dari satu halaman ke halaman lain tidak terkait dengan halaman virtual ini, tetapi hanya tugas umum.

Namun, jika Anda memiliki formulir di halaman pertama, dan ingin meneruskan data dari sana ke halaman kedua, cukup gunakan URL halaman kedua dalam bentuk properti action.

Misalnya. dalam file templat halaman pertama Anda dapat:

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

dan kemudian di file templat halaman kedua:

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

Saya pernah menggunakan solusi yang dijelaskan di sini: http://scott.sherrillmix.com/blog/blogger/creating-a-better-fake-post-with-a-wordpress-plugin/

Sebenarnya, ketika saya menggunakannya, saya memperluas solusi dengan cara saya bisa mendaftarkan lebih dari satu halaman dalam satu waktu (sisa kode adalah +/- mirip dengan solusi yang saya tautkan dari paragraf di atas).

Solusinya mengharuskan Anda untuk memiliki permalink ...

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