Eklentili özel sayfalar


13

Özel sayfaları etkinleştirmek istediğim bazı eklentiler geliştiriyorum. Benim durumumda bazı özel sayfalar iletişim formu gibi bir form içerecektir (kelimenin tam anlamıyla değil). Kullanıcı bu formu doldurarak gönderirken, daha fazla bilgi gerektiren bir sonraki adım olmalıdır. Form içeren ilk sayfanın www.domain.tld/custom-page/başarılı form gönderimi sırasında ve sonrasında bulunacağını varsayalım, kullanıcının yönlendirilmesi gerekir www.domain.tld/custom-page/second. HTML öğeleri ve PHP kodu içeren şablon da özel olmalıdır.

Sorunun bir kısmının özel URL yeniden yazma işlemleriyle elde edilebileceğini düşünüyorum, ancak diğer bölümler şu anda benim için bilinmiyor. Nereye bakmaya başlamam gerektiğini ve bu problem için doğru isimlendirmenin ne olduğunu gerçekten bilmiyorum. Herhangi bir yardım gerçekten takdir edilecektir.


Bu sayfaların WordPress veya 'sanal' olarak depolanmasını istiyor musunuz?
Welcher

Yeniden yazma API'sini kullanmanız gerekir. Bu çok zor olmamalı. Verileri ikinci sayfaya gönderdiğinizden emin olun.
setterGetter

@Welcher: Bu sayfalar, kontrol panelindeki WordPress teklifleriyle aynı değildir. Verileri veritabanına kaydetmeleri gerekir, ancak sorun bu değildir. @ .setterGetter: Verilerin ilk sayfadan ikinci sayfaya nasıl aktarılacağını ve formu gösteren PHP dosyasını nereye (eylem?) ekleyeceğinize dair bir örnek var mı?
user1257255

Giriş alanlarının birden çok slaytına (javascript ve / veya css) sahip tek bir sayfa formu kullanmayı düşündünüz mü?
birgire

Yanıtlar:


57

Bir ön uç sayfasını ziyaret ettiğinizde, WordPress veritabanını sorgulayacaktır ve sayfanız veritabanında yoksa, bu sorgu gerekli değildir ve sadece kaynak kaybıdır.

Neyse ki, WordPress ön uç isteklerini özel bir şekilde ele almanın bir yolunu sunuyor. Bu 'do_parse_request'filtre sayesinde yapılır .

falseBu kancaya döndüğünüzde , WordPress'in istekleri işlemesini durdurabilir ve kendi özel yolunuzla yapabilirsiniz.

Bununla birlikte, sanal sayfaları kullanımı kolay (ve yeniden kullanım) bir şekilde işleyebilen basit bir OOP eklentisi oluşturmanın bir yolunu paylaşmak istiyorum.

İhtiyacımız olan

  • Sanal sayfa nesneleri için bir sınıf
  • Bir isteğe bakacak ve sanal bir sayfa için kullanılacak bir denetleyici sınıfı, uygun şablonu kullanarak gösterin
  • Şablon yükleme için bir sınıf
  • Her şeyi çalıştıracak kancaları eklemek için ana eklenti dosyaları

Arayüzler

Sınıfları oluşturmadan önce, yukarıda listelenen 3 nesne için arayüzler yazalım.

Önce sayfa arayüzü (dosya 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();
}

Çoğu yöntem sadece alıcılar ve ayarlayıcılardır, açıklamaya gerek yoktur. WP_PostSanal sayfadan bir nesne almak için son yöntem kullanılmalıdır .

Denetleyici arayüzü (dosya 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 ); 
}

