Complemento de WordPress que genera páginas virtuales y usa una plantilla de tema

10 minutos de lectura

avatar de usuario
Brian C.

Necesito poder generar páginas falsas/virtuales/dinámicas basadas en una URL como http://www.mycinema.com/wpcinema/movie/MOVIEID para poder mostrar películas para cines con información sobre la película e información de transmisión de sesiones en vivo.

Después de pasar muchas horas investigando, no parece haber mucho escrito sobre cómo hacer páginas virtuales en WordPress, ¡así que escribiré mis experiencias después de resolver esto!

Hasta ahora, el plan actual es usar los dos filtros: template_redirect para establecer la plantilla en la plantilla page.php del complemento actual y the_content para insertar el contenido. La idea es usar la plantilla del tema para que el tema de las páginas encaje bien con el sitio.

(Obtuve este enfoque de esta excelente página de 2012 de Xavi Esteve).

Tengo dos problemas:

  1. ¿Cuál es la mejor manera, la más a prueba de balas, de hacer esto? ¿Estoy usando el enfoque equivocado? Pensé que usar la plantilla del tema actual probablemente proporcionaría el mejor ajuste actual para el estilo del sitio web.

  2. TwentyTwelve no parece estar llamando al filtro the_content en el contexto en el que lo estoy usando. Sospecho que estoy haciendo algo mal, pero no puedo encontrar el problema. Esto probablemente esté estrechamente relacionado con la pregunta 1. TwentyTwelve definitivamente llama a the_content para una página normal, e incluso un add_filter() temprano no se activa en mi código.

Ayer descubrí get_template_part() y me pregunté si debería usar eso en lugar de buscar manualmente en la carpeta secundaria y luego en la principal y ejecutar una inclusión.

No estaría preguntando, pero estoy al final de mi ingenio después de haber buscado en Google extensamente, posiblemente por los términos de búsqueda incorrectos.

He considerado tipos de publicaciones personalizadas, pero hay varias complejidades en torno a esto (incluido el contenido que puede cambiar cada pocos minutos), lo que significa que una página generada dinámicamente funciona mucho mejor.

Este es un extracto del código que he escrito para explicar más el problema:

add_action('parse_request', array(&$this, 'vm_parse_request'));

function vm_parse_request( &$wp )
{
    global $wp;
    if (empty($wp->query_vars['pagename']))
        return; // page isn't permalink

    $p = $wp->query_vars['pagename'];

    if (! preg_match("#wp-cinema/movie/([^/]+)#", $p, $m))
        return;

    // setup hooks and filters to generate virtual movie page
    add_action('template_redirect', array(&$this, 'vm_template_redir'));
    add_filter('the_content', array(&$this, 'vm_the_content'));
}

function vm_template_redir()
{
    // Reset currrently set 404 flag as this is a plugin-generated page
    global $wp_query;
    $wp_query->is_404 = false;

    $template="page.php";

    include(STYLESHEETPATH."/page.php"); // child
    // parent case left out for brevity

    exit;
}


function vm_the_content($content)
{
    return "my new content (actually generated dynamically)";
}

Esto va a ser algo cada vez más común en WordPress. ¿Alguien puede ofrecer sugerencias o ayuda? Cualquier cosa es muy apreciada.

  • Hola Brian, quisiera saber si puedo aplicar paginas virtuales a mi pregunta wordpress.stackexchange.com/questions/110118/… ¿qué piensas?

    – Radek

    15 de agosto de 2013 a las 6:26

  • Radek: aunque no lo he probado, creo que podría hacerlo, si puede configurar la página de inicio para que no sea una página existente. O bien, podría usar mi código para anularlo.

    – Brian C.

    20 de febrero de 2014 a las 1:17

avatar de usuario
Brian C.

(Actualización al pie del artículo, incluido el enlace a la esencia del código de trabajo mejorado)

