Creación de un complemento de WordPress que utiliza API de servicio, "De la sopa a las nueces"

Publicado: 2022-03-10
Resumen rápido ↬ Un número cada vez mayor de API disponibles públicamente brindan servicios poderosos para expandir la funcionalidad de nuestras aplicaciones. WordPress es un CMS increíblemente dinámico y flexible que impulsa todo, desde pequeños blogs personales hasta los principales sitios web de comercio electrónico y todo lo demás. Parte de lo que hace que WordPress sea tan versátil es su poderoso sistema de complementos , que hace que sea increíblemente fácil agregar funcionalidad. Explicaremos cómo creé GitHub Pipeline, un complemento que le permite mostrar datos de la API de GitHub en páginas de WordPress usando códigos abreviados. Daré ejemplos específicos y fragmentos de código, pero considere la técnica descrita aquí como un modelo sobre cómo consumir cualquier API de servicio con un complemento. Comenzaremos desde el principio, pero se asume un grado de familiaridad con WordPress y el desarrollo de complementos, y no dedicaremos tiempo a temas para principiantes, como instalar WordPress o Composer.

Un número cada vez mayor de API disponibles públicamente brindan servicios poderosos para expandir la funcionalidad de nuestras aplicaciones. WordPress es un CMS increíblemente dinámico y flexible que impulsa todo, desde pequeños blogs personales hasta los principales sitios web de comercio electrónico y todo lo demás. Parte de lo que hace que WordPress sea tan versátil es su poderoso sistema de complementos , que hace que sea increíblemente fácil agregar funcionalidad.

Explicaremos cómo creé GitHub Pipeline, un complemento que le permite mostrar datos de la API de GitHub en páginas de WordPress usando códigos abreviados. Daré ejemplos específicos y fragmentos de código, pero considere la técnica descrita aquí como un modelo sobre cómo consumir cualquier API de servicio con un complemento.

Lectura adicional en SmashingMag:

  • WordPress Essentials: Cómo crear un complemento de WordPress
  • Cómo implementar complementos de WordPress con GitHub usando transitorios
  • Tres enfoques para agregar campos configurables a su complemento

Comenzaremos desde el principio, pero se asume un grado de familiaridad con WordPress y el desarrollo de complementos, y no dedicaremos tiempo a temas para principiantes, como instalar WordPress o Composer.

¡Más después del salto! Continúe leyendo a continuación ↓

Necesitarás:

  • un entorno PHP, con una instalación nueva de WordPress;
  • una cuenta de GitHub (u otro proveedor de API si quieres improvisar);
  • Compositor (recomendado).

Elegir una API

El primer paso para escribir este tipo de complemento es elegir una API. En este tutorial, usaremos la API de GitHub. Si está pensando en usar otra API, considere algunos factores importantes que pueden afectar la recompensa de su proyecto.

Una de las primeras cosas que observo es la minuciosidad y la calidad de la documentación de la API. Si es escaso o está desactualizado, prepárese para dedicar tiempo a revisar el código fuente para responder sus propias preguntas. Además, considere qué tan madura es la API y qué tan responsablemente el proveedor la ha versionado. No hay nada peor que invertir tiempo en crear algo grandioso, solo para que se rompa debido a los cambios en la API ascendente. Busque un sistema de control de versiones en las URL de punto final.

 // good https://api.stable.com/v1/user/ // danger https://api.dodgy.com/user/

A riesgo de decir lo obvio, la programación con una API de terceros implica una relación de confianza, y no todas las API se crean de la misma manera.

Compras para una biblioteca

Las marcas establecidas a menudo distribuyen bibliotecas o SDK para facilitar el trabajo con su API. Si este no es el caso, recuerda investigar si alguien más ha escrito una biblioteca antes de ir y reinventar la rueda. Google y GitHub son dos excelentes lugares para comenzar su investigación. La cantidad de estrellas o bifurcaciones en GitHub es una buena indicación de cuán efectiva es una biblioteca. La antigüedad de las confirmaciones más recientes y/o la cantidad de problemas abiertos son una indicación de qué tan activamente se mantiene. En mi caso, tuve la suerte de encontrar la hermosa API PHP GitHub, de KNP Labs.

Escribiendo el tuyo con Guzzle