ve şablon yükleyici arabirimi (dosya 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 yorumları bu arayüzler için oldukça açık olmalıdır.

Plan

Artık arayüzlerimiz olduğuna ve somut sınıflar yazmadan önce iş akışımızı gözden geçirelim:

  • İlk önce bir Controllersınıfı başlatırız (uygularız ControllerInterface) ve (muhtemelen bir kurucuya) bir TemplateLoadersınıf örneği uygularız (uygular TemplateLoaderInterface)
  • Açık initkanca dediğimiz ControllerInterface::init()kurulum kadar verilmesi yöntemini kontrolörü ve tüketici kod sanal sayfalar eklemek için kullanacağı kancayı ateşe.
  • Açık 'do_parse_request' biz arayacak ControllerInterface::dispatch()ve orada tüm sanal sayfalar eklendi kontrol ve bunlardan biri geçerli isteğin aynı URL'ye sahipse, bunu gösterecektir; tüm temel global değişkenleri ayarladıktan sonra ( $wp_query, $post). Ayrıca TemplateLoaderdoğru şablonu yüklemek için sınıfı kullanacağız .

Bazı çekirdek kanca tetikleyecek iş akışı bu süresince gibi wp, template_redirect, template_include... eklenti daha esnek hale ve bunların iyi bir sayı ile çekirdek ve diğer eklentileri, ya da en azından ile uyumluluğu sağlamak için.

Önceki iş akışının yanı sıra şunları da yapmamız gerekecek:

  • Ana döngü çalıştıktan sonra kancaları ve global değişkenleri temizleyin, yine çekirdek ve 3. taraf koduyla uyumluluğu artırmak için
  • the_permalinkGerektiğinde doğru sanal sayfa URL'sini döndürmesi için bir filtre ekleyin .

Beton Sınıfları

Şimdi somut sınıflarımızı kodlayabiliriz. Sayfa sınıfı (dosya Page.php) ile başlayalım :

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

Arayüzü uygulamaktan başka bir şey yoktur.

Şimdi denetleyici sınıfı (dosya 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();
    }
}

Temelde sınıf SplObjectStorage, eklenen tüm sayfa nesnelerinin saklandığı bir nesne oluşturur .

Açık olarak 'do_parse_request', denetleyici sınıfı, eklenen sayfalardan birinde geçerli URL için bir eşleşme bulmak üzere bu depolamayı döngüler.

Eğer bulunursa, sınıf tam olarak planladığımız şeyi yapar: bazı kancaları, kurulum değişkenlerini tetikleyin ve şablonu sınıf genişletme yoluyla yükleyin TemplateLoaderInterface. Ondan sonra, sadece exit().

Şimdi son sınıfı yazalım:

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

Sanal sayfada depolanan şablonlar varsayılan olarak bir dizide birleştirilir page.phpve index.phpşablon yüklenmeden önce 'template_redirect'esneklik eklemek ve uyumluluğu artırmak için.

Bundan sonra, bulunan şablon özel 'virtual_page_template've çekirdek 'template_include'filtrelerden geçer: yine esneklik ve uyumluluk için.

Son olarak şablon dosyası yeni yüklendi.

Ana eklenti dosyası

Bu noktada dosyayı eklenti üstbilgileriyle yazmamız ve iş akışımızı gerçekleştirecek kancaları eklemek için kullanmamız gerekir:

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

Gerçek dosyaya muhtemelen eklenti ve yazar bağlantıları, açıklama, lisans vb. Gibi daha fazla başlık ekleyeceğiz.

Eklenti Özeti

Tamam, eklentimizle işimiz bitti. Tüm kod burada bir Gist bulunabilir .

Sayfa Ekleme

Eklenti hazır ve çalışıyor, ancak sayfa eklemedik.

Bu, eklentinin içinde, temanın içinde functions.php, başka bir eklentide vb.

Sayfa eklemek sadece şu konularla ilgilidir:

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

} );

Ve bunun gibi. İhtiyacınız olan tüm sayfaları ekleyebilirsiniz, sayfalar için göreli URL'ler kullanmayı unutmayın.

Şablon dosyasının içinde tüm WordPress şablon etiketlerini kullanabilir ve ihtiyacınız olan tüm PHP ve HTML'yi yazabilirsiniz.

Global gönderi nesnesi, sanal sayfamızdan gelen verilerle doldurulur. Sanal sayfanın kendisine $wp_query->virtual_pagedeğişken üzerinden erişilebilir .