¡Quería publicar una respuesta a esto, ya que parece que NINGUNA de las consultas sobre las páginas virtuales de WordPress aquí tenía respuestas! Y hubo mucha sangre involucrada en obtener la respuesta a esta, probarla y asegurarse de que funcionara bien. Con suerte, esto salvará a algunos otros del dolor por el que pasé…

Resulta que en 2013 con WordPress 3.5.2+ (ahora 3.6 desde hace una semana) la solución de Xavi Esteve mencionada anteriormente ya no funciona, ya que WordPress ha evolucionado, joder.

Usando el método template_redirect anterior por sí mismo, el problema es que, en lo que respecta a WordPress, no hay contenido de página/publicación y, por lo tanto, muchos temas no llamarán a the_content(), por lo que nunca se llama a mi código en el filtro the_content.

La mejor solución hoy en día parece ser engancharse al filtro ‘the_posts’ y devolver una pseudo-página, sin embargo, eso en sí mismo no tiene un tema adjunto.

La solución a la falta de tema fue hibridar esto con parte del enfoque de Xavi Esteve para permitirme cambiar la plantilla que se utiliza para generar la página.

Este enfoque debería funcionar de inmediato para la mayoría de los temas de WordPress, si no para todos, que era mi objetivo y está funcionando muy bien con los temas que he probado hasta ahora.

Usé el enfoque documentado por Dave Jesch en esta página (hay otras versiones de esto, pero Dave es el único que lo explicó cuidadosamente, ¡gracias Dave!): http://davejesch.com/wordpress/wordpress-tech/creating-virtual-pages-in-wordpress/

También pasé por mucho dolor aquí con la sección de comentarios de WordPress que aparece en la parte inferior de la página en algunos temas. La solución para esto aparecerá en el archivo vinculado anteriormente y probablemente esté fuera del alcance de esta solución específica.

Además, para evitar una advertencia con WordPress 3.5.2+, también tuve que agregar un miembro de la publicación:

 $post->ancestors = array();

Esta solución se utiliza en el complemento de WordPress wp-cinema (archivo vistas.php si desea obtener un código de trabajo, debe registrarse en las próximas semanas). Si hay problemas con el enfoque, mantendré ese archivo actualizado ya que es parte de un proyecto más grande.

La solución de trabajo completa se encuentra a continuación. Esto se extrae de un código mucho más largo que también evita que aparezcan comentarios, etc. (consulte el enlace proporcionado arriba). El código:

add_action('parse_request', 'vm_parse_request');


// Check page requests for Virtual movie pages
// If we have one, generate 'movie details' Virtual page.
// ...
//
function vm_parse_request(&$wp)
{
    if (empty($wp->query_vars['pagename']))
       return; // page isn't permalink

    $p = $wp->query_vars['pagename'];

    if (! preg_match("#wp-cinema/movie/([^/]+)#", $p, $m))
       return;

    // setup hooks and filters to generate virtual movie page
    add_action('template_redirect', 'vm_template_redir');

    $this->vm_body = "page body text";

    add_filter('the_posts', 'vm_createdummypost');

    // now that we know it's my page,
    // prevent shortcode content from having spurious <p> and <br> added
    remove_filter('the_content', 'wpautop');
}


