Cum creezi o pagină "virtuală" în WordPress
Încerc să creez un endpoint API personalizat în WordPress și am nevoie să redirecționez cererile către o pagină virtuală în root-ul WordPress către o pagină reală care vine cu plugin-ul meu. Deci, practic, toate cererile către o pagină sunt direcționate către cealaltă.
Exemplu:
http://mysite.com/my-api.php
=> http://mysite.com/wp-content/plugins/my-plugin/my-api.php
Scopul acestui lucru este de a face URL-ul pentru endpoint-ul API cât mai scurt posibil (similar cu http://mysite.com/xmlrpc.php
), dar de a include fișierul real al endpoint-ului API în plugin, fără a necesita ca utilizatorul să mute fișiere în instalare sau să modifice fișierele de bază.
Prima mea încercare a fost să adaug o regulă de rescriere personalizată. Totuși, aceasta a avut două probleme.
- Endpoint-ul avea întotdeauna o bară oblică la final. A devenit
http://mysite.com/my-api.php/
- Regula mea de rescriere a fost aplicată doar parțial. Nu redirecționa către
wp-content/plugins...
, ci cătreindex.php&wp-content/plugins...
. Acest lucru a dus la afișarea fie a unei erori de pagină negăsită, fie la revenirea la pagina principală.
Idei? Sugestii?

Există două tipuri de reguli de rescriere în WordPress: reguli interne (stocate în baza de date și analizate de WP::parse_request()), și reguli externe (stocate în .htaccess
și analizate de Apache). Puteți alege oricare dintre acestea, în funcție de cât de mult din WordPress aveți nevoie în fișierul apelat.
Reguli externe:
Regula externă este cea mai ușor de configurat și de urmărit. Aceasta va executa my-api.php
în directorul pluginului dvs., fără a încărca nimic din WordPress.
add_action( 'init', 'wpse9870_init_external' );
function wpse9870_init_external()
{
global $wp_rewrite;
$plugin_url = plugins_url( 'my-api.php', __FILE__ );
$plugin_url = substr( $plugin_url, strlen( home_url() ) + 1 );
// Modelul este prefixat cu '^'
// Substituția este prefixată cu "rădăcina de acasă", cel puțin un '/'
// Acest lucru este echivalent cu adăugarea acesteia la `non_wp_rules`
$wp_rewrite->add_external_rule( 'my-api.php$', $plugin_url );
}
Reguli interne:
Regula internă necesită ceva mai multă muncă: mai întâi adăugăm o regulă de rescriere care adaugă variabile de interogare, apoi facem această variabilă de interogare publică, iar apoi trebuie să verificăm existența acestei variabile de interogare pentru a transmite controlul către fișierul nostru de plugin. Până în acest moment, inițializarea obișnuită a WordPress va fi avut loc (ne întrerupem chiar înainte de interogarea obișnuită a postării).
add_action( 'init', 'wpse9870_init_internal' );
function wpse9870_init_internal()
{
add_rewrite_rule( 'my-api.php$', 'index.php?wpse9870_api=1', 'top' );
}
add_filter( 'query_vars', 'wpse9870_query_vars' );
function wpse9870_query_vars( $query_vars )
{
$query_vars[] = 'wpse9870_api';
return $query_vars;
}
add_action( 'parse_request', 'wpse9870_parse_request' );
function wpse9870_parse_request( &$wp )
{
if ( array_key_exists( 'wpse9870_api', $wp->query_vars ) ) {
include 'my-api.php';
exit();
}
return;
}

Vreau doar să adaug că este important să mergi la pagina Permalinks și să dai clic pe "Salvează modificările" în WP-Admin. Am stat o oră încercând diverse variante înainte să-mi dau seama că trebuie să actualizez permalinkurile... Dacă nu știe cineva o funcție care poate face asta?

Pentru regula externă: Deoarece calea către rădăcina site-ului meu conținea un spațiu, acest lucru a făcut ca Apache să eșueze. Spațiile trebuie escapanate dacă există în calea către instalarea WordPress.

Funcționează, dar nu pare să pot accesa variabilele de interogare transmise cu get_query_vars()
în my-api.php. Am verificat ce variabile sunt încărcate. Și singura variabilă setată este un obiect WP
numit $wp
. Cum pot accesa sau transforma acest lucru într-un obiect WP_Query
pentru a putea accesa variabilele transmise cu get_query_vars()
?

