Login to bookmark this video
Buy Access to Course
18.

Recurso API de usuario

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Quiero exponer nuestra nueva entidad User como un recurso API. ¡Y ya sabemos cómo hacerlo! Añade... @ApiResource!

128 lines | src/Entity/User.php
// ... lines 1 - 4
use ApiPlatform\Core\Annotation\ApiResource;
// ... lines 6 - 8
/**
* @ApiResource()
// ... line 11
*/
class User implements UserInterface
// ... lines 14 - 128

¡Así de fácil! ¡Sí! Nuestra documentación de la API muestra un nuevo recurso con cinco nuevas rutas, u operaciones. Y en la parte inferior, está el nuevo modelo User.

Hmm, pero es un poco extraño: tanto el campo password con hash como el array roles forman parte de la API. Sí, ¡podríamos crear un nuevo usuario ahora mismo y pasarle los roles que creamos que debe tener! Eso podría estar bien para un usuario administrador, pero no para cualquiera. Tomemos el control de las cosas.

¿Usuarios?

Una cosa que quiero que notes es que, hasta ahora, la clave primaria siempre se utiliza como "id" en nuestra API. Esto es algo que es flexible en la Plataforma API. De hecho, en lugar de utilizar un id autoincrementado, una opción es utilizar un UUID. No vamos a utilizarlos en este tutorial, pero utilizar un UUID como identificador es algo que admiten Doctrine y la Plataforma API. Los UUIDs funcionan con cualquier base de datos, pero se almacenan de forma más eficiente en PostgreSQL que en MySQL, aunque utilizamos algunos UUIDs en MySQL en algunas partes de SymfonyCasts.

Pero... ¿por qué te hablo de UUID's? ¿Qué hay de malo en autoincrementar los ids? Nada... pero.... Los UUID's pueden ayudar a simplificar tu código JavaScript. Supongamos que escribimos un JavaScript para crear un nuevo CheeseListing. Con los ids autoincrementados, el proceso se parece a esto: hacer una petición POST a /api/cheeses, esperar la respuesta, luego leer el @id de la respuesta y almacenarlo en algún sitio... porque normalmente necesitarás saber el id de cada lista de quesos. Con los UUID, el proceso es así: genera un UUID en JavaScript -eso es totalmente legal-, envía la petición POST y... ¡ya está! Con los UUID's, no necesitas esperar a que termine la llamada AJAX para poder leer el id: has creado el UUID en JavaScript, así que ya lo conoces. Por eso los UUID a menudo pueden ser muy útiles.

Para que todo esto funcione, tendrás que configurar tu entidad para que utilice un UUID y añadir un método setId() para que sea posible que la Plataforma API lo establezca. O puedes crear el id de autoincremento y añadir una propiedad UUID independiente. La Plataforma API tiene una anotación para marcar un campo como "identificador".

Grupos de normalización y desnormalización

De todos modos, vamos a tomar el control del proceso de serialización para poder eliminar cualquier campo extraño, como que se devuelva la contraseña codificada. Haremos exactamente lo mismo que hicimos en CheeseListing: añadir grupos de normalización y desnormalización. Copia las dos líneas de contexto, abre User y pégalas. Voy a eliminar la parte deswagger_definition_name: realmente no la necesitamos. Para la normalización, utilizauser:read y para la desnormalización, user:write.

135 lines | src/Entity/User.php
// ... lines 1 - 9
/**
* @ApiResource(
* normalizationContext={"groups"={"user:read"}},
* denormalizationContext={"groups"={"user:write"}},
* )
// ... line 15
*/
class User implements UserInterface
// ... lines 18 - 135

Seguimos el mismo patrón que hemos estado utilizando. Ahora... pensemos: ¿qué campos necesitamos exponer? Para $email, añade @Groups({}) con "user:read", "user:write": es un campo legible y escribible. Cópialo, pégalo encima de password y hazlo sólo con user:write.