// Setup a dummy post/page 
// From the WP view, a post == a page
//
function vm_createdummypost($posts)
{
    // have to create a dummy post as otherwise many templates
    // don't call the_content filter
    global $wp, $wp_query;

    //create a fake post intance
    $p = new stdClass;
    // fill $p with everything a page in the database would have
    $p->ID = -1;
    $p->post_author = 1;
    $p->post_date = current_time('mysql');
    $p->post_date_gmt =  current_time('mysql', $gmt = 1);
    $p->post_content = $this->vm_body;
    $p->post_title = $this->vm_title;
    $p->post_excerpt="";
    $p->post_status="publish";
    $p->ping_status="closed";
    $p->post_password = '';
    $p->post_name="movie_details"; // slug
    $p->to_ping = '';
    $p->pinged = '';
    $p->modified = $p->post_date;
    $p->modified_gmt = $p->post_date_gmt;
    $p->post_content_filtered = '';
    $p->post_parent = 0;
    $p->guid = get_home_url("https://stackoverflow.com/" . $p->post_name); // use url instead?
    $p->menu_order = 0;
    $p->post_type="page";
    $p->post_mime_type="";
    $p->comment_status="closed";
    $p->comment_count = 0;
    $p->filter="raw";
    $p->ancestors = array(); // 3.6

    // reset wp_query properties to simulate a found 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;
    unset($wp_query->query['error']);
    $wp->query = array();
    $wp_query->query_vars['error'] = '';
    $wp_query->is_404 = FALSE;

    $wp_query->current_post = $p->ID;
    $wp_query->found_posts = 1;
    $wp_query->post_count = 1;
    $wp_query->comment_count = 0;
    // -1 for current_comment displays comment if not logged in!
    $wp_query->current_comment = null;
    $wp_query->is_singular = 1;

    $wp_query->post = $p;
    $wp_query->posts = array($p);
    $wp_query->queried_object = $p;
    $wp_query->queried_object_id = $p->ID;
    $wp_query->current_post = $p->ID;
    $wp_query->post_count = 1;

    return array($p);
}


// Virtual Movie page - tell wordpress we are using the page.php
// template if it exists (it normally will).
//
// We use the theme page.php if we possibly can; if not, we do our best.
// The get_template_part() call will use child theme template if it exists.
// This gets called before any output to browser
//
function vm_template_redir()
{
    // Display movie template using WordPress' internal precedence
    //  ie: child > parent; page-movie.php > page.php
    //  this call includes the template which outputs the content
    get_template_part('page', 'movie');

    exit;
}

Por cierto, es importante decir que siento que esto es más o menos un truco y me encantaría saber cómo se podría hacer mejor. Además, me encantaría ver a WordPress dar un paso adelante y proporcionar una API para generar páginas falsas. (Sospecho que tienen razones ideológicas por las que no lo harán, pero sería bueno ver sus soluciones a esto, incluso si son alternativas, explicadas en profundidad); Personalmente, creo que hay casos en los que no quiero entrometerme en el sitio de un usuario solo para generar páginas.

ACTUALIZACIÓN de febrero de 2014: He resumido esto en una clase que debería proporcionar suficiente flexibilidad para la mayoría de las aplicaciones: https://gist.github.com/brianoz/9105004

  • Desde entonces, he convertido esto en una clase para administrar varias páginas virtuales. La clase llama a una función de contenido proporcionada cuando coincide una expresión regular. Cuando me sienta seguro sobre esto, reemplazaré el enlace de arriba con un enlace al archivo de clase (que incluye un ejemplo). ¡Púlsame si lo necesitas y lo he olvidado! 🙂

    – Brian C.

    13 de agosto de 2013 a las 2:00

  • Este es un método interesante. He estado tratando de hacer algo similar durante un tiempo con un éxito moderado. ¿Tiene un enlace al archivo de clase que estaba poniendo a disposición?

    – La sal

    11 de enero de 2014 a las 17:28

  • Lo siento, sí; Olvidé que ya lo había hecho. Gist está aquí (artículo actualizado también): gist.github.com/brianoz/9105004 Esto debería ser lo suficientemente flexible para funcionar con la mayoría de las aplicaciones, se agradecen los comentarios.

    – Brian C.

    20 de febrero de 2014 a las 1:12


  • La clase vinculada a Gist funcionó perfectamente para mi caso de uso (creando páginas “virtuales” basadas en la primera parte de la ruta de la URL). Vale la pena leer los comentarios en la página Gist: hay tres líneas de código que deberá modificar para que funcione.

    –Andrew Sauder

    22 de agosto de 2015 a las 9:07

¿Ha sido útil esta solución?