Todos los artículos

Interfaz de línea de comandos para Trello

Usando componentes de Symfony para crear automáticamente tarjetas de Trello desde un sitio web.

Trello es una excelente herramienta para organizar nuestro trabajo. Después de perder mi empleo por la COVID-19, me ha servido mucho para organizar mi búsqueda de trabajo, la educación en casa de mis hijos y mi preparación profesional.

Leer sobre desarrollo de software forma parte de mi rutina diaria. Una de mis fuentes favoritas es ThoughtWorks. Sus artículos suelen contener enlaces útiles para profundizar. Normalmente copio algunos de los más interesantes y creo una tarjeta de Trello con una lista de verificación. Era un proceso muy repetitivo, así que creé una interfaz de línea de comandos para Trello que extrae enlaces de cualquier página web y crea una tarjeta con una lista cuyos elementos son esos enlaces. Publiqué el código aquí: https://github.com/WebCu/trello-cli.

Entorno de desarrollo

Docker es mi opción predeterminada para crear entornos de desarrollo. Ya tengo una configuración de PHP que incluye xDebug, Composer, Git y otras extensiones. Puedes verla aquí: https://github.com/WebCu/trello-cli/blob/master/.docker/Dockerfile.

Herramientas de desarrollo

Llevo tiempo trabajando con el framework Symfony. Es muy completo y una de sus fortalezas es la calidad de sus componentes. Para esta prueba de concepto decidí combinar algunos sin usar el framework completo.

Composer

Composer es la herramienta habitual para gestionar dependencias en PHP.

Para especificar desde dónde se cargarán las clases y qué espacio de nombres usaremos, añadimos la opción autoload a composer.json. Por ejemplo:

"autoload": {
    "psr-4": {
        "Trello\\CLI\\": "src/"
    }
}

Debemos incluir el autocargador de Composer en nuestros archivos para poder utilizar las clases. Por ejemplo:

require_once __DIR__.'/vendor/autoload.php';

Symfony Console

Este proyecto es una herramienta de línea de comandos y ya existe un excelente componente para ayudarnos: Symfony Console. Es fácil de usar: creamos una aplicación, añadimos el comando que queremos gestionar y ejecutamos la aplicación. Por ejemplo:

$application = new Application();
$application->add(new CreateLinkCardCommand($trelloApi, $webCrawler));
$application->run();

Comandos

Los comandos se definen mediante clases que extienden Command. Los métodos principales son configure y execute; este último es obligatorio. La propiedad defaultName establece el nombre con el que invocamos el comando. Más información: Comandos de consola.

Invocamos nuestro comando así:

php index.php trello-cli:create-link-card

Asistentes

Symfony Console incluye asistentes muy útiles: Asistentes de consola. Usé el asistente de preguntas y el de barra de progreso.

Asistente de preguntas

El asistente de preguntas permite solicitar información al usuario. Está incluido en el conjunto de asistentes predeterminado, que obtenemos llamando a getHelperSet():

$helper = $this->getHelper("question");

Lo usamos para preguntar al usuario en qué tablero y lista quiere añadir la tarjeta, su nombre, de dónde extraer los enlaces, etc.

Asistente de barra de progreso

En comandos de larga duración puede ser útil mostrar información de progreso que se actualice durante la ejecución. El asistente de barra de progreso ofrece esta funcionalidad y muchas posibilidades de personalización.

Lo usamos para mostrar el progreso al crear los elementos de la lista de verificación.

Symfony Http Client

Para extraer información de una página web y comunicarnos con la API de Trello necesitamos hablar HTTP. Symfony Http Client nos ayuda.

Según su documentación, HttpClient es un cliente HTTP de bajo nivel compatible con los flujos de PHP y cURL. Proporciona utilidades para consumir API y admite operaciones síncronas y asíncronas.

Es fácil de usar. Primero creamos una instancia:

$httpClient = HttpClient::create();

Después empezamos a crear solicitudes. Por ejemplo:

$response = $httpClient->request(
    "POST",
    "https://api.trello.com/1/checklists",
    ["query" => array_merge($defaultQuery, ["idCard" => $idCard])]
);

$content = $response->toArray();

Symfony Dot Env

La forma de gestionar nuestras variables secretas es muy importante. Symfony Dot Env interpreta archivos .env para que sus variables de entorno sean accesibles mediante $_ENV o $_SERVER.

Es buena idea crear un archivo .env.dist que contenga solo los nombres de las variables necesarias y añadirlo al repositorio. Al mismo tiempo, añade .env a .gitignore para evitar publicar tus secretos.

Así lo usé en este proyecto:

$dotenv = new Dotenv();
$dotenv->load(__DIR__.'/.env');

$httpClient = HttpClient::create();

$trelloApi = new TrelloApi($httpClient, $_ENV['TRELLO_API_KEY'], $_ENV['TRELLO_API_TOKEN']);

Symfony Dom Crawler

Ya tenemos la consola preparada, el cliente HTTP funciona y los secretos están protegidos. ¿Cómo extraemos los enlaces? ¡Symfony Dom Crawler al rescate! Este componente facilita la navegación por el DOM de documentos HTML y XML.

Después de recibir la respuesta de la página de la que queremos extraer enlaces, creamos un crawler con su contenido:

$response = $this->httpClient->request(
    "GET",
    $url
);
$content = $response->getContent();
$crawler = new Crawler($content);

Ahora filtramos los enlaces y devolvemos un arreglo con ellos:

$links = $crawler->filter("a")->each(
    fn (Crawler $aCrawler) => [
        "text" => $aCrawler->text(),
        "href" => $aCrawler->attr("href")
    ]
);

Este componente es muy potente. Recomiendo consultar su documentación si necesitas realizar tareas más específicas.

Estándares de código, detectores de problemas y análisis estático

Es importante tener reglas predefinidas para evaluar la calidad y los estándares de nuestro código. Por suerte existen herramientas para ayudarnos. Estas son las que utilicé:

Decisiones de desarrollo

Al desarrollar una solución, grande o pequeña, tomamos decisiones continuamente. Es importante anotarlas para recordar qué pensábamos cuando volvamos a revisar el código.

¿Comillas simples o dobles?

Como uso mucho la interpolación de variables, prefiero las comillas dobles. Por ejemplo:

"body" => ["name" => "[{$text}]({$href})"],

Dependencias

Creé los servicios TrelloApi y WebCrawler para gestionar las interacciones con Trello y la extracción de páginas web, respectivamente, siguiendo el principio de responsabilidad única (SRP). Podríamos crearlos dentro del comando, pero es mejor pasarlos como dependencias para poder sustituirlos fácilmente al probar la aplicación o usar otra implementación. Una solución aún mejor sería pasar una interfaz al comando para que no dependa de una implementación específica.

public function __construct(TrelloApi $trelloApi, WebCrawler $webCrawler)
{
    $this->trelloApi = $trelloApi;
    $this->webCrawler = $webCrawler;

    parent::__construct();
}   

Funciones flecha

Esta funcionalidad apareció en PHP 7.4. Permite acceder directamente a variables del ámbito exterior, escribir menos código y otras ventajas. Más información en Clausuras cortas en PHP. Como todo, no es una solución mágica. Me gusta, pero puede dificultar las pruebas del código que contiene. Hay que evaluar cada caso antes de decidir usarla.

Pendiente

Referencias