135 lines | src/Entity/User.php
// ... lines 1 - 7
use Symfony\Component\Serializer\Annotation\Groups;
// ... lines 9 - 16
class User implements UserInterface
{
// ... lines 19 - 25
/**
// ... line 27
* @Groups({"user:read", "user:write"})
*/
private $email;
// ... lines 31 - 36
/**
// ... lines 38 - 39
* @Groups({"user:write"})
*/
private $password;
// ... lines 43 - 133
}

Esto... no tiene mucho sentido todavía. Es decir, ya no es legible, lo que tiene mucho sentido. Pero esto acabará almacenando la contraseña codificada, que no es algo que un cliente de la API vaya a establecer directamente. Pero... nos preocuparemos de todo eso en nuestro tutorial de seguridad. Por ahora, como la contraseña es un campo obligatorio en la base de datos, vamos a hacerla temporalmente escribible para que no nos estorbe.

Por último, haz que username sea legible y también escribible.

135 lines | src/Entity/User.php
// ... lines 1 - 16
class User implements UserInterface
{
// ... lines 19 - 43
/**
// ... line 45
* @Groups({"user:read", "user:write"})
*/
private $username;
// ... lines 49 - 133
}

¡Vamos a probarlo! Actualiza los documentos. Al igual que con CheeseListing, ahora tenemos dos modelos: podemos leer email y username y podemos escribir email, passwordy username.

Lo único que nos falta para que sea un recurso de la API totalmente funcional es la validación. Para empezar, tanto $email como $username deben ser únicos. En la parte superior de la clase, añade @UniqueEntity() con fields={"username"}, y otro@UniqueEntity() con fields={"email"}.

142 lines | src/Entity/User.php
// ... lines 1 - 6
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
// ... lines 8 - 11
/**
// ... lines 13 - 16
* @UniqueEntity(fields={"username"})
* @UniqueEntity(fields={"email"})
// ... line 19
*/
class User implements UserInterface
// ... lines 22 - 142

Entonces, veamos, $email debe ser @Assert\NotBlank() y @Assert\Email(), y $username necesita ser @Assert\NotBlank(). No me preocuparé todavía de la contraseña, eso hay que arreglarlo bien de todos modos en el tutorial de seguridad.

142 lines | src/Entity/User.php
// ... lines 1 - 9
use Symfony\Component\Validator\Constraints as Assert;
// ... lines 11 - 20
class User implements UserInterface
{
// ... lines 23 - 29
/**
// ... lines 31 - 32
* @Assert\NotBlank()
* @Assert\Email()
*/
private $email;
// ... lines 37 - 49
/**
// ... lines 51 - 52
* @Assert\NotBlank()
*/
private $username;
// ... lines 56 - 140
}

Así que, ¡creo que estamos bien! Actualiza la documentación y empecemos a crear usuarios! Haz clic en "Probar". Utilizaré mi dirección de correo electrónico personal de la vida real:cheeselover1@example.com. La contraseña no importa... y hagamos que el nombre de usuario coincida con el correo electrónico sin el dominio... para no confundirme. ¡Ejecuta!

¡Woohoo! ¡201 éxito! Vamos a crear un usuario más... para tener mejores datos con los que jugar.

Validación fallida

¿Y si enviamos un JSON vacío? Pruébalo. ¡Sí! código de estado 400.

Bien... ¡hemos terminado! Tenemos 1 nuevo recurso, cinco nuevas operaciones, control sobre los campos de entrada y salida, validación, paginación y podríamos añadir fácilmente el filtrado... ¡es increíble! Este es el poder de la Plataforma API. Y a medida que vayas mejorando en su uso, desarrollarás aún más rápido.

Pero en última instancia, creamos el nuevo recurso API User no sólo porque crear usuarios es divertido: lo hicimos para poder relacionar cada CheeseListing con el User que lo "posee". En una API, las relaciones son un concepto clave. Y te va a encantar cómo funcionan en la Plataforma API.