Creare un semplice form di registrazione con Symfony e Doctrine.
Il form è funzionante e pronto all’uso.
0. Se è la prima volta che utilizziamo il DB, procediamo con il setup come mostrato nella lezione precedente a:
www.lucedigitale.com/blog/symfony-databases-and-the-doctrine-orm/
1. Creiamo il Database da PhpMyAdmin:
Database: test_project
Tabella: app_users
Campi:
– Id char(11) AUTO_INCREMENT
– username char (25) UNIQUE
– password char (60) UNIQUE
– email char (60) UNIQUE
2. Creiamo l’entità User in src/AppBundle/Entity/User.php
<?php
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM; // uso Doctrine
use Symfony\Component\Validator\Constraints as Assert; // Uso la validazione
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; // Uso la validazione
use Symfony\Component\Security\Core\User\UserInterface;// usa il sistema Symfony per gestire gli utenti
/**
* @ORM\Entity
* @ORM\Table(name="app_users")
* @UniqueEntity(fields="email", message="Email already taken")
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface// aggiungi i metodi seguenti alla classe UserInterface di Symfony
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=255, unique=true)
* @Assert\NotBlank()
* @Assert\Email()
*/
private $email;
/**
* @ORM\Column(type="string", length=255, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* @Assert\NotBlank()
* @Assert\Length(max=4096)
*/
private $plainPassword; // la password in chiaro, non è salvata nel DB, infatti non abbiamo @ORM ma solo una validazione
/**
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt.
*
* @ORM\Column(type="string", length=64)
*/
private $password; // la password criptata con bcript
// other properties and methods
// metodi GET e SET necessari per il funzionamento del form di Symfony
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
// ritorna NULL percho bcrypt gestisce il seme per criptare le password
public function getSalt()
{
// The bcrypt algorithm doesn't require a separate salt.
// You *may* need a real salt if you choose a different encoder.
return null;
}
// other methods, including security methods like getRoles()
public function getRoles()// i ruoli concessi all'utente
{
return array('ROLE_USER');
}
public function eraseCredentials()// rimuove i dati sensibili dell'utente
{
}
// Serialize
// Alla fine di ogni - request - l'oggetto User viene trasformato in un array (serialize) e salvato nella sessione
// Alla richiesta successiva viene ricaricato dalla sessione e riconvertito (unserialize)
/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt,
));
}
/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
}// END Class
Come funziona?
Indico a Symfony dove trovare questo file che contiene la classe che sarà caricata come namespace
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
Carico i namespace
use Doctrine\ORM\Mapping as ORM; // uso Doctrine
use Symfony\Component\Validator\Constraints as Assert; // Uso la validazione
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; // Uso la validazione
use Symfony\Component\Security\Core\User\UserInterface;// usa il sistema Symfony per gestire gli utenti
Aggiungo i metodi alla classe UserInterface di Symfony
class User implements UserInterface...
Dichiaro le proprietà della classe e le loro caratteristiche SQL
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
Metodi GET e SET necessari a Symfony
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
Altre note:
getSalt() – non serve per il bcrypt ma va esplicitata obbligatoriamente o ritorna errore
getRoles() – anche se NULL va esplicitata obbligatoriamente o ritorna errore
eraseCredentials() – anche se NULL va esplicitata obbligatoriamente o ritorna errore
$plainPassword – non viene salvata nel DB ma solo validata @Assert\NotBlank() …
3. Modifichiamo app/config/security.yml, configuro i parametri di security per puntare a User.php e al DB app_users
In particolare notiamo:
– encoders: AppBundle\Entity\User: algorithm: bcrypt
-> usa l’algoritmo bcrypt per criptare la password
– app_users: entity: class: AppBundle:User property: username
-> come gli utenti vengono caricati tradotto -> ‘nometabella’: in ‘AppBundle/Entity/User.php’ proprietà ‘username’
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/security.html
# app/config/security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
# http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
in_memory:
memory: ~
app_users:
entity:
class: AppBundle:User
property: username
# if you're using multiple entity managers
# manager_name: customer
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
# activate different ways to authenticate
pattern: ^/
http_basic: ~
# http://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
#http_basic: ~
# http://symfony.com/doc/current/cookbook/security/form_login_setup.html
#form_login: ~
4. Creiamo in src/AppBundle/Form/UserType.php il form come classe in modo che sia riusabile con facilità
<?php
// src/AppBundle/Form/UserType.php
namespace AppBundle\Form;
// carico User.php
use AppBundle\Entity\User;
// carico i namespace per il creare il form
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
class UserType extends AbstractType // Estende la classe Symfony AbstractType{}
{
public function buildForm(FormBuilderInterface $builder, array $options) // metodo di AbstractType{}
{
$builder
->add('email', EmailType::class) // la casella email
->add('username', TextType::class) // la casella username
// la casella password e repeat password
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
}
}
5. Creiamo il controller per renderizzare il form a src/AppBundle/Controller/RegistrationController.php
<?php
// src/AppBundle/Controller/RegistrationController.php
namespace AppBundle\Controller;
use AppBundle\Form\UserType;// UserType.php
use AppBundle\Entity\User; // User.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; // @Route
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class RegistrationController extends Controller
{
/**
* @Route("/register", name="user_registration")
*/
public function registerAction(Request $request)
{
// 1) build the form
$user = new User(); // istanziamo l'oggetto di User.php
$form = $this->createForm(UserType::class, $user);
// 2) controlla se il bottone - submit - è stato premuto
$form->handleRequest($request);
// se l'oggetto user è submit AND validato
if ($form->isSubmitted() && $form->isValid()) {
// 3) Encode the password (you could also do this via Doctrine listener)
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
// 4) save the User!
$em = $this->getDoctrine()->getManager();
$em->persist($user);// prepara in cache i dati
$em->flush(); // scrivi i dati nel DB
// ... do any other work - like sending them an email, etc
// maybe set a "flash" success message for the user
return $this->redirect('http://symfony.com');
}
return $this->render(
'registration/register.html.twig',
array('form' => $form->createView())
);
}
}
6. Creiamo il Template app/Resources/views/registration/register.html.twig
{# app/Resources/views/registration/register.html.twig #}
{{ form_start(form) }}
{{ form_row(form.username) }}
{{ form_row(form.email) }}
{{ form_row(form.plainPassword.first) }}
{{ form_row(form.plainPassword.second) }}
<button type="submit">Register!</button>
{{ form_end(form) }}
7. Puntiamo il browser a: http://localhost/symfonytest/first_test_symfony/web/register
Il form in automatico restituirà un messaggio di errore all’utente se:
– username è già stato preso
– email è già stata presa
– password è già stata presa
– password e repeat password non sono uguali
Il mio sito ufficiale:
lucedigitale.com
Bibliografia:
symfony.com/doc/current/doctrine/registration_form.html