Tabla de Contenidos

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

Otra librería bastante útil a la hora de depurar aplicaciones es 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.