@Jules: Când incluzi un fișier cu include
, acesta este executat în scope-ul curent. În acest caz, este funcția wpse9870_parse_request
, care are doar parametrul $wp
. Este posibil ca obiectul global $wp_query
să nu fie setat în acest moment, așa că get_query_var()
nu va funcționa. Totuși, ai noroc: $wp
este clasa care conține membrul query_vars
de care ai nevoie - eu îl folosesc în codul de mai sus.

@JanFabry Mulțumesc. Am înțeles asta după ceva timp. Mă întrebam doar dacă există o modalitate de a converti un obiect WP într-un obiect WP_Query. Dar nu sunt sigur dacă ar exista vreodată nevoie de așa ceva, așa că nu mai contează. Mulțumesc pentru acest cod și pentru toate celelalte fragmente de cod. Am găsit deja multe informații utile pe acest site datorită ție.

Încerc să creez niște reguli de rewrite externe. Am adăugat prima bucată de cod dar tot primesc eroarea 404. btw: am resetat regulile de rewrite

De asemenea, încă primesc o eroare 404 cu regulile de rescriere externe. Doar pentru a clarifica, codul de mai sus merge în fișierul PHP principal al plugin-ului, nu în fișierul de funcții al șabloanelor, corect?

@ethanpil poți (acum?) să resetezi regulile pentru a include noile tale reguli de rescriere dacă regulile de rescriere ale WP nu le includ. Metoda este documentată aici http://codex.wordpress.org/Class_Reference/WP_Rewrite

În cazul meu funcționează pe URL-ul index.php?wpse9870_api=1
și de asemenea my-api.php?wpse9870_api=1
cum pot elimina șirul de interogare?

@Irfan Încerc să obțin același lucru, poți să-mi spui ce ar trebui să scriu în my-api.php?

@Prafulla Kumar Sahu Eu folosesc așa: `add_filter( 'query_vars', 'wpse9870_query_vars' ); function wpse9870_query_vars( $query_vars ) { $query_vars[] = 'getrequest'; return $query_vars; }
add_action( 'parse_request', 'wpse9870_parse_request' );
function wpse9870_parse_request( &$wp )
{
if ( array_key_exists( 'getrequest', $wp->query_vars ) ) {
include 'my-api.php';
exit();
}
return;
}`
și URL-ul este http://homeurl/?getrequest

Regula internă este cu adevărat stocată în baza de date? Funcția add_rewrite_rule
inserează în baza de date? Se pare că este doar stocată în codul sursă.

Scuze pentru întrebarea de începător dar... unde ar trebui să pun apelul add_action( 'init'...
? L-am pus în metoda __construct()
a pluginului meu, dar metoda callback nu este niciodată executată. Am încercat să repornesc serverul, etc.

Încerc să folosesc regula External dar primesc eroarea 403 când încerc să accesez direct. Fișierul .htaccess implicit din wp-content blochează accesul la fișierul php din directorul pluginului meu și niciuna dintre regulile de .htaccess pe care le-am adăugat pentru a permite accesul nu funcționează. Aveți sugestii?

Aceasta a funcționat pentru mine. Niciodată nu am atins API-ul de rescriere, dar sunt întotdeauna deschis să încerc direcții noi. Următoarea soluție a funcționat pe serverul meu de test pentru WordPress 3.0, amplasat într-un subfolder al localhost-ului. Nu anticipez probleme dacă WordPress este instalat în rădăcina web.
Pur și simplu introduceți acest cod într-un plugin și încărcați fișierul numit "taco-kittens.php" direct în folderul de plugin-uri. Va trebui să forțați o actualizare a permalink-urilor. Cred că se spune că cel mai bun moment pentru a face acest lucru este la activarea plugin-ului.
function taco_kitten_rewrite() {
$url = str_replace( trailingslashit( site_url() ), '', plugins_url( '/taco-kittens.php', __FILE__ ) );
add_rewrite_rule( 'taco-kittens\\.php$', $url, 'top' );
}
add_action( 'wp_loaded', 'taco_kitten_rewrite' );
Cele mai bune urări, -Mike

Am primit o eroare de acces interzis în timp ce încercam acest cod. Bănuiesc că serverul meu sau WP nu au apreciat URL-ul absolut. Această variantă, pe de altă parte, a funcționat perfect: add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );

