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 .
false
Bu 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_Post
Sanal 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
Controller
sınıfı başlatırız (uygularız ControllerInterface
) ve (muhtemelen bir kurucuya) bir TemplateLoader
sınıf örneği uygularız (uygular TemplateLoaderInterface
)
- Açık
init
kanca 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 TemplateLoader
doğ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_permalink
Gerektiğ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.php
ve 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_page
değ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_page
değ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>