Um modo interessante de estender o alcance e as funcionalidades de um plugin é através de requisições remotas. O site local solicita informações de um servidor e a partir do retorno realiza os devidos processamentos. Para implementar esse recurso no WordPress, você utilizará a HTTP API para as requisições e a Transient API para fazer um pequeno cache dos dados obtidos.

A proposta consiste em conectar o WordPress com um servidor externo (outro site, serviço web) de acordo com opções definidas via Dashboard. Para evitar muitas requisições externas e eventualmente atrasar na entrega das informações ao visitante, o conteúdo recuperado será armazenado em banco de dados durante um período de tempo a ser definido; isto é, a atualização deverá ocorrer em intervalos programados.

O serviço web escolhido para exemplificar e colocar em prática os conceitos abordados pelo plugin será uma requisição REST para recuperar informações do site wpdeveloper.com.br. O plugin deverá realizar uma pesquisa de termos entre as publicações do site, com um limite definido de resultados e então exibí-los em tela. As opções a constar na página administrativa serão os termos de busca e a quantidade de Posts a serem recuperados.

Objetivos do plugin

  • Consultar dados de um serviço externo
  • Possibilitar a definição de opções através de páginas próprias no painel de controle
  • Otimizar a quantidade de requisições externas
  • Trabalhar com opções por um dado intervalo de tempo

Páginas administrativas

Para inserir uma página administrativa em seu plugin é preciso acrescentar um item ao menu do Dashboard que permita seu fácil acesso. Esse item é adicionado através da action ‘admin_menu’.

class KDM_Get_Posts
{
    static $prefix = 'kdm_';
    function init()
    {
        add_action( 'admin_menu', array( 'KDM_Get_Posts', 'menu' ) );
    }
    function menu()
    {
        add_menu_page( 'Dados Remotos', 'Dados Remotos', 'administrator', 'grp_menu', array( 'KDM_Get_Posts', 'options_form' ) );
    }
    function options_form(){}
}
Página administrativa com opções personalizadas para o plugin de integração com sites externos

Página administrativa com opções personalizadas para o plugin de integração com sites externos

Repare que o uso de add_menu_page incluiu um item de nível principal ao menu do Dashboard. Supondo que o plugin em desenvolvimento tivesse a necessidade de novas páginas administrativas, o WordPress permite que essas páginas adicionais sejam agrupadas dentro de um item principal no formato de um submenu.

function menu() {
    add_menu_page( 'Dados remotos', 'Dados remotos', self::$role, 'grp_menu', array( 'KDM_Get_Posts', 'options_form' ) );
    add_submenu_page( 'grp_menu', 'Configurações do Plugin', 'Configurações', 'administrator', 'grp_submenu', array( 'KDM_Get_Posts', 'options_details_form' ) );
}

Além de incorporar uma página como subitem do item anteriormente criado, o WordPress permite adicionar itens aos tópicos já existentes no menu. Para realizar esse tipo de inserção informe no parâmetro $parent_slug (o primeiro de add_submenu_page) a qual página deseja submeter o submenu, por exemplo ‘edit.php’ para Posts, ‘options-general.php’ para Configurações e assim por diante. Outra maneira de realizar tal inserção é através das funções específicas:

  • add_dashboard_page
  • add_posts_page
  • add_media_page
  • add_links_page
  • add_pages_page
  • add_comments_page
  • add_theme_page
  • add_plugins_page
  • add_users_page
  • add_management_page
  • add_options_page

Tabela de Opções

Todas as configurações do WordPress, desde o modo como um administrador pode personalizar o funcionamento do sistema até informações mais simples, como o nome e descrição do site, são dados armazenados na tabela wp_options do banco de dados. Essa tabela possui essencialmente o nome e o valor de um dado qualquer que se deseja armazenar e será nela que você armazenará as opções do plugin de recuperação de Posts do Facebook.

Com intuito de acessar e manipular as opções, o WordPress disponibiliza a Options API formada basicamente por quatro funções essenciais de manipulação dos dados: add_optionupdate_optiondelete_option e get_option. As funções são, pela ordem, responsáveis por adicionar, alterar, excluir e recuperar o valor armazenado.