Există vreun motiv să nu faci ceva de genul acesta în schimb?
Apoi pur și simplu conectează plugin-ul tău la acțiunea 'init' și verifică acea variabilă GET. Dacă există, fă ce trebuie să facă plugin-ul tău și folosește die()

Asta ar funcționa, dar încerc să ofer o distincție foarte clară între variabilele de interogare și punctul final real. Ar putea exista și alte argumente de interogare în viitor și nu vreau ca utilizatorii să le amestece.

Ce-ar fi dacă ai păstra rescrierea, dar ai rescrie-o la variabila GET? Ai putea, de asemenea, să te uiți la cum funcționează rescrierea pentru robots.txt. S-ar putea să te ajute să înțelegi cum să eviți redirecționarea către my-api.php/

Poate nu înțeleg pe deplin întrebările tale, dar un simplu shortcode ar putea rezolva problema ta?
Pași:
- Clientul creează o pagină, de exemplu http://mysite.com/my-api
- Clientul adaugă un shortcode pe acea pagină, de exemplu [my-api-shortcode]
Noua pagină acționează ca un punct final de API, iar shortcode-ul tău trimite cereri către codul pluginului tău din http://mysite.com/wp-content/plugins/my-plugin/my-api.php
(desigur, acest lucru înseamnă că my-api.php ar avea shortcode-ul definit)
Probabil poți automatiza pașii 1 și 2 prin intermediul pluginului.

Nu am prea lucrat cu rescrieri până acum, așa că probabil această soluție este puțin brută, dar pare să funcționeze:
function api_rewrite($wp_rewrite) {
$wp_rewrite->non_wp_rules['my-api\.php'] = 'wp-content/plugins/my-plugin/my-api.php';
file_put_contents(ABSPATH.'.htaccess', $wp_rewrite->mod_rewrite_rules() );
}
Funcționează dacă legi această funcție de hook-ul 'generate_rewrite_rules', dar probabil există o metodă mai bună, deoarece nu dorești să rescrii fișierul .htaccess la fiecare încărcare de pagină.
Se pare că nu pot să mă abțin să nu editez propriile postări... probabil ar trebui să fie plasată în callback-ul de activare și să folosească global $wp_rewrite în loc de asta. Și apoi să elimini intrarea din non_wp_rules și să scrii din nou în .htaccess în callback-ul de dezactivare.
În final, scrierea în .htaccess ar trebui să fie puțin mai sofisticată, deoarece dorești să înlocuiești doar secțiunea WordPress din acel fișier.

Am avut o cerință similară și am dorit să creez mai multe puncte finale bazate pe slug-uri unice care să indice conținut generat de plugin.
Aruncă o privire la codul sursă al plugin-ului meu: https://wordpress.org/extend/plugins/picasa-album-uploader/
Tehnica pe care am folosit-o începe prin adăugarea unui filtru pentru the_posts
pentru a examina cererea primită. Dacă plugin-ul ar trebui să o gestioneze, un post dummy este generat și o acțiune este adăugată pentru template_redirect
.
Când acțiunea template_redirect
este apelată, trebuie să rezulte în afișarea întregului conținut al paginii care urmează să fie afișată și să se încheie sau ar trebui să se întoarcă fără a genera nicio ieșire. Vezi codul din wp_include/template-loader.php
și vei înțelege de ce.

Folosesc o altă abordare care constă în forțarea paginii de start să încarce un titlu personalizat, conținut și șablon de pagină.
Soluția este foarte elegantă, deoarece poate fi implementată atunci când un utilizator accesează un link prietenos precum http://example.com/?plugin_page=myfakepage
Este foarte ușor de implementat și ar trebui să permită un număr nelimitat de pagini.
Cod și instrucțiuni aici: Generează o pagină Wordpress personalizată/falsă/virtuală din mers

