Scroll down to the script below, click on any sentence (including terminal blocks!) to jump to that spot in the video!
With a Subscription, click any sentence in the script to jump to that part of the video!Login Subscribe
We're not going to talk specifically about security in this tutorial - we'll do that in our next course and give it proper attention. But, even forgetting about security and logging in and all that, there's a pretty good chance that your API will have some concept of "users". In our case, a "user" will post a cheese listing and becomes its "owner". And maybe later, in order to buy a CheeseListing, one User might send a message to another User. It's time to take our app to the next level by creating that entity.
And even though I'm telling you not to think about security, instead of creating the user entity with
make:entity like I normally would, I'm actually going to use
php bin/console make:user
Yea, this will set up a few security-related things... but nothing that we'll use yet. Watch part 2 of this series for all that stuff.
Anyways, call the class
User, and I do want to store users in the database. For the unique display name, I'm going to have users log in via email, so use that. And then:
Does this app need to hash or check user passwords?
We'll talk more about this in the security tutorial. But if users will need to log in to your site via a password and your app will be responsible for checking to see if that password is valid - you're not just sending the password to some other service to be verified - then answer yes. It doesn't matter if the user will enter the password via an iPhone app that talks to your API or via a login form - answer yes if your app is responsible for managing user passwords.
I'll use the Argon2i password hasher. But! If you don't see this question, that's ok! Starting in Symfony 4.3, you don't need to choose a password hashing algorithm because Symfony can choose the best available automatically. Really cool stuff.
Let's go see what this did! I'm happy to say... not much! First, we now have a
User entity. And... there's nothing special about it: it does have a few extra security-related methods, like
eraseCredentials(), but they won't affect what we're doing. Mostly we have a normal, boring entity with
$roles array property, and
$password, which will eventually store the hashed password.
|... lines 1 - 2|
|use Doctrine\ORM\Mapping as ORM;|
|class User implements UserInterface|
|* @ORM\Column(type="string", length=180, unique=true)|
|private $roles = ;|
|* @var string The hashed password|
|... lines 35 - 112|
This also created the normal
UserRepository and made a couple of changes to
security.yaml: it set up
encoders - this might say
auto for you, thanks to the new Symfony 4.3 feature - and the user provider. All things to talk more about later. So... just forget they're here and instead say... yay! We have a
|... lines 5 - 6|
|# used to reload user from session & other features (e.g. switch_user)|
|... lines 13 - 33|
|... lines 1 - 2|
|* @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)|
|... lines 21 - 49|
Thanks to the command, the entity has an
php bin/console make:entity
User and add
username as a
string, 255, not nullable in the database, and hit enter to finish.
Now open up
User... and scroll down to
make:user command generated this and returned
$this->email... because that's what I chose as my "display" name for security. Now that we really do have a username field, return
|... lines 1 - 10|
|class User implements UserInterface|
|... lines 13 - 35|
|* @ORM\Column(type="string", length=255)|
|... lines 40 - 62|
|public function getUsername(): string|
|return (string) $this->username;|
|... lines 67 - 124|
Oh, and while we're making this class, just, amazing, the
make:user command knew that
unique=true. Let's also add that to
|... lines 1 - 35|
|* @ORM\Column(type="string", length=255, unique=true)|
|... lines 40 - 126|
That is a nice entity! Let's sync up our database by running:
php bin/console make:migration
Move over... and double-check the SQL:
CREATE TABLE user - looks good!
|... 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|
Run it with:
php bin/console doctrine:migration:migrate
Perfect! We have a gorgeous new Doctrine entity... but as far as API Platform is concerned, we still only have one API resource:
Next: let's expose
User as an API Resource and use all of our new knowledge to perfect that new resource in... about 5 minutes.