gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
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!
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!
// composer.json
{
"require": {
"php": "^7.4.1 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"babdev/pagerfanta-bundle": "^3.3", // v3.3.0
"composer/package-versions-deprecated": "^1.11", // 1.11.99.4
"doctrine/annotations": "^1.0", // 1.13.2
"doctrine/doctrine-bundle": "^2.1", // 2.6.3
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.1.1
"doctrine/orm": "^2.7", // 2.10.1
"knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
"knplabs/knp-time-bundle": "^1.11", // v1.16.1
"pagerfanta/doctrine-orm-adapter": "^3.3", // v3.3.0
"pagerfanta/twig": "^3.3", // v3.3.0
"phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
"scheb/2fa-bundle": "^5.12", // v5.12.1
"scheb/2fa-qr-code": "^5.12", // v5.12.1
"scheb/2fa-totp": "^5.12", // v5.12.1
"sensio/framework-extra-bundle": "^6.0", // v6.2.0
"stof/doctrine-extensions-bundle": "^1.4", // v1.6.0
"symfony/asset": "5.3.*", // v5.3.4
"symfony/console": "5.3.*", // v5.3.7
"symfony/dotenv": "5.3.*", // v5.3.8
"symfony/flex": "^1.3.1", // v1.17.5
"symfony/form": "5.3.*", // v5.3.8
"symfony/framework-bundle": "5.3.*", // v5.3.8
"symfony/monolog-bundle": "^3.0", // v3.7.0
"symfony/property-access": "5.3.*", // v5.3.8
"symfony/property-info": "5.3.*", // v5.3.8
"symfony/rate-limiter": "5.3.*", // v5.3.4
"symfony/runtime": "5.3.*", // v5.3.4
"symfony/security-bundle": "5.3.*", // v5.3.8
"symfony/serializer": "5.3.*", // v5.3.8
"symfony/stopwatch": "5.3.*", // v5.3.4
"symfony/twig-bundle": "5.3.*", // v5.3.4
"symfony/ux-chartjs": "^1.3", // v1.3.0
"symfony/validator": "5.3.*", // v5.3.8
"symfony/webpack-encore-bundle": "^1.7", // v1.12.0
"symfony/yaml": "5.3.*", // v5.3.6
"symfonycasts/verify-email-bundle": "^1.5", // v1.5.0
"twig/extra-bundle": "^2.12|^3.0", // v3.3.3
"twig/string-extra": "^3.3", // v3.3.3
"twig/twig": "^2.12|^3.0" // v3.3.3
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
"symfony/debug-bundle": "5.3.*", // v5.3.4
"symfony/maker-bundle": "^1.15", // v1.34.0
"symfony/var-dumper": "5.3.*", // v5.3.8
"symfony/web-profiler-bundle": "5.3.*", // v5.3.8
"zenstruck/foundry": "^1.1" // v1.13.3
}
}