Ora che abbiamo connesso la nostra applicazione al database, possiamo modificare il sistema di login affinché carichi gli utenti dal database anziché in memoria.
Prima di procedere è necessario aggiornare Symfony all’ultima versione disponibile, così da avere anche l’applicazione aggiornata. Vi basta spostarvi sul tag
bump-symfony-5.2
e lanciare uncomposer install
. Se si dovesse verificare qualche problema di conflitti provate a cancellare la cartellavendor
per poi riprovare.
Creazione entità
Il primo passo per modificare il sistema di login consiste nella creazione dell’Entità su cui mapperemo l’utente. Abbiamo già visto cos’è un’entità quando abbiamo introdotto Doctrine e la libreria di ORM. Andiamo quindi a creare la classe PHP e le relative configurazioni che consentiranno a Doctrine di mappare la tabella user
sul database con l’entità App\Entity\User
.
Non sarà un lavoro molto lungo la creazione dell’entità, molto del lavoro lo farà la linea di comando.
All’interno del container, quindi, lanciamo il comando ./bin/console make:user
che, come possiamo immaginare dal nome, aprirà il wizard con cui creare la nostra entità User
. La maggior parte delle impostazioni di default non dovranno esser modificate, limitiamoci quindi a dire al sistema che utilizzeremo la e-mail come campo “display”. Di seguito l’output completo:
$ ./bin/console make:user
The name of the security user class (e.g. User) [User]:
>
Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
>
Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
> email
Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).
Does this app need to hash/check user passwords? (yes/no) [yes]:
>
created: src/Entity/User.php
created: src/Repository/UserRepository.php
updated: src/Entity/User.php
updated: config/packages/security.yaml
Success!
Next Steps:
- Review your new App\Entity\User class.
- Use make:entity to add more fields to your User entity and then run make:migration.
- Create a way to authenticate! See https://symfony.com/doc/current/security.html
Come anche il comando ci suggerisce, sono stati generati due nuovi file ed aggiornato il file security.yaml
.
Dando un veloce sguardo all’entità appena creata (app/src/Entity/User.php
) noteremo che non è solo una semplice classe PHP ma che contiene anche diverse annotazioni. Come per il Repository, tratteremo queste annotazioni in una lezione dedicata, per ora è importante sapere che sono le configurazioni necessarie a Doctrine per associare l’entità alla tabella sul database.
L’altro file creato (src/Repository/UserRepository.php
) è il repository della nostra entità. Introdurremo in una delle prossime lezioni il concetto di Repository, mentre per il momento ci è sufficiente sapere, in questa fase, che sarà la classe che ci permetterà di effettuare query al database per l’utente.
Il security.yaml
, invece, è stato modificato per aggiungere:
- un nuovo encoder: serve ad indicare quale meccanismo di encoding verrà utilizzato per generare l’hash della password;
- è stato sostituito il provider in memory con uno che contiene utilizza la nuova entità.
Dopo aver apportato queste modifiche il precedente sistema di login non funzionerà più. Se abbiamo una sessione ancora attiva effettuiamo il logout per evitare di ricevere errori: http://kvak.local/logout.
Creazione ed esecuzione della migrazione
Come suggerito nell’output del comando make:user
, dobbiamo creare la tabella user
nel database. Per fare ciò abbiamo bisogno di affrontare due step.
Creare una migrazione
Una migrazione un file PHP che contiene le query che andranno a modificare il database per allinearlo alle modifiche che abbiamo effettuato sull’applicazione. È molto utile utilizzare le migrazioni per tenere il database allineato anche in fase di deploy senza la necessità di eseguire delle query manualmente, riducendo nel contempo in modo notevole la possibilità di un errore umano.
Lanciamo quindi il comando come di seguito:
$ ./bin/console make:migration
Success!
Next: Review the new migration "migrations/Version20210220161856.php"
Then: Run the migration with php bin/console doctrine:migrations:migrate
See https://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
Se apriamo il file appena creato (migrations/Version20210220161856.php
) noteremo che continue due metodi: uno con una query che crea la tabella utente e uno che la droppa.
Questo perché le migrazioni possono essere eseguite ma anche rimosse dal database. Immaginiamo per esempio cosa succederebbe quando qualcosa va storto e si deve effettuare il rollback dell’applicazione ad una situazione precedente a quella del deploy.
Lanceremo questo comando ogni volta che aggiungeremo o rimuoveremo tabelle e/o campi dal database. Non interverremo mai manualmente sul database.
Eseguire la migrazione nel nostro database locale
una volta creato il file che contiene le query, possiamo lanciare un altro comando che applica le migrazioni mancanti:
$ php bin/console doctrine:migrations:migrate
WARNING! You are about to execute a migration in database "kvak" that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:
> yes
[notice] Migrating up to DoctrineMigrations\Version20210220161856
[notice] finished in 148.7ms, used 18M memory, 1 migrations executed, 1 sql queries
Accedendo al database dopo questa operazione noteremo che abbiamo due tabelle:
user
che contiene 4 campi:id
,email
,roles
,password
.doctrine_migration_versions
che contiene le migrazioni finora applicate. Nel nostro caso solo una.
Caricamento dati di prova
Ora che abbiamo la tabella utenti nel database possiamo creare qualche utente di prova con cui effettuare il login. Per fare questo creeremo delle fixtures, ovvero un set di dati pre-esistente con cui possiamo popolare l’applicazione in ogni momento. Per fare questo abbiamo bisogno di aggiungere una nuova ricetta:
$ composer require orm-fixtures --dev
Al termine dell’operazione oltre ad avere i vari file di composer e Symfony Flex modificati, troveremo anche un nuovo file app/src/DataFixtures/AppFixtures.php
. All’interno di questo file creeremo due utenti che useremo per popolare il database:
class AppFixtures extends Fixture
{
/** @var UserPasswordEncoderInterface */
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
public function load(ObjectManager $manager)
{
$ryan = new User();
$ryan->setEmail('ryan@ryan.it');
$ryan->setPassword($this->passwordEncoder->encodePassword($ryan, 'helloworld'));
$julie = new User();
$julie->setEmail('julie@julie.it');
$julie->setPassword($this->passwordEncoder->encodePassword($julie, 'helloworld'));
$manager->persist($ryan);
$manager->persist($julie);
$manager->flush();
}
}
Guardando il costruttore noteremo che abbiamo anche iniettato un password encoder, questo per permettere di salvare su database la password cifrata e non in chiaro. Ora possiamo caricare le fixtures sul nostro database con il comando:
www-data@4d191dffa1fb:~/kvak$ ./bin/console doctrine:fixtures:load
Careful, database "kvak" will be purged. Do you want to continue? (yes/no) [no]:
> yes
> purging database
> loading App\DataFixtures\AppFixtures
Se guardiamo ora sul database avremo i nostri due utenti appena creati! Ci basterà quindi effettuare il login con le credenziali di uno dei due per poter accedere nuovamente all’applicazione.
Tutto il codice è disponibile nel repository con il tag login-from-db
.
Come abbiamo potuto notare da questa lezione, sostituire la sorgente di dati da cui caricare gli utenti è stato davvero molto semplice e veloce. L’unico codice necessario è stato quello per la creazione di utenti di prova. Ora che abbiamo gli utenti salvati sul database possiamo procedere a creare anche la pagina di registrazione.