Sanal bir sayfanın URL'sini almak home_url(), sayfayı oluşturmak için kullanılan aynı yola geçmek kadar kolaydır :

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

Yüklenen şablondaki ana döngüde the_permalink()sanal sayfaya doğru kalıcı bağlantı döndüreceğini unutmayın.

Sanal sayfalar için stiller / komut dosyaları hakkında notlar

Muhtemelen sanal sayfalar eklendiğinde, özel stillerin / komut dosyalarının sıralanması ve daha sonra sadece wp_head()özel şablonlarda kullanılması da istenir .

Bu çok kolaydır, çünkü sanal sayfalar $wp_query->virtual_pagedeğişkene bakarak kolayca tanınır ve sanal sayfalar URL'lerine bakarak diğerinden ayırt edilebilir.

Sadece bir örnek:

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

} );

OP notları

Bir sayfadan diğerine veri aktarmak bu sanal sayfalarla ilgili değildir, sadece genel bir görevdir.

Ancak, ilk sayfada bir formunuz varsa ve oradan ikinci sayfaya veri aktarmak istiyorsanız, form actionözelliğinde ikinci sayfanın URL'sini kullanmanız yeterlidir .

İlk sayfa şablonu dosyasında şunları yapabilirsiniz:

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

ve sonra ikinci sayfa şablonu dosyasında:

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

9
Sadece sorunun kendisi üzerinde değil, aynı zamanda bir OOP tarzı eklentisi ve daha fazlası yaratma konusunda inanılmaz kapsamlı bir cevap. Benim oyumu kesin olarak aldın, daha fazlasını hayal et, cevabın kapsadığı her seviye için bir tane.
Nicolai

2
Çok kaygan ve düz bir çözüm. Yükseltilmiş, tweetlenmiş.
kaiser

Denetleyici'deki kod biraz yanlış ... checkRequest (), localhost / wordpress döndüren home_url () 'den yol bilgisi alıyor. Preg_replace ve add_query_arg öğelerinden sonra, bu url / wordpress / virtual-page olur. Ve checkRequest'deki trim'den sonra bu url wordpress / sanaldır. WordPress, etki alanının kök klasörüne yüklenirse bu işe yarar. Sağ url döndürecek uygun işlevi bulamıyorum çünkü bu sorun için düzeltme sağlayabilir misiniz. Herşey için teşekkürler! (Mükemmel hale geldikten sonra cevabı kabul edeceğim :)
user1257255 25:14

2
Tebrikler, güzel cevap ve bu işi bir sürü ücretsiz çözüm olarak görmem gerekiyor.
bueltge

@GM: Benim durumumda WordPress ... / htdocs / wordpress / dizinine kurulur ve site localhost / wordpress'te bulunur . home_url () localhost / wordpress ve add_query_arg (array ()) / wordpress / virtual-page / döndürür. $ Yolunu karşılaştırdığımızda ve $ $ bu-> sayfalar-> current () -> checkRequest () içindeki getUrl () sorununu çözdüğümüzde, $ yolu olduğundan wordpress/virtual-pageve sayfanın kesilmiş URL'si olduğundan sorun olur virtual-page.
user1257255 26:14

0

Bir zamanlar burada açıklanan bir çözüm kullandım: http://scott.sherrillmix.com/blog/blogger/creating-a-better-fake-post-with-a-wordpress-plugin/

Aslında, ben kullanırken, ben bir kerede birden fazla sayfa kaydedebilirsiniz bir şekilde çözüm uzatmak (bir kodun geri kalanı yukarıdaki paragrafa bağladığım çözüm +/- benzer).

Çözüm tho izin güzel permalinks olmasını gerektirir ...

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

Formumu yerleştirebileceğim özel şablon ne olacak?
user1257255

contentSahte sayfayı kaydettirirken dizide sayfanın gövdesinde görüntüleniyor - bir HTML'nin yanı sıra basit bir metin veya bir kısa kod da içerebilir.
david.binda
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.