Buy Access to Course
17.

Creación de la entidad 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

No vamos a hablar específicamente de la seguridad en este tutorial; lo haremos en nuestro próximo curso y le prestaremos la debida atención. Pero, incluso olvidando la seguridad y el inicio de sesión y todo eso, es muy probable que tu API tenga algún concepto de "usuarios". En nuestro caso, un "usuario" publicará un listado de quesos y se convertirá en su "propietario". Y puede que después, para comprar un listado de quesos, un usuario envíe un mensaje a otro usuario. Es hora de llevar nuestra aplicación al siguiente nivel creando esa entidad.

make:usuario

Y aunque te diga que no pienses en la seguridad, en lugar de crear la entidad usuario con make:entity como haría normalmente, voy a utilizar make:user,

php bin/console make:user

Sí, esto configurará algunas cosas relacionadas con la seguridad... pero nada que vayamos a utilizar todavía. Mira la parte 2 de esta serie para ver todas esas cosas.

En cualquier caso, llama a la clase User, y quiero almacenar los usuarios en la base de datos. Para el nombre único de visualización, voy a hacer que los usuarios se registren a través del correo electrónico, así que usa eso. Y entonces:

¿Esta aplicación necesita hacer un hash o comprobar las contraseñas de los usuarios?

Hablaremos más de esto en el tutorial de seguridad. Pero si los usuarios van a tener que iniciar sesión en tu sitio a través de una contraseña y tu aplicación va a ser la responsable de comprobar si esa contraseña es válida -no te limitas a enviar la contraseña a algún otro servicio para que la verifique-, entonces responde que sí. No importa si el usuario va a introducir la contraseña a través de una aplicación de iPhone que habla con tu API o a través de un formulario de inicio de sesión: responde que sí si tu aplicación es responsable de gestionar las contraseñas de los usuarios.

Utilizaré el hashtag de contraseñas Argon2i. Pero si no ves esta pregunta, ¡no pasa nada! A partir de Symfony 4.3, no es necesario que elijas un algoritmo de hashing de contraseñas porque Symfony puede elegir el mejor disponible de forma automática. Algo realmente genial.

¡Vamos a ver qué hace esto! Me alegra decir que... ¡no mucho! En primer lugar, ahora tenemos una entidadUser. Y... no tiene nada de especial: tiene algunos métodos adicionales relacionados con la seguridad, como getRoles(), getPassword(), getSalt()y eraseCredentials(), pero no afectarán a lo que estamos haciendo. En su mayor parte, tenemos una entidad normal y aburrida con $id, $email, una propiedad de matriz $roles, y $password, que finalmente almacenará la contraseña con hash.

114 lines | src/Entity/User.php
// ... lines 1 - 2
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
// ... lines 35 - 112
}

Esto también ha creado la entidad normal UserRepository y ha hecho un par de cambios ensecurity.yaml: ha creado encoders - esto podría decir auto para ti, gracias a la nueva característica de Symfony 4.3 - y el proveedor de usuarios. Cosas de las que hablaremos más adelante. Así que... olvida que están aquí y en su lugar di... ¡vaya! ¡Tenemos una entidadUser!

33 lines | config/packages/security.yaml
security:
encoders:
App\Entity\User:
algorithm: argon2i
// ... lines 5 - 6
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
// ... lines 13 - 33

51 lines | src/Repository/UserRepository.php
// ... lines 1 - 2
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, User::class);
}
// ... lines 21 - 49
}

Añadiendo el campo de nombre de usuario

Gracias al comando, la entidad tiene una propiedad email, y pienso hacer que los usuarios se registren utilizando eso. Pero también quiero que cada usuario tenga un "nombre de usuario" que podamos mostrar públicamente. Vamos a añadirlo: busca tu terminal y ejecuta

php bin/console make:entity

Actualiza User y añade username como string, 255, no anulable en la base de datos, y pulsa intro para terminar.

Ahora abre User... y desplázate hasta getUsername(). El comando make:usergeneró esto y devolvió $this->email... porque eso es lo que elegí como mi nombre "para mostrar" por seguridad. Ahora que realmente tenemos un campo de nombre de usuario, devuelve $this->username.

126 lines | src/Entity/User.php
// ... lines 1 - 10
class User implements UserInterface
{
// ... lines 13 - 35
/**
* @ORM\Column(type="string", length=255)
*/
private $username;
// ... lines 40 - 62
public function getUsername(): string
{
return (string) $this->username;
}
// ... lines 67 - 124
}

Ah, y mientras hacemos esta clase, simplemente, increíble, el comando make:user sabía que email debía ser único, así que añadió unique=true. Añadamos también eso a username: unique=true.

126 lines | src/Entity/User.php
// ... lines 1 - 35
/**
* @ORM\Column(type="string", length=255, unique=true)
*/
private $username;
// ... lines 40 - 126

¡Esa es una bonita entidad! Vamos a sincronizar nuestra base de datos ejecutando:

php bin/console make:migration

Muévete... y vuelve a comprobar el SQL: CREATE TABLE user - ¡se ve bien!

36 lines | src/Migrations/Version20190509185722.php
// ... lines 1 - 12
final class Version20190509185722 extends AbstractMigration
{
// ... lines 15 - 19
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, username VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
}
// ... lines 27 - 34
}

Ejecútalo con:

php bin/console doctrine:migration:migrate

¡Perfecto! Tenemos una nueva y preciosa entidad Doctrine... pero en lo que respecta a la Plataforma API, seguimos teniendo sólo un recurso API: CheeseListing.

Lo siguiente: vamos a exponer User como un Recurso API y a utilizar todos nuestros nuevos conocimientos para perfeccionar ese nuevo recurso en... unos 5 minutos.