====== 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.