acesta este un exemplu gata de producție, mai întâi creează clasa pentru pagină virtuală:
class VirtualPage
{
private $query;
private $title;
private $content;
private $template;
private $wp_post;
function __construct($query = '/index2', $template = 'page', $title = 'Fără titlu')
{
$this->query = filter_var($query, FILTER_SANITIZE_URL);
$this->setTemplate($template);
$this->setTitle($title);
}
function getQuery()
{
return $this->query;
}
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;
}
public function updateWpQuery()
{
global $wp, $wp_query;
// Actualizează interogarea principală
$wp_query->current_post = $this->wp_post->ID;
$wp_query->found_posts = 1;
$wp_query->is_page = true;//parte importantă
$wp_query->is_singular = true;//parte importantă
$wp_query->is_single = false;
$wp_query->is_attachment = false;
$wp_query->is_archive = false;
$wp_query->is_category = false;
$wp_query->is_tag = false;
$wp_query->is_tax = false;
$wp_query->is_author = false;
$wp_query->is_date = false;
$wp_query->is_year = false;
$wp_query->is_month = false;
$wp_query->is_day = false;
$wp_query->is_time = false;
$wp_query->is_search = false;
$wp_query->is_feed = false;
$wp_query->is_comment_feed = false;
$wp_query->is_trackback = false;
$wp_query->is_home = false;
$wp_query->is_embed = false;
$wp_query->is_404 = false;
$wp_query->is_paged = false;
$wp_query->is_admin = false;
$wp_query->is_preview = false;
$wp_query->is_robots = false;
$wp_query->is_posts_page = false;
$wp_query->is_post_type_archive = false;
$wp_query->max_num_pages = 1;
$wp_query->post = $this->wp_post;
$wp_query->posts = array($this->wp_post);
$wp_query->post_count = 1;
$wp_query->queried_object = $this->wp_post;
$wp_query->queried_object_id = $this->wp_post->ID;
$wp_query->query_vars['error'] = '';
unset($wp_query->query['error']);
$GLOBALS['wp_query'] = $wp_query;
$wp->query = array();
$wp->register_globals();
}
public function createPage()
{
if (is_null($this->wp_post)) {
$post = new stdClass();
$post->ID = -99;
$post->ancestors = array(); // 3.6
$post->comment_status = 'closed';
$post->comment_count = 0;
$post->filter = 'raw';
$post->guid = home_url($this->query);
$post->is_virtual = true;
$post->menu_order = 0;
$post->pinged = '';
$post->ping_status = 'closed';
$post->post_title = $this->title;
$post->post_name = sanitize_title($this->template); // adaugă număr aleatoriu pentru a evita conflictele
$post->post_content = $this->content ?: '';
$post->post_excerpt = '';
$post->post_parent = 0;
$post->post_type = 'page';
$post->post_status = 'publish';
$post->post_date = current_time('mysql');
$post->post_date_gmt = current_time('mysql', 1);
$post->modified = $post->post_date;
$post->modified_gmt = $post->post_date_gmt;
$post->post_password = '';
$post->post_content_filtered = '';
$post->post_author = is_user_logged_in() ? get_current_user_id() : 0;
$post->post_content = '';
$post->post_mime_type = '';
$post->to_ping = '';
$this->wp_post = new WP_Post($post);
$this->updateWpQuery();
@status_header(200);
wp_cache_add(-99, $this->wp_post, 'posts');
}
return $this->wp_post;
}
}
În pasul următor conectează acțiunea template_redirect
și gestionează pagina ta virtuală ca mai jos
add_action( 'template_redirect', function () {
switch ( get_query_var( 'name' ,'') ) {
case 'contact':
// http://siteultau/contact ==> încarcă page-contact.php
$page = new VirtualPage( "/contact", 'contact',__('Contactează-mă') );
$page->createPage();
break;
case 'archive':
// http://siteultau/archive ==> încarcă page-archive.php
$page = new VirtualPage( "/archive", 'archive' ,__('Arhive'));
$page->createPage();
break;
case 'blog':
// http://siteultau/blog ==> încarcă page-blog.php
$page = new VirtualPage( "/blog", 'blog' ,__('Blog'));
$page->createPage();
break;
}
} );

Folosesc o abordare similară cu cea a lui Xavi Esteve menționată mai sus, care a încetat să funcționeze din cauza unei actualizări WordPress, după cum am putut observa în a doua jumătate a anului 2013.
Este documentată în detaliu aici: https://stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template
Partea esențială a abordării mele este utilizarea șablonului existent, astfel încât pagina rezultată să pară că face parte din site; am dorit să fie cât mai compatibilă cu toate temele, sperând că va funcționa și în versiunile viitoare de WordPress. Timpul va spune dacă am avut dreptate!