As funções get_option e delete_option recebem como parâmetro apenas o nome da opção, já add_option e update_option devem ser usadas com o nome seguido pelo valor da opção. Atualize o método options_form da classe de modo que ele exiba um formulário capaz de recuperar os valores do banco de dados utilizando as função da API em estudo.

function options_form()
{
    $fields = self::options_save();
    if ( !$fields ) {
        $fields = array(
            'search'    => get_option( self::$prefix . 'search' ),
            'count'     => get_option( self::$prefix . 'count' )
        );
    }
    ?>
    <div>
        <span></span>
        <?php screen_icon( 'options-general' ); ?>
        <h2>Configurações do Plugin</h2>
        <form method="post">
            <table>
                <tr>
                    <th><label for="gti-search">Termos de busca</label></th>
                    <td><input type="text" size="32" name="<?php echo self::$prefix; ?>search" value="<?php echo $fields[ 'search' ]; ?>" id="gti-search" /></td>
                </tr>
                <tr>
                    <th><label for="gti-count">Qtd de posts</label></th>
                    <td><input type="text" size="4" name="<?php echo self::$prefix; ?>count" value="<?php echo $fields[ 'count' ]; ?>" id="gti-count" /></td>
                </tr>
            </table>
            <p><input name="submit" type="submit" value="Salvar" /></p>
        </form>
    </div>
    <?php
}

A estrutura HTML e o uso das classes acima propostos servem para manter o padrão de formatação do WordPress. Procure separar em funções distintas a exibição do form e a submissão de seu conteúdo, da mesma maneira que foi estabelecido ao referenciar o método options_save; seu código ficará mais compreensível e fácil de ser manipulado ou atualizado.

function options_save()
{
    $fields = array(
        'search'   => '',
        'count'     => ''
    );
    foreach ( $fields as $f => $value ) {
        $field = self::$prefix . $f;

        $new = false;
        $old = get_option( $field );

        if ( isset( $_POST[ $field ] ) )
            $new = sanitize_text_field( $_POST[ $field ] );

        $fields[ $f ] = $new;
        if ( ( $new !== $old ) && $new )
            update_option( $field, $new );
        else if ( !$new && $old )
            delete_option( $field );
    }
    echo '<div><p>Configurações atualizadas com sucesso!</p></div>';
    return $fields;
}

Dados Remotos

Existem diferentes maneiras de estabelecer uma conexão com outros sites no PHP; utilizar a biblioteca CURL é um exemplo de como essa comunicação pode ser realizada. Esse tipo de conexão requer que o servidor esteja apto a fazê-la (no caso da utilização da CURL é preciso que a biblioteca esteja disponível). Em aplicações de teste localhost é possível até receber dados de modo simplificado com file_get_contents.

Fique atento ao recebimento das informações por parte do serviço escolhido, faça verificações a fim de confirmar se os dados recebidos são os esperados e mantenha assim o correto funcionamento do plugin. No projeto atual você está solicitando informações ao Facebook; pode ocorrer da rede social não fornecer mais a API utilizada ou então mudar o modelo de integração. Por essa razão é importante criar condições capazes de manter o recurso ativo ou ao menos de não comprometer o restante do site.

HTTP API

Referente a consultas externas, no WordPress existe a HTTP API dedicada em operar requisições desse mesmo gênero; cuja manipulação das informações ocorre através da classe WP_Http.

if ( !class_exists( 'WP_Http' ) )
    include_once( ABSPATH . WPINC. '/class-http.php' );

$url = 'https://wpdeveloper.com.br/wp-json.php?s=plugins&limit=3';
$request = new WP_Http();
$result = $request->request( $url, array( 'method' => 'GET', 'sslverify' => false ) );
$json = json_decode( $result[ 'body' ] );

Todo esse processo de verificação da classe e definição de nova instância não é necessário realizar, pois a API oferece funções próprias que já se utilizam de uma instância existente.