Si no hay una biblioteca satisfactoria para su proveedor, no debe comenzar de cero porque herramientas como Guzzle facilitan mucho el trabajo con solicitudes HTTP. Guzzle envuelve la biblioteca cURL de PHP y elimina muchos de los dolores de cabeza asociados típicamente con la configuración y realización de solicitudes manualmente. Incluso si está haciendo solo una o dos solicitudes, aún así recomendaría usarlo porque hará que su código sea más sólido, e instalarlo con Composer es pan comido.

Configuración del complemento

Comenzaremos con el esqueleto básico de un complemento mínimo de WordPress, un directorio con dos archivos. Es importante elegir un nombre de carpeta descriptivo y único para evitar conflictos con otros complementos. Si el nombre de su complemento es algo genérico, considere agregar un prefijo único.

 github-api/ readme.txt github-api.php

El readme.txt contiene los metadatos de su complemento que aparecerán en wordpress.org si decide publicarlo allí. Lea sobre la publicación de complementos de WordPress en la documentación, o consulte la muestra completa readme.txt.

El archivo PHP también contiene algunos metadatos en el encabezado que se usarán para mostrar información sobre su complemento en el tablero. Comience con un encabezado que se vea así:

 <?php /** Plugin Name: GitHub API description: >- Add GitHub project information using shortcode Version: 1.0 Author: Your Name License: GPLv2 or later Text Domain: github-api */

Por razones de seguridad, también es una buena idea denegar el acceso directo al archivo, así:

 defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

En este punto, el complemento no hará nada, pero cuando copie los archivos en wp-content/plugins , debería aparecer en la lista de complementos y debería poder activarlo.

Página de complementos del tablero de WordPress
Página de complementos del tablero de WordPress. (Ver versión grande)

A continuación, querremos incluir la biblioteca que manejará las solicitudes de la API. En el siguiente ejemplo de código, incluimos la API PHP GitHub de KNP Labs, pero puede incluir cualquier dependencia al sustituir knplabs/github-api con el paquete que está utilizando.

 $ cd wp-content/plugins/github-api $ composer require knplabs/github-api

Ahora, su estructura de archivos debería verse de la siguiente manera:

 github-api/ composer.json composer.lock github-api.php readme.txt vendor/

Ahora que los archivos están en su lugar, necesitamos solicitar el archivo vendor/autoload.php que se creó cuando lo instaló.

 require_once 'vendor/autoload.php';

Si todo se configuró correctamente, debería poder crear una instancia de una clase de la biblioteca sin que se produzca un error fatal.

 $testing = new \Github\Client();

Pero esta es solo una forma rápida de probar que sus dependencias están disponibles. Recomiendo definir una nueva clase que amplíe la biblioteca.

 class MyGithub extends \Github\Client {};

Esto no es crítico, pero hace que su código sea más flexible en un par de formas. La forma más obvia es que puede modificar o agregar nuevas funciones si lo necesita. Pero también facilita la vida si alguna vez desea cambiar de biblioteca en el futuro, porque tendrá que realizar los cambios en un solo lugar, en lugar de en todo el código.

Códigos cortos de WordPress

Ahora es el momento de configurar nuestro primer código abreviado, que es un fragmento especial que le permite generar contenido colocándolo en una publicación o página. Si eres completamente nuevo en los códigos abreviados o quieres una comprensión más profunda de cómo funcionan, consulta la documentación oficial de códigos abreviados. En mi ejemplo, mostraré una lista de problemas de GitHub donde el autor coloque este código abreviado: [github_issues]

Como hicimos antes, comencemos con un ejemplo mínimo para asegurarnos de que el código abreviado esté registrado correctamente antes de trabajar en la llamada a la API.

 function github_issues_func( $atts ) { return "Hello world!"; } add_shortcode( "github_issues", "github_issues_func" );

Ahora, si publica una página que contiene [github_issues] , debería ver "Hola mundo" cuando visite la página. Ahora que esto funciona, configuremos la llamada a la API.

