6 pts
En esta primera práctica vamos a empezar a desarrollar una API de películas.
Nuestra estructura será la siguiente:
/api //Donde estará nuestro código fuente de la aplicación /routes api.php //Archivo donde definimos nuestras rutas /src App.php //Clase principal de nuestra aplicación helpers.php //Archivo con funciones útiles que serán accesibles desde toda nuestra aplicación /vendor //Librerías bajadas con Composer composer.json //Archivo de configuración de Composer composer.lock //Archivo con el registro exacto de las versiones de dependencia que se han instalado index.php //Archivo principal de nuestra API /html //Carpeta pública de nuestra API /api .htaccess //Archivo especial para configurar Apache index.php //Punto de entrada publico de nuestra API
¿Por qué crear dos carpetas separadas (html y api) con dos index.php? Cuando desplegamos nuestra aplicación en un servidor HTTP (como Apache), tenemos una carpeta pública (htdocs, html…) que es donde copiamos los archivos que queremos que sean accesibles desde el exterior. En nuestra aplicación, no queremos que la gente pueda acceder a nuestros archivos de código fuente o de configuración, con lo que una buena práctica es almacenarlos en una carpeta de un nivel anterior a la pública del servidor para que no sean accesibles y llamarlos desde nuestros archivos públicos cuando sea necesario.
De esta forma, a la hora de desplegar nuestra web tendremos que copiar la carpeta html en la carpeta pública del servidor HTTP que utilicemos y nuestra carpeta api en un nivel inferior para que no sea accesible.
En esta carpeta tendremos dos archivos: index.php y .htaccess.
La idea es que todas las peticiones que lleguen a nuestra API sean redirigidas al archivo index.php, así controlamos todo desde un mismo archivo. Para conseguir eso, utilizamos el archivo .htaccess. Se trata de un archivo especial de Apache donde podemos configurar el acceso a la web, las restricciones de seguridad, las redirecciones, etc. Es un archivo que admite muchas posibilidades y que puede dar mucho juego a tu hosting.
En nuestro caso, vamos a indicarle que queremos redirigir todas las peticiones que lleguen al servidor al archivo index.php mediante el siguiente código:
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . ./index.php [L]
Escribe un mensaje de bienvenida (cualquiera, es sólo para probar la redirección) en el archivo index.php y comprueba que cualquier petición muestra ese mensaje (miweb.com/api, miweb.com/api/peliculas, miweb.com/api/cosas…).
Una vez funcione la redirección, el archivo index.php es muy sencillo. Lo único que hará será incluir el archivo index.php de la carpeta api.
Lo primero de todo, escribe cualquier cosa en el archivo index.php y comprueba que se muestra al acceder a cualquier ruta, con lo que la inclusión del archivo funciona como toca.
El siguiente paso, será crear nuestra clase App, que será la clase principal que cargaremos cuando llegue una petición. Aunque todavía no hemos visto POO en PHP, copia el siguiente código en el archivo App.php:
namespace App; class App { public function run(): void { echo "Running application"; } }
No te preocupes por ahora de las partes que no entiendas (ya lo entenderás cuando veamos POO y Namespaces).
Vamos a aprovechar ahora para inicializar Composer y así poder usar su autoload. Antes de continuar, vamos a añadir algo de código al archivo composer.json para poder cargar de forma automática clases y archivos:
{ "name": "cesar/api", "autoload": { "psr-4": { "App\\": "src/" }, "files": ["src/helpers.php"] }, "authors": [ { "name": "César Guijarro", "email": "cguijarro@fpmislata.com" } ], "require": { "bramus/router": "^1.6", "larapack/dd": "^1.1" } }
Fíjate en el código resaltado de arriba. En la sección autoload del archivo composer.json, podemos indicar como se van a autocargar nuestras clases, archivos… Por ahora, la sección psr-4 déjala como está (tiene que ver con Namespaces, que veremos más adelante). En la siguiente sección (files), podemos indicar archivos que se cargarán (se incluirán) siempre que se inicié nuestra aplicación. Como ves, aprovechamos para cargar de inicio el archivo helpers.php, donde iremos añadiendo funciones globales que estarán accesibles desde cualquier parte de la aplicación sin necesidad de incluir el archivo (ya lo hace Composer por nosotros al inicio).
Vamos a aprovechar para crear una función global (base_path()) en helpers.php que devolverá la ruta base de nuestra aplicación (app) más la ruta que le pasemos. De esta forma, si queremos acceder al archivo routes/api.php podremos usar la función de la forma base_path('routes/api.php'), así nos despreocupamos de tener que averiguar la ruta principal cuando despleguemos nuestra API en un servidor HTTP:
define("BASE_DIR", dirname(__DIR__)); if (! function_exists('base_path')) { /** * Devuelve la ruta indicada desde la ruta raiz del proyecto. * * @param string $path * @return string */ function base_path(string $path = ''): string { return BASE_DIR . "/" . ltrim($path, "/"); } }
En la primera línea usamos la función dirname de PHP, que devuelve la ruta de un directorio padre. También usamos la constante predefinida __DIR__, que devuelve del directorio del fichero. De esta forma, como nuestro archivo helpers.php está dentro de la carpeta src, la constante BASE_DIR que definimos apuntará a la carpeta api.
Vamos a instalar también la librería bramus/router, que usaremos más adelante para crear nuestras rutas.
Una vez inicializado Composer y con las librerías instaladas, añade lo siguiente al archivo index.php:
use App\App; require_once __DIR__ . "/vendor/autoload.php"; $app = new App(); $app->run();
La primera línea tiene que ver, de nuevo, con Namespaces (como antes, ya lo entenderás más adelante. Por ahora, déjala como está). La siguiente carga el autoload del Composer. Fíjate que todavía no podemos utilizar la función que hemos creado en helpers.php (base_path), ya que hasta que no se carga el autoload no está disponible.
Las dos siguientes son la creación de un objeto de tipo App y cargar su método run(). Si pruebas ahora cualquier ruta, te debería salir la frase Running application.
Vamos ahora a usar la librería bramus/router para ir definiendo nuestras rutas. La idea es crear rutas del estilo:
http://miservidor.com/api/peliculas
Para ir sirviendo los diferentes recursos. La librería bramus/router nos simplifica mucho la creación de esas rutas. Escribe el siguiente código en el archivo routes/api.php:
$router = new \Bramus\Router\Router(); $router->setNamespace('\App'); /** * Definimos nuestras rutas */ $router->get('/', function() { echo "Bienvenido a la API de películas"; }); $router->get('/peliculas', function() { echo "Listado de todas las películas"; }); $router->run();
Como hemos dicho, su uso es muy sencillo. Al principio creamos una instancia de la clase Router(). La siguiente línea es para indicar que todos nuestros Namespaces empiezan con App (como siempre, ya entenderás su funcionamiento más adelante). Lo único que nos queda, es ir añadiendo rutas mediante $router→metodoHTTP().
En nuestro caso, creamos dos rutas (la raiz y /peliculas), ambas con el método GET. Por ahora, lo único que mostraremos es una frase indicando qué recurso se ha solicitado.
Por último, debemos cambiar el código del método run() de nuestra clase App para incluir el archivo de rutas (mediante un include o require). Utiliza la función base_path() que hemos creado en helpers.php.
Ampliación 1 (2 pts)
Crea otra ruta para servir peticiones del tipo miweb.com/api/peliculas/3 con los detalles de la película con id 3. Averigua como crear rutas con parámetros opcionales con la librería bramus/router. La ruta deberá mostrar la frase Detalle de la película con id 3 (el id puede cambiar y la aplicación deberá responder a cualquier id).
Ampliación 2 (2 pts)
Averigua como mostrar un mensaje de error cuando la ruta no existe.