$url = 'https://wpdeveloper.com.br/wp-json.php?s=plugins&limit=3';
$data = wp_remote_get( $url, array( 'sslverify' => false ) );
$data = json_decode( $data[ 'body' ] );

Além da requisição via GET, existem os métodos POST e HEAD do HTTP; eles são respectivamente acessados com wp_remote_post e wp_remote_head. Você pode optar também por wp_remote_request e definir o método através do segundo parâmetro da função (por padrão o método é GET).

wp_remote_get( $url, array( 'method' => 'POST' ) );

O segundo parâmetro dessas funções tratam especificamente de configurar a conexão externa pretendida. Observe no exemplo que a verificação de SSL foi removida. Conheça os demais argumentos da função.

Transient API

Um outro ponto importante a observar em conexões externas é a performance, o tempo de recuperação e exibição dos resultados pretendidos. Imagine que a cada novo visitante seja realizada uma nova consulta ao outro site, isso traria um atraso desnecessário na exibição dos resultados para todos os usuários já que em boa parte das requisições as respostas seriam as mesmas.

Uma idéia para sanar esse problema é armazenar a informação obtida em banco de dados por um determinado tempo. Essa técnica pode ser aplicada manualmente com a API de opções, sessões, cookies ou então você tem a possibilidade de empregar a Transient API. Trata-se em essência de opções com um tempo de vida útil.

function get_posts( $search, $count=5 )
{
    $posts = get_transient( self::$prefix . 'posts' );
    if ( !$posts ) {
        $url = "https://wpdeveloper.com.br/wp-json.php?s={$search}&limit={$count}";
        $data = wp_remote_get( $url, array( 'sslverify' => false ) );
        $data = json_decode( $data[ 'body' ] );
        if ( is_array( $data->posts ) && count( $data->posts ) ) {
            $posts = array();
            foreach ( $data->posts as $post ) {
                $item = sprintf( '%s', $post->permalink, $post->title );
                array_push( $posts, $item );
            }
            set_transient( self::$prefix . 'posts', $posts, 60*60 );
        }
    }
    return $posts;
}

Usando get_transient você recupera o valor armazenado do banco de dados, quando for a primeira requisição nenhum valor estará armazenado; portanto a requisição HTTP será feita e o resultado obtido tratado e salvo com set_transient. Essa função recebe o nome da opção temporária, seu valor e o tempo de expiração, validade em segundos; isto é, de acordo com o exemplo logo após 1 hora a opção deixará de existir e o processo todo se repete.

Integração do conteúdo

O fechamento do plugin será feito seguindo o mesmo modelo de integração empregado no plugin do Slider; para isso crie uma Template Tag que recupere o valor das opções e com base nelas faça a requisição e imprima as informações em tela.

function gti_posts()
{
    KDM_Get_Posts::show_posts();
}
// em seguida, dentro da classe
function show_posts()
{
    $search = get_option( self::$prefix . 'search' );
    $count = get_option( self::$prefix . 'count' );
    $posts = self::get_posts( $search, $count );

    echo '<div>';
    echo '<h2>Publicações Remotas</h2>';

    if ( is_array( $posts ) && count( $posts ) ) {
        echo '<ul>';
        foreach( $posts as $t )
            echo "<li>{$t}</li>";

        echo '</ul>';
    } else {
        echo '<p>Nenhum post foi localizado...</p>';
    }
    echo '</div>';
}

Atente ao fato que os textos já foram tratados uma única vez no momento em que a requisição foi feita ao servidor externo e os dados armazenados já estão formatados, prontos para uso. Se esse tratamento não tivesse sido feito, novamente a questão de que o processo teria que ser feito para todo usuário. Também repare que mesmo recuperando a informação do banco é feita uma pequena verificação do conteúdo, dessa forma você está identificando se os dados são confiáveis e podem ser exibidos.

Para não esquecer

  • Permitir a configuração de opções no plugin
  • Recuperar dados de fontes externas
  • Opções temporárias para diminuir requisições e aperfeiçoar o uso do servidor