Use AJAX en shortcode

9

Tengo el siguiente código para el shortcode para mostrar una cita aleatoria. Pregunta: ¿cómo hacer que un botón muestre una nueva cita aleatoria? Quiero decir, presionaría el botón y te mostraría una nueva cita (sin actualizar la página, por supuesto).

function random_quote() {

    // quotes file
     $array = file("/path to txt file");

    // generate a random number between 0 and the total count of $array minus 1
    // we minus 1 from the total quotes because array indices start at 0 rather than 1 by default
    $r = rand(0,count($array)-1);

    // return the quote in the array with an indices of $r - our random number
    return $array[rand(0,count($array)-1)];
}

add_shortcode( 'randomquotes', 'random_quote');

¿Estoy interesado en cómo puedes actualizar el contenido de la página usando ajax en WordPress? En mi situación, porque de hecho sea exactamente eso.

Perdón por mi mal ingles. Espero que me entiendas. ¡Gracias!

usuario23769
fuente

Respuestas:

4

En primer lugar, esto es muy límite dentro del alcance de WPSE, en absoluto.
Además del shortcode para activar la salida HTML inicial, esto es realmente solo AJAX.

De todos modos, dicho esto, así es como se hace:

El PHP

Suponiendo que el fragmento de PHP anterior que proporcionó es funcional, coloque lo siguiente en un archivo php para la llamada ajax:

/wp-content/themes/%your_theme%/js/ajax-load-quote.php

 <?php
 /* uncomment the below, if you want to use native WP functions in this file */
// require_once('../../../../wp-load.php');

 $array = file( $_POST['file_path'] ); // file path in $_POST, as from the js
 $r = rand( 0, count($array) - 1 );

 return '<p>' . $array[$r] . '</p>';
 ?>

Para referencia futura y para que esta respuesta sea útil para otros: wp-load.phptenga en cuenta que debe incluirse para hacer uso de la funcionalidad nativa de WordPress. El caso más común probablemente sea la necesidad de WP_Queryo $wpdb.

La estructura HTML

En el contenido de una página, un widget o un archivo de plantilla:

<div id="randomquotes">
    <p>I would rather have my ignorance than another man’s knowledge,
       because I have so much more of it.<br />
       -- Mark Twain, American author & Playwright</p>
</div>
<a id="newquote" class="button" href="#" title="Gimme a new one!">New Quote</a>

Obviamente, esto puede ajustarse a su gusto, pero por el bien de este ejemplo, esto es con lo que vamos.
Generaremos lo anterior a través de un shortcode más adelante.

El jQuery

/wp-content/themes/%your_theme%/js/ajax-load-quote.js

function ajaxQuote() {
    var theQuote = jQuery.ajax({
        type: 'POST',
        url: ajaxParams.themeURI+'js/ajax-load-quote.php',
        /* supplying the file path to the ajax loaded php as a $_POST variable */
        data: { file_path: ajaxParams.filePath },
        beforeSend: function() {
            ajaxLoadingScreen(true,'#randomquotes');
        },
        success: function(data) {
            jQuery('#randomquotes').find('p').remove();
            jQuery('#randomquotes').prepend(data);
        },
        complete: function() {
            ajaxLoadingScreen(false,'#randomquotes');
        }
    });
    return theQuote;
}
/* Loading screen to be displayed during the process, optional */
function ajaxLoadingScreen(switchOn,element) {
    /* show loading screen */
    if (switchOn) {
        jQuery(''+element).css({
            'position': 'relative'
        });
        var appendHTML = '<div class="ajax-loading-screen appended">
            <img src="'+ajaxParams.themeURI+'images/ajax-loader.gif"
                alt="Loading ..." width="16" height="16" /></div>';
        if( jQuery(''+element).children('.ajax-loading-screen').length === 0 ) {
            jQuery(''+element).append(appendHTML);
        }
        jQuery(''+element).children('.ajax-loading-screen').first().css({
            'display': 'block',
            'visibility': 'visible',
            'filter': 'alpha(opacity=100)',
            '-ms-filter': '"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"',
            'opacity': '1'
        });
    } else {
        /* hide the loading screen */
        jQuery(''+element).children('.ajax-loading-screen').css({
            'display': '',
            'visibility': '',
            'filter': '',
            '-ms-filter': '',
            'opacity': ''
        });
        jQuery(''+element).css({
            'position': ''
        });
    }
}
/* triggering the above via the click event */
jQuery('#newquotes').click( function() {
    var theQuote = ajaxQuote();
    return false;
});

Poniéndolo junto en functions.php

Debajo de su fragmento anterior (que encontrará incluido modificado a continuación), pegue lo siguiente:

