Home » Laravel » Hay un Modelo oculto en su API REST

REST es un gran standard, pero a veces se necesitará ir más allá de lo convencional.

REST, REpresentational State Transfer, es un tipo de arquitectura, un conjunto de convenciones para aplicaciones web y servicios web, que se centra principalmente en la manipulación de recursos a través de especificaciones HTTP. Podemos decir que REST es una interfaz web estándar y simple que nos permite interactuar con servicios web de una manera muy cómoda.

Una REST API es una API, o librería de funciones, a la que se accede por el protocolo HTTP REST.

Una de las claves de su diseño es el conjunto de operaciones bien definidas que se aplican a todos los recursos de información: HTTP en sí define un conjunto pequeño de operaciones, las más importantes son POST, GET, PUT y DELETE.

Supongamos que su aplicación gestiona listas de correo, y los usuarios suscritos a ellas. Su estructura de base de datos podría tener este aspecto:

users
id email
1 john@example.com
mailing_lists
id title
1 Gotham News
mailing_list_users
id user_id mailing_list_id
1 1 1
2 1 2

…y pueden obtenerse un par de modelos:

class User
{
    public function mailingLists()
    {
        return $this->belongsToMany('MailingList');
    }
}

class MailingList
{
    public function users()
    {
        return $this->belongsToMany('User');
    }
}

Parece suficientemente sencillo ¿no? Y ahora crearemos un par de recursos (para REST, todo es un recurso):

Route::resource('users', 'UsersContoller');
Route::resource('mailing-lists', 'MailingListsContoller');

… que funcionan muy bien, hasta que necesite poder suscribir a un usuario a una lista de correo.
Entonces empieza a pensar: “Hmm … la acción que estoy tratando de hacer es suscribir, lo que no es exactamente los mismo que un verbo REST como crear o actualizar …”

“Supongo que tal vez una petición POST a /mailing-lists/1/subscribe, valdría, ¿no es cierto?”

En principio parece lógico, quiero decir ¿qué otra cosa se puede hacer? Realmente no está actualizando la lista, y sin duda no está creando, eliminando o recuperando la lista.

Pero si se conforma con esto, en realidad está perdiendo una gran oportunidad de enriquecer su proyecto. Create, Update y Delete deberían ser suficientes en teoría, pero en la práctica tendrá necesidad de más verbos. Cuando usted no puede lograr una acción que necesita específicamente, necesita darle forma a un nuevo “verbo” para utilizarlo en las peticiones a una API REST. Esta es una situación en el desarrollo de una aplicación en la que se tienen nuevos conceptos que no ha nombrado todavía.

Así que si nos obligamos a trabajar dentro de las limitaciones las operaciones crear / leer / actualizar / borrar, ¿qué es lo que realmente estamos creando, leyendo, actualizando o borrando?

En el caso de una relación many-to-many (muchos-a-muchos) como el ejemplo que estamos tratando, un truco que me gusta usar es obligarme a describirlo simplemente como has-many (uno –a-muchos, como un post o un artículo pueden tener varios comentarios pero cada comentario solo puede pertenecer a un post o a un artículo.).

Establecer una relación has-many para listas de correo de usuario, en realidad no tiene sentido. Los usuarios no son dueños de la lista de correo.

Pero si un usuario se suscribe a muchas listas de correo, se podría decir que tiene una relación has-many. Y cuando un usuario se suscribe a una lista de correo, está creando una nueva suscripción.

Espere un minuto, crear! Eso sí es un verbo REST!

Así que algo como una petición POST a /subscriptions suena casi perfecto.

Extrayendo un nuevo recurso

Puede crear una nueva ruta específica para la suscripción y redireccinarla a otro controlador

Route::resource('users', 'UsersContoller');
Route::resource('mailing-lists', 'MailingListsContoller');
Route::post('subscriptions', 'MailingListsContoller@subscribe');

O usted podría aprovechar esta oportunidad para añadir un recurso completamente nuevo!

Route::resource('users', 'UsersContoller');
Route::resource('mailing-lists', 'MailingListsContoller');
Route::resource('subscriptions', 'SubscriptionsContoller');

Puede objetar: “Pero no tengo un modelo de Suscripción,!”

En realidad, sí lo tiene. Si recuerda la tabla mailing_list_users, ¿qué tal si cambiamos el nombre a subscriptions

subscriptions
id user_id mailing_list_id
1 1 1
2 1 2

Podemos crear ese modelo Subscription ahora, y actualizar algunas de nuestras relaciones con algunos nombres mejores …

class Subscription
{
    public function user()
    {
        return $this->belongsTo('User');
    }

    public function mailingList()
    {
        return $this->belongsTo('MailingList');
    }
}

class User
{
    public function subscribedMailingLists()
    {
        return $this->belongsToMany('MailingList', 'subscriptions');
    }

    public function subscriptions()
    {
        return $this->hasMany('Subscription');
    }
}

class MailingList
{
    public function subscribers()
    {
        return $this->belongsToMany('User', 'subscriptions');
    }
}

Ahora bien, si alguna vez necesitamos añadir metadatos para una suscripción, tenemos una buena manera de trabajar con ellos, en lugar de tratar de hacer un montón de cosas en una pivot table (tabla intermedia) que no tiene representación en nuestro sistema.

Tal vez tenemos que almacenar una marca de tiempo last_payment_received_at asociada a la suscripción.

class User
{
    public function mailingLists()
    {
        return $this->belongsToMany('MailingList')->withPivot('last_payment_received_at');
    }
}

Y luego para usar esa información, tendríamos que acceder a ella a través de la propiedad pivot en listas de correo de un usuario:

foreach ($user->mailingLists as $mailingList) {
    echo $mailingList->pivot->last_payment_received_at;
}

Ciertamente parece que estamos modelando el dominio limpiamente, ¿no?

Compare esto con la forma en que se vería con nuestro modelo de Subscription que hemos creado:

foreach ($user->subscriptions as $subscription) {
    echo $subscription->last_payment_received_at;
}

Mucho más claro, ¿no?

Así que la próxima vez que se encuentre en la necesidad de romper con las convenciones de REST, de un paso atrás y oblíguese a trabajar sin las limitaciones REST. Hay una buena probabilidad de que su diseño será mejor.

Deja un comentario

Si te gustó este Artículo, lo mejor que puedes hacer por el Blog es compartirlo en tu Red Social y darle un "Me Gusta".

Tu dirección de correo electrónico no será publicada.

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>