Customizing the User Class
What's cool about the User
class is that... it's our class! As long as we implement UserInterface
, we can add whatever else we want:
// ... lines 1 - 7 | |
use Symfony\Component\Security\Core\User\UserInterface; | |
// ... lines 9 - 12 | |
class User implements UserInterface | |
{ | |
// ... lines 15 - 113 | |
} |
For example, I'd like to store the first name of my users. So let's go add a property for that. At your terminal, run:
symfony console make:entity
We'll edit the User
entity, add a firstName
property, have it be a string, 255 length... and say "yes" to nullable. Let's make this property optional in the database.
Done! Back over in the User
class, no surprises! We have a new property... and new getter and setter methods:
// ... lines 1 - 12 | |
class User implements UserInterface | |
{ | |
// ... lines 15 - 31 | |
/** | |
* @ORM\Column(type="string", length=255, nullable=true) | |
*/ | |
private $firstName; | |
// ... lines 36 - 119 | |
public function getFirstName(): ?string | |
{ | |
return $this->firstName; | |
} | |
public function setFirstName(string $firstName): self | |
{ | |
$this->firstName = $firstName; | |
return $this; | |
} | |
} |
Go generate a migration for our new User
. At the terminal, run
symfony console make:migration
Cool! Spin over and open that up to make sure it's not hiding any surprises:
// ... lines 1 - 2 | |
declare(strict_types=1); | |
namespace DoctrineMigrations; | |
use Doctrine\DBAL\Schema\Schema; | |
use Doctrine\Migrations\AbstractMigration; | |
/** | |
* Auto-generated Migration: Please modify to your needs! | |
*/ | |
final class Version20211001172813 extends AbstractMigration | |
{ | |
public function getDescription(): string | |
{ | |
return ''; | |
} | |
public function up(Schema $schema): void | |
{ | |
// this up() migration is auto-generated, please modify it to your needs | |
$this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, first_name VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); | |
} | |
public function down(Schema $schema): void | |
{ | |
// this down() migration is auto-generated, please modify it to your needs | |
$this->addSql('DROP TABLE user'); | |
} | |
} |
Awesome: CREATE TABLE user
with id
, email
, roles
and first_name
columns. Close this... and run it:
symfony console doctrine:migrations:migrate
Success!
Adding User Fixtures
And because the User
entity is... just a normal Doctrine entity, we can also add dummy users to our database using the fixtures system.
Open up src/DataFixtures/AppFixtures.php
. We're using Foundry to help us load data. So let's create a new Foundry factory for User
. Since we're having SO much fun running commands in this video, let's sneak in one... or three more:
symfony console make:factory
Yup! We want one for User
. Go open it up: src/Factory/UserFactory.php
:
// ... lines 1 - 2 | |
namespace App\Factory; | |
use App\Entity\User; | |
use App\Repository\UserRepository; | |
use Zenstruck\Foundry\RepositoryProxy; | |
use Zenstruck\Foundry\ModelFactory; | |
use Zenstruck\Foundry\Proxy; | |
// ... lines 10 - 28 | |
final class UserFactory extends ModelFactory | |
{ | |
public function __construct() | |
{ | |
parent::__construct(); | |
// TODO inject services if required (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services) | |
} | |
protected function getDefaults(): array | |
{ | |
return [ | |
// TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) | |
'email' => self::faker()->text(), | |
'roles' => [], | |
'firstName' => self::faker()->text(), | |
]; | |
} | |
protected function initialize(): self | |
{ | |
// see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization | |
return $this | |
// ->afterInstantiate(function(User $user) {}) | |
; | |
} | |
protected static function getClass(): string | |
{ | |
return User::class; | |
} | |
} |
Our job in getDefaults()
is to make sure that all of the required properties have good default values. Set email
to self::faker()->email()
, I won't set any roles right now and set firstName
to self::faker()->firstName()
:
// ... lines 1 - 28 | |
final class UserFactory extends ModelFactory | |
{ | |
// ... lines 31 - 37 | |
protected function getDefaults(): array | |
{ | |
return [ | |
'email' => self::faker()->email(), | |
'firstName' => self::faker()->firstName(), | |
]; | |
} | |
// ... lines 45 - 57 | |
} |
Cool! Over in AppFixtures
, at the bottom, create a user: UserFactory::createOne()
. But use a specific email so we can log in using this later. How about, abraca_admin@example.com
:
// ... lines 1 - 11 | |
use App\Factory\UserFactory; | |
// ... lines 13 - 15 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager) | |
{ | |
// ... lines 20 - 41 | |
AnswerFactory::new(function() use ($questions) { | |
// ... lines 43 - 45 | |
})->needsApproval()->many(20)->create(); | |
UserFactory::createOne(['email' => 'abraca_admin@example.com']); | |
// ... lines 49 - 51 | |
} | |
} |
Then, to fill out the system a bit, add UserFactory::createMany(10)
:
// ... lines 1 - 11 | |
use App\Factory\UserFactory; | |
// ... lines 13 - 15 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager) | |
{ | |
// ... lines 20 - 47 | |
UserFactory::createOne(['email' => 'abraca_admin@example.com']); | |
UserFactory::createMany(10); | |
// ... lines 50 - 51 | |
} | |
} |
Let's try it! Back at the terminal, run:
symfony console doctrine:fixtures:load
No errors! Check out the new table:
symfony console doctrine:query:sql 'SELECT * FROM user'
And... there they are! Now that we have users in the database, we need to add one or more ways for them to authenticate. It's time to build a login form!
Hi, the symfony console make:factroy command is not defined, I'm with symfony 6.3.x what can I replace it with ? Thanks