En la sección anterior, configuramos la carga automática para nuestra biblioteca de GitHub y definimos nuestra propia clase para ampliarla. Ahora, podemos comenzar a usarlo para hacer llamadas a la API. Entonces, lo agregaremos a la función de devolución de llamada del shortcode. Como prueba de concepto, busquemos todos los problemas en el repositorio de canalización de GitHub. La documentación para este método está disponible.

 function github_issues_func( $atts ) { // Instantiate our class $gh = new MyGithub(); // Make the API call to get issues, passing in the GitHub owner and repository $issues = $gh->api('issue')->all('TransitScreen', 'wp-github-pipeline'); // Handle the case when there are no issues if ( empty($issues) ) return "<strong>" . __("No issues to show", 'githup-api') . "</strong>"; // We're going to return a string. First, we open a list. $return = "<ul>"; // Loop over the returned issues foreach( $issues as $issue ) { // Add a list item for each issue to the string // (Feel free to get fancier here) // Maybe make each one a link to the issue issuing $issue['url] ) $return .= "<li>{$issue['title']}</li>"; } // Don't forget to close the list $return .= "</ul>"; return $return; } add_shortcode( 'github_issues', 'github_issues_func' );

Ahora debería poder ver la misma página que usamos anteriormente para probar el código abreviado, y esta vez debería ver una lista desordenada de problemas. ¡Pero espera! Antes de continuar, hagamos una optimización para que nuestro código sea más fácil de probar. (¡Estás probando, verdad?!) En lugar de usar new en nuestra función para instanciar la clase de la biblioteca de GitHub, pasémoslo como un parámetro e instanciamos solo si es necesario.

 function github_issues_func( $atts, $gh=null ) { // Conditionally instantiate our class $gh = ( $gh ) ? $gh : new MyGithub(); …

Esta modificación se asemeja al patrón de inyección de dependencia, lo que hace que nuestra función sea mucho más fácil de probar. Las pruebas unitarias de WordPress están más allá del alcance de este tutorial, pero es fácil ver cómo esta nueva versión nos facilita pasar datos API "falsos" a la función para que las afirmaciones de nuestra prueba sepan exactamente qué esperar.

Lo que hemos hecho hasta ahora está muy bien para un proyecto interno que solo se usará en un único repositorio, pero sería mucho más útil si los usuarios pudieran configurar de qué repositorio buscar problemas. Configuremos eso.

Primero, usaremos un enlace de WordPress para registrar nuestra nueva página de configuración, y la función de devolución de llamada definirá las etiquetas y los títulos del menú. Usaremos el mismo enfoque incremental para que funcione.

 // Register the menu. add_action( "admin_menu", "gh_plugin_menu_func" ); function gh_plugin_menu_func() { add_submenu_page( "options-general.php", // Which menu parent "GitHub", // Page title "GitHub", // Menu title "manage_options", // Minimum capability (manage_options is an easy way to target administrators) "github", // Menu slug "gh_plugin_options" // Callback that prints the markup ); } // Print the markup for the page function gh_plugin_options() { if ( !current_user_can( "manage_options" ) ) { wp_die( __( "You do not have sufficient permissions to access this page." ) ); } echo "Hello world!"; }

Ahora debería poder iniciar sesión en el tablero y ver el nuevo menú de GitHub en "Configuración" y luego hacer clic para ver una página de configuración en blanco con "¡Hola mundo!"

Página vacía de complementos de WordPress
Página vacía de complementos de WordPress. (Ver versión grande)

A continuación, reemplazaremos Hello word con el marcado real del formulario. Te daré un ejemplo de barebones que puedes peinar tan poco o tanto como quieras.

 ?> <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="update_github_settings" /> <h3><?php _e("GitHub Repository Info", "github-api"); ?></h3> <p> <label><?php _e("GitHub Organization:", "github-api"); ?></label> <input class="" type="text" name="gh_org" value="<?php echo get_option('gh_org'); ?>" /> </p> <p> <label><?php _e("GitHub repository (slug):", "github-api"); ?></label> <input class="" type="text" name="gh_repo" value="<?php echo get_option('gh_repo'); ?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Save", "github-api"); ?>" /> </form> <?php

Las clases de CSS que he incluido coinciden con las utilizadas por el núcleo de WordPress, que es una buena manera de hacer que su página de opciones personalizadas se vea decente sin esfuerzo adicional.

Página de configuración personalizada de WordPress con estilos básicos
Página de configuración personalizada de WordPress con estilos básicos. (Ver versión grande)

Hay dos cosas importantes que debe comprender acerca de este formulario. Primero, el punto final al que se envía debe ser /wp-admin/admin-post.php . Puede codificar esta ruta, pero si el sitio web está instalado en un subdirectorio, no funcionará. Entonces, usamos el admin_url() para crearlo dinámicamente.

En segundo lugar, observe la entrada oculta con el nombre action . Este campo es cómo WordPress sabe qué hacer con la solicitud de publicación que se envía mediante el formulario. El value corresponde al nombre del gancho de acción que usaremos para establecer la función de devolución de llamada. El nombre de la acción a la que nos conectaremos será este valor, con el prefijo admin post . Entonces, en nuestro caso, necesitamos agregar esto:

 add_action( 'admin_post_update_github_settings', 'github_handle_save' );

Considero que este es uno de los aspectos más extravagantes y menos intuitivos de la creación de menús de administración personalizados, pero una vez que te acostumbras, es bastante sencillo. Como habrás adivinado, el segundo parámetro de add_action() es el nombre de nuestra función de devolución de llamada que realmente guardará los valores en la base de datos.

 function github_handle_save() { // Get the options that were sent $org = (!empty($_POST["gh_org"])) ? $_POST["gh_org"] : NULL; $repo = (!empty($_POST["gh_repo"])) ? $_POST["gh_repo"] : NULL; // Validation would go here // Update the values update_option( "gh_repo", $repo, TRUE ); update_option("gh_org", $org, TRUE); // Redirect back to settings page // The ?page=github corresponds to the "slug" // set in the fourth parameter of add_submenu_page() above. $redirect_url = get_bloginfo("url") . "/wp-admin/options-general.php?page=github&status=success"; header("Location: ".$redirect_url); exit; }

El ejemplo también es bastante mínimo. En un entorno de producción, probablemente desee agregar alguna validación. Además, en este ejemplo, devolvemos automáticamente status=success a la URL. El marcado del formulario aún no lo usa. Para mostrar condicionalmente un mensaje de éxito, agregue algo como lo siguiente arriba del formulario en gh_plugin_options() :

 if ( isset($_GET['status']) && $_GET['status']=='success') { ?> <div class="updated notice is-dismissible"> <p><?php _e("Settings updated!", "github-api"); ?></p> <button type="button" class="notice-dismiss"> <span class="screen-reader-text"><?php _e("Dismiss this notice.", "github-api"); ?></span> </button> </div> <?php }

Si agrega validación, use este mismo patrón para devolver diferentes estados y mensajes para que el usuario sepa si falló el envío del formulario y por qué.

Ahora debería poder guardar nuevos valores de propietario y repositorio. Pruébelo ingresando el propietario y el repositorio de cualquier proyecto público en GitHub. El último paso es volver a la función de devolución de llamada de shortcode github_issues_func() y reemplazar los valores de propietario y repositorio codificados.

 … $issues = $gh->api("issue")->all(get_option("gh_org"), get_option("gh_repo")); …

Una vez que esto esté en su lugar, vuelva a visitar la página donde agregó el código abreviado. Ahora debería ver los problemas de cualquier proyecto que establezca.

Ronda de bonificación: Autenticación OAuth 2.0

El enfoque que usamos anteriormente funciona muy bien para repositorios públicos, pero ¿qué pasa si queremos usar este complemento con un repositorio privado que requiere autenticación? La API de GitHub se autentica mediante el protocolo OAuth 2.0, que es lo que encontrará al trabajar con las API más populares en estos días. El flujo de trabajo básico de OAuth 2.0 es así:

  1. Registra una aplicación (a veces llamada "cliente") con el proveedor y recibe una identificación única y una clave secreta.
  2. Su aplicación realiza una solicitud al punto final de autenticación del proveedor, pasando las credenciales anteriores, así como una URL de redireccionamiento que el proveedor utiliza para redirigir la solicitud a un punto final de su aplicación.
  3. A continuación, se solicita al usuario que acepte su solicitud de acceso. Si lo hacen, el proveedor usa la URL que envió con la solicitud para redirigir al usuario a su aplicación, junto con un código temporal.
  4. Su aplicación captura este código y luego realiza una segunda solicitud, devolviendo este código al proveedor. El proveedor responde con un token de acceso, que su aplicación luego usa para autenticarse con el proveedor.

Comenzaremos registrando una aplicación con GitHub. Al igual que con las API más populares, esta sección se encuentra en el área de desarrolladores del sitio web.

GitHub nueva página de registro de aplicaciones
Nueva página de registro de aplicaciones de GitHub. (Ver versión grande)

Lo más importante aquí es la URL de devolución de llamada de autorización. La URL de redirección pasada en el paso tres debe coincidir con lo que ingresaste aquí o incluirla. Entonces, durante el desarrollo, generalmente ingreso a la página de inicio del sitio web. De esta manera, mi aplicación puede redirigir a cualquier ruta.

A continuación, agregaremos un segundo formulario a nuestra página de configuración, en gh_plugin_options() , para enviar el ID y el secreto del cliente de la aplicación.

 <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="oauth_submit" /> <h3>Oauth 2.0</h3> <p> <label><?php _e("GitHub Application Client ID:", "github-api"); ?></label> <input class="" type="text" name="client_id" value="<?php echo get_option('client_id')?>" /> </p> <p> <label><?php _e("GitHub Application Client Secret:", "github-api"); ?></label> <input class="" type="password" name="client_secret" value="<?php echo get_option('client_secret')?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Authorize", "github-api"); ?>" /> </form>

Para hacer la vida más fácil, usaré el proveedor de GitHub para el cliente OAuth 2.0 de The League of Extraordinary Packages. Entonces, primero, usemos Composer nuevamente para agregar la dependencia:

 composer require league/oauth2-github

Tenga en cuenta que la biblioteca de GitHub de KNP Labs también tiene compatibilidad con OAuth 2.0 integrada. Entonces, en mi ejemplo específico, esto es algo redundante. Pero quería presentar esta biblioteca porque pertenece a un conjunto de bibliotecas de cliente OAuth 2.0 específicas del proveedor que amplían el mismo marco mantenido por la poderosa Liga de paquetes extraordinarios. Puede ver una lista completa de proveedores admitidos o leer instrucciones sobre cómo ampliar el marco para admitir un nuevo proveedor.

Vamos a crear una función para solicitar y guardar el token cuando el usuario envíe el formulario. Debido a que GitHub es uno de los proveedores que ya son compatibles, puedo copiar el ejemplo en su documentación con solo un par de modificaciones.

 function handle_oauth() { // If the form was just submitted, save the values // (Step 1 above) if ( isset($_POST["client_id"]) && isset($_POST["client_secret"]) ) { update_option( "client_id", $_POST["client_id"], TRUE ); update_option("client_secret", $_POST["client_secret"], TRUE); } // Get the saved application info $client_id = get_option("client_id"); $client_secret = get_option("client_secret"); if ($client_id && $client_secret) { $provider = new League\OAuth2\Client\Provider\Github([ "clientId" => $client_id, "clientSecret" => $client_secret, "redirectUri" => admin_url("options-general.php?page=github"), ]); } // If this is a form submission, start the workflow // (Step 2) if (!isset($_GET["code"]) && $_SERVER["REQUEST_METHOD"] === "POST") { // If we don't have an authorization code, then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION["oauth2state"] = $provider->getState(); header("Location: ".$authUrl); exit; // Check given state against previously stored one to mitigate CSRF attack // (Step 3 just happened and the user was redirected back) } elseif (empty($_GET["state"]) || ($_GET["state"] !== $_SESSION["oauth2state"])) { unset($_SESSION["oauth2state"]); exit("Invalid state"); } else { // Try to get an access token (using the authorization code grant) // (Step 4) $token = $provider->getAccessToken("authorization_code", [ "code" => $_GET["code"] ]); // Save the token for future use update_option( "github_token", $token->getToken(), TRUE ); } }

Y, tal como hicimos con el otro formulario, necesitamos agregar el gancho de acción para que se llame a la función cuando se guarde el formulario.

 add_action( "admin_post_oauth_submit", "handle_oauth" );

Guardar el formulario ahora debería enviarlo a la página de autorización del proveedor de la API. Después de la autorización, su aplicación puede usar el token guardado para solicitar datos de repositorios privados a los que tiene acceso. La biblioteca que estoy usando de KNP Labs tiene un método útil para esto.

 $gh = new MyGithub(); $gh->authenticate( get_option("github_token"), NULL, Github\Client::AUTH_HTTP_TOKEN);

Las bibliotecas difieren en cómo manejan la autenticación con precisión, pero de una forma u otra, pasará el token, que luego realizará solicitudes autenticadas en nombre de un usuario.

Conclusión

Hemos cubierto mucho terreno y he tratado de mantener los ejemplos lo mínimo posible para que el flujo de trabajo general permanezca claro. El código fuente completo de este tutorial está disponible. Espero que ahora tenga una comprensión clara de las piezas móviles involucradas en la creación de un complemento de WordPress que consume API de servicios de terceros, y espero que esté inspirado para escribir su propio complemento de API de WordPress.

Notas finales

  • Códice de WordPress (documentación)
  • API de GitHub (documentación)
  • OAuth 2.0 (documentación)
  • Compositor (documentación)
  • La Liga de los Paquetes Extraordinarios
  • Documentación Guzzle