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
- Añadir pruebas.
- Gestionar errores.
- Gestionar dependencias con Symfony Dependency Injection.
Referencias
- Repositorio público de Trello CLI
- Trello
- ThoughtWorks
- Docker
- Framework Symfony
- Composer
- Symfony Console
- Comandos de consola
- Asistentes de consola
- Asistente de preguntas
- Asistente de barra de progreso
- Symfony Http Client
- Symfony Dot Env
- Symfony Dom Crawler
- Symfony Dependency Injection
- PHP_CodeSniffer
- PHP Coding Standards Fixer
- PHP Mess Detector
- PHPStan
- Principio de responsabilidad única
- Clausuras cortas en PHP