====== Práctica 1 ====== **6 pts** En esta primera práctica vamos a empezar a desarrollar una API de películas. ===== Estructura de carpetas ===== 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. Si tuviéramos más proyectos, podríamos hacer que la carpeta //api// estuviera dentro de otra carpeta con el nombre del proyecto que estemos desarrollando (//peliculas/api//, por ejemplo), pero nosotros vamos a simplificar el proceso. Relacionado con la anterior nota, creamos la carpeta //api// dentro de //html// para acceder a nuestra API mediante URL del estilo //miweb.com/api//. Igual que antes, si tuviésemos más proyectos, deberíamos crear una carpeta de tal forma que accediéramos a la API de películas mediante //miweb.com/peliculas/api// Una buena práctica es añadir la versión de la API a la que estamos accediendo. Ten en cuenta que muchas veces tenemos varias versiones conviviendo juntas, con lo que podrías crear una carpeta con la versión de forma que las URL serían //miweb.com/api/v1.0//, //miweb.com/api/v2.0//... ===== Carpeta html/api ===== En esta carpeta tendremos dos archivos: //index.php// y //.htaccess//. Fíjate bien que el nombre del segundo archivo empieza por punto. 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//. ===== 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 [[https://www.php.net/manual/es/function.dirname.php|dirname]] de PHP, que devuelve la ruta de un directorio padre. También usamos la constante predefinida [[https://www.php.net/manual/es/language.constants.predefined.php|__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 [[https://packagist.org/packages/bramus/router|bramus/router]], que usaremos más adelante para crear nuestras rutas. Otra librería bastante útil a la hora de depurar aplicaciones es [[https://packagist.org/packages/larapack/dd|larapack/dd]]. No es imprescindible, pero te aconsejo que la instales y aprendas su funcionamiento (muy sencillo), ya que facilita bastante la depuración. 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 ;#; ;#; 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//. ===== Ampliaciones ===== **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.