function random_quote( $atts ) {
    /* extracts the value of shortcode argument path */
    extract( shortcode_atts( array(
        'path' => get_template_directory_uri() . '/quotes.txt' // default, if not set
    ), $atts ) );
    $array = file( $path );
    $r = rand( 0, count($array) - 1 );
    $output = '<div id="randomquotes">' .
            '<p>' . $array[$r] . '</p>' .
        '</div>' .
        '<a id="newquote" class="button" href="#" title="Gimme a new one!">New Quote</a>';
    /* enqueue the below registered script, if needed */
    wp_enqueue_script( 'ajax-quote' );
    /* supplying the file path to the script */
    wp_localize_script(
        'ajax-quote',
        'ajaxParams',
        array(
            'filePath' => $path,
            'themeURI' => get_template_directory_uri() . '/'
        )
    );
    return $output;
}
add_shortcode( 'randomquotes', 'random_quote');
/* register the js */
function wpse72974_load_scripts() {
    if ( ! is_admin() ) {
        wp_register_script(
           'ajax-quote', 
            get_template_directory_uri() . '/js/ajax-load-quote.js',
            array( 'jquery' ),
            '1.0',
            true
        );
    }
}
add_action ( 'init', 'wpse72974_load_scripts' );

Opcional: el CSS para la pantalla de carga

.ajax-loading-screen {
    display: none;
    visibility: hidden;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    background: #ffffff; /* the background of your site or the container of the quote */
    filter: alpha(opacity=0);
    -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
    opacity: 0;
    -webkit-transition:  opacity .1s;
    -moz-transition:  opacity .1s;
    -ms-transition:  opacity .1s;
    -o-transition: opacity .1s;
    transition: opacity .1s;
    z-index: 9999;
}
.ajax-loading-screen img {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -8px 0 0 -8px;
}

Recursos / Lectura

Johannes Pille
fuente
1
¿Alguna razón para no usar la API AJAX incorporada?
fuxia
@toscho Honestamente, no estoy muy familiarizado con eso. Vale la pena leerlo?
Johannes Pille
Si, absolutamente. :) Agregaré una alternativa.
fuxia
¡Perfecto! ¡Gracias! <br/> Un script funcionará, si configura el argumento de la función? Por ejemplo, ¿estar en el shortcode para proporcionar un enlace a un archivo de texto? <br/> function random_quote ($path) {      $ array = file ("$ path");<br/> ... [randomquote file = "http://exampe.com/file.txt"]<br/> Entonces, ¿funcionará? No estoy muy versado en programación.
user23769
He actualizado la respuesta. Ahora incluye una ruta de archivo establecida por el shortcode [randomquotes path="path/to/file.txt"], transmitida a js y desde allí al script php.
Johannes Pille
7

Puede registrar un script en un shortcode. Se imprimirá en el pie de página, dado que el tema contiene wp_footer().

Cómo funciona:

  1. Registre la devolución de llamada de shortcode con add_shortcode().
  2. En la devolución de llamada de shortcode, registre el script y luego devuelva la salida.
  3. En el script, agregue un botón de actualización, envíe una solicitud POST admin_url( 'admin-ajax.php' )y obtenga nuevos datos. Inserte los datos devueltos en el elemento con el shortcode.

Aquí hay un script de muestra que hace eso. Dos archivos: una clase PHP y un archivo JavaScript. Ambos deben sentarse en el mismo directorio, por ejemplo ajax-shortcode-demo.

ajax-shortcode-demo.php

<?php
/**
 * Plugin Name: AJAX Shortcode Demo
 * Description: How to use AJAX from a shortcode handler named <code>[ajaxdemo]</code>.
 */

add_action( 'wp_loaded', array ( 'Ajax_Shortcode_Demo', 'get_instance' ) );

class Ajax_Shortcode_Demo
{
    /**
     * Current plugin instance
     *
     * @type NULL|object
     */
    protected static $instance = NULL;

    /**
     * Unique action name to trigger our callback
     *
     * @type string
     */
    protected $ajax_action = 'load_demo_data';

    /**
     * CSS class for the shortcode, reused as JavaScript handle.
     *
     * Must be unique too.
     *
     * @type string
     */
    protected $shortcode_class = 'ajaxdemo';

    /**
     * Remeber if we had regsitered a script on a page already.
     *
     * @type boolean
     */
    protected $script_registered = FALSE;

    /**
     * Create a new instance.
     *
     * @wp-hook wp_loaded
     * @return  object $this
     */
    public static function get_instance()
    {
        NULL === self::$instance and self::$instance = new self;
        return self::$instance;
    }

    /**
     * Constructor. Register shortcode and AJAX callback handlers.
     */
    public function __construct()
    {
        add_shortcode( 'ajaxdemo', array ( $this, 'shortcode_handler' ) );

        // register the AJAX callback
        $callback = array ( $this, 'ajax_callback' );
        // user who are logged in
        add_action( "wp_ajax_$this->ajax_action", $callback );
        // anonymous users
        add_action( "wp_ajax_nopriv_$this->ajax_action", $callback );
    }

    /**
     * Render the shortcode.
     */
    public function shortcode_handler()
    {
        $this->register_scripts();

        return sprintf(
            '<div class="%1$s"><b>%2$s</b></div>',
            $this->shortcode_class,
            $this->get_rand()
        );
    }

    /**
     * Return AJAX result.
     *
     * Must 'echo' and 'die'.
     *
     * @wp-hook wp_ajax_$this->ajax_action
     * @wp-hook wp_ajax_nopriv_$this->ajax_action
     * @return int
     */
    public function ajax_callback()
    {
        echo $this->get_rand();
        exit;
    }

    /**
     * Random number.
     *
     * @return int
     */
    protected function get_rand()
    {
        return rand( 1, 1000 );
    }

    /**
     * Register script and global data object.
     *
     * The data will be printent before the linked script.
     */
    protected function register_scripts()
    {
        if ( $this->script_registered )
            return;

        $this->script_registered = TRUE;

        wp_register_script(
            // unique handle
            $this->shortcode_class,
            // script URL
            plugin_dir_url( __FILE__ ) . '/jquery-ajax-demo.js',
            // dependencies
            array ( 'jquery'),
            // version
            'v1',
            // print in footer
            TRUE
        );

        wp_enqueue_script( $this->shortcode_class );

        $data = array (
            // URL address for AJAX request
            'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
            // action to trigger our callback
            'action'    => $this->ajax_action,
            // selector for jQuery
            'democlass' => $this->shortcode_class
        );

        wp_localize_script( $this->shortcode_class, 'AjaxDemo', $data );
    }
}

jquery-ajax-demo.js

jQuery( function( $ ) {

    var buttonClass = AjaxDemo.democlass + 'Button',
        // insert AJAX result into the shortcode element
        updateDemo = function( response ){          
            $( '.' + AjaxDemo.democlass ).find( 'b' ).html( response );
        },
        // fetch AJAX data
        loadDemo = function() {
            $.post( AjaxDemo.ajaxUrl, { action: AjaxDemo.action }, updateDemo );
        };

    // add an update button
    $( '.' + AjaxDemo.democlass )
        .append( ' <button class="' + buttonClass + '">New</button>' );

    // assign the clock handler to the button
    $( '.' + buttonClass ).click( loadDemo );
});

Resultado en una publicación de blog:

ingrese la descripción de la imagen aquí

fuxia
fuente
+1 y gracias por lo anterior. Parece que definitivamente vale la pena leerlo. Creo que obtengo la mayor parte de lo que está sucediendo arriba, las páginas del códice están en mi agenda de lectura y probablemente verifique la fuente de todos modos. Aún así, permítanme disparar 2 preguntas de seguimiento rápido: puedo ver que usar la API será una ventaja para mí, el programador (limpio, conciso, vinculado al entorno (es decir, WP)). 1. ¿Qué tal el rendimiento? ¿Alguna ganancia o desventaja en comparación con el uso de jQuery directamente (soy consciente de la brecha de rendimiento con js directo)? 2. ¿Es tan flexible? Es decir, ¿puedo hacer uso de las mismas devoluciones de llamada y argumentos?
Johannes Pille
@JohannesPille El rendimiento no es perfecto . Por otro lado, actúas ahora en un entorno predecible, otros complementos pueden reutilizar tu código (ganchos, funciones) y no tienes que saber dónde está instalado WP (el directorio / URL del complemento podría estar en un lugar diferente servidor). Además de eso, es lo mismo que una solución personalizada.
fuxia
Cuando @toscho He activado el define('WP_DEBUG', true);en mi wp-config.php esta solución genera un error: Strict Standards: call_user_func_array() expects parameter 1 to be a valid callback, non-static method Ajax_Shortcode_Demo::get_instance() should not be called statically in /var/www/.../public_html/wp-includes/plugin.php on line 496. ¿Es esto crítico? Lo cambié un poco: wordpress.stackexchange.com/q/196332/25187
Iurie Malai
1
@Iurie Sí, ese método debe declararse como static. Hice una edición en mi publicación para eso. Gracias por la noticia. Ya no escribiría el código así. :)
fuxia