symfony

Install Symfon y1.4.20 on WAMP and WIN7 with PEAR

PEAR installation

PEAR is a framework and distribution system for reusable PHP components.

0. Start WAMP

1. Download PEAR at http://pear.php.net/go-pear.phar

The phar extension provides a way to put entire PHP applications into a single file called a “phar” (PHP Archive) for easy distribution and installation.

2. Copy go-pear.phar into C:\wamp64\bin\php\php5.6.25

3. On Win Button type cmd.exe, RMB ‘Run as Administrator’ and navigate to C:\wamp64\bin\php\php5.6.25

4. Input in the command window: php go-pear.phar
– Are you installing a system-wide PEAR or a local copy?: leave default (system:loca) [system]
– Below is suggested file layout… (locazioni proposte per l’installazione): ENTER to go on
– Would you like to later php.ini?: Y (includerà il path per il pear)
– Enter to continue: ENTER

5. C:\wamp64\bin\php\php5.6.25 double click Pear_ENV.reg (aggiunta al registro di sistema)

PEAR is installed

Nota: per vedere il registo di sistema cliccare sul bottone di windows, in cerca programmi e file scrivere ‘regedit’

Environment variables

1. Bottone di Windows> RMB Computer> Proprietà> Impostazioni di sistema avanzate> Avanzate> Variabili d’ambiente> Variabili di sistema> Variabile Path> Modifica…>aggiungere il separatore ; e C:\wamp64\bin\php\php5.6.25

Symfony 1.4

1. On Win Button type cmd.exe, RMB ‘Run as Administrator’ and navigate to C:\wamp64\bin\php\php5.6.25

2. Type the command: pear channel-discover pear.symfony-project.com (aggiunge a PEAR il canale di Symfony)

3. Type the command: pear install symfony/symfony (scarica symfony-1.4.20.tgz)

At the end you will have the message install ok: …

4. Type: C:\wamp64\bin\php\php5.6.25\symfony -V (message: symfony version 1.4.20), l’installazione è verificata
NB: il comando symfony -V è visto da tutte le cartelle perchè il percorso è registrato

Project Creation

0. We want to create a project named ‘jobeet’

1. Create the folder c:\wamp64\www\jobeet

NB: Windows users are advised to run symfony and to setup their new project in a path which contains no spaces

2. Inside \jobeet folder run the command:
cmd.exe -> c:\wamp64\www\jobeet\symfony generate:project jobeet –orm=Propel

Symfony will create the folder structure:

apps/ Hosts all project applications -> al momento è vuota
cache/ The files cached by the framework
config/ The project configuration files
lib/ The project libraries and classes
log/ The framework log files
plugins/ The installed plugins
test/ The unit and functional test files
web/ The web root directory

NB: The generate:project task has also created a symfony shortcut in the project root directory to shorten the number of characters you have to write when running a task.

App Creation

1. Copy C:\wamp64\bin\php\symfony.bat to c:\wamp64\www\jobeet\

2. cmd.exe -> c:\wamp64\www\jobeet\symfony generate:app frontend

Symfony will create the folder structure:

apps/config
apps/i18n
apps/lib -> libraries
apps/modules -> code MVC
apps/templates

3. Set the write permission for jobeet\cache – jobeet\log – chmod 777, permit read,write,execute

RMB sulla cartella cache> Proprietà> Condivisione> Condivisione avanzata> Condividi la cartella> Autorizzazioni> Consenti> attivare Controllo completo, Modifia, Lettura

RMB sulla cartella log> Proprietà> Condivisione> Condivisione avanzata> Condividi la cartella> Autorizzazioni> Consenti> attivare Controllo completo, Modifia, Lettura

4. Set Apache
LMB over WAMP Icon> Apache> httpd.conf

Copy and paste in the end:

# -------------------------------------------------------------------------------
# Project jobeet START

# Be sure to only have this line once in your configuration
NameVirtualHost 127.0.0.1:8080

# This is the configuration for your project
Listen 127.0.0.1:8080

# 127.0.0.1:8080 da browser punta a c:\wamp64\www\jobeet\web
<VirtualHost 127.0.0.1:8080>
  DocumentRoot "c:\wamp64\www\jobeet\web"
  DirectoryIndex index.php
  <Directory "c:\wamp64\www\jobeet\web">
    AllowOverride All
    Allow from All
  </Directory>

  Alias /sf "c:\wamp64\www\jobeet\lib\vendor\symfony\data\web\sf"
  <Directory "c:\wamp64\www\jobeet\lib\vendor\symfony\data\web\sf">
    AllowOverride All
    Allow from All
  </Directory>
</VirtualHost>

# Project jobeet END
# -------------------------------------------------------------------------------

NB: The /sf alias gives you access to images and javascript files needed to properly display default symfony pages and the web debug toolbar|Web Debug Toolbar.

LMB over WAMP Icon> Restart All services

5. Open the browser and point it at: 127.0.0.1:8080 or at http://localhost/jobeet/web/index.php

If you do not see images you have to correct the code addet in httpd.conf.

Reference:
http://symfony.blog.gogo.mn/read/entry350425
https://blog.rolandl.fr/59-installer-symfony-1-4-avec-pear
http://symfony.com/legacy
http://symfony.com/legacy/doc

By |PHP, Symfony, Web Design|Commenti disabilitati su Install Symfon y1.4.20 on WAMP and WIN7 with PEAR

MVC – Programmazione Model View Controller

Symfony è basato sul modello di programmazione MVC. A differenza della programmazione classica dove potremo in una sola pagina programmare tutta la nostra applicazione nel modello MVC dividiamo il codice in Model (modello), View (vista), Controller (controllore).

Programmazione classica in un’unico script:

– difficile da mantenere perchè di difficile lettura
– impossibile da affidare a tecnici che non abbiano la conoscenza di tutti i linguaggi utilizzati


<?php
 
// Connessione e selezione del database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
 
// Esecuzione query SQL
$result = mysql_query('SELECT date, title FROM post', $link);
 
?>
 
<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>
 
<?php
 
// Chiusura connessione
mysql_close($link);
 
?>

Pattern MVC

Per semplicità in questi esempi utilizziamo un paradigma di programmazione procedurate senza usare la programmazione orientata agli oggetti (OOP) con classi, metodi e proprietà.

model.php solo manipolazione dei dati


function getAllPosts()
{
  // Connessione al database
  $link = open_connection('localhost', 'myuser', 'mypassword');
 
  // Esecuzione query SQL
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);
 
  // Popolamento dell'array
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }
 
  // Chiusura connessione
  close_connection($link);
 
  return $posts;
} 

astrazione del database per separare le query di connessione.
Si potrebbero scrivere diverse query in base al tipo di DB (MySQL, PostgreSQL) senza modificare model.php


<?php
 
function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}
 
function close_connection($link)
{
  mysql_close($link);
}
 
function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);
 
  return mysql_query($query, $link);
}
 
function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

view.php solo vista HTML


<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

index.php il controllore, gestisce la logica business, cioè preleva i dati dal model, li manipola, scrive nella view.


<?php
 
// Richiesta del modello
require_once('model.php');
 
// Recupero della lista dei post
$posts = getAllPosts();
 
// Richeista della vista
require('view.php');

La vista può essere suddivisa in:

– Layout


<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php include('mytemplate.php'); ?>
  </body>
</html>

– Template


<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

In Symfony la struttira MVC sarà la seguente:

# Model layer
– Astrazione del Database
– Accesso ai Dati

# View layer
– Layout disposizione (tag comuni html, head, body, footer) in Symfony app\Resources\views\base.html.twig
– Template modello (codice specifico) in Symfony app\Resources\views\default

# Controller
– Front controller (è unico in tutta l’applicazione, sicurezza, configurazione) in Symfony web\app.php e app_dev.php
– Action (azioni specifiche di quella pagina), in Symfony src\AppBundle\Controller\ilcontrollerfile.php -> nomefunzioneAction()

Bibliografia:
symfony.com/legacy/doc/gentle-introduction/1_4/it/01-introducing-symfony

By |PHP, Symfony, Web Design|Commenti disabilitati su MVC – Programmazione Model View Controller

Symfony YAML vs XML JSON

YAML è una notazione nata nel 2001 per la serializzazione standard dei dati come XML ed è la notazione pricipale per Symfony.
YAML significa ‘YAML Ain’t Markup Language’ cioè è una notazione senza marker, infatti a differenza di XML non presenta marker, ma la struttura dei dati è mostrata tramite l’indentazione (l’utilizzo degli spazi e dell’andare a capo). Vediamo di seguito degli esempi pratici.

OGGETTO PHP


$house = array(
  'family' => array(
    'name'     => 'Doe',
    'parents'  => array('John', 'Jane'),
    'children' => array('Paul', 'Mark', 'Simone')
  ),
  'address' => array(
    'number'   => 34,
    'street'   => 'Main Street',
    'city'     => 'Nowheretown',
    'zipcode'  => '12345'
  )
);

YAML sintassi lunga


house:
  family:
    name:     Doe
    parents:
      - John
      - Jane
    children:
      - Paul
      - Mark
      - Simone
  address:
    number: 34
    street: Main Street
    city: Nowheretown
    zipcode: "12345"

YAML sentassi breve


house:
  family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] }
  address: { number: 34, street: Main Street, city: Nowheretown, zipcode: "12345" }

Notare per gli array [], per gli hash {}

Un altro esempio comparativo YAMS XML JSON

XML


<people>
    <person>
        <firstName>Charles</firstName> <lastName>Schulz</lastName>
    </person>
    <person>
        <firstName>Walt</firstName> <middleName>Elias</middleName>
        <lastName>Disney</lastName>
    </person>
    <person>
        <firstName>Gary</firstName> <lastName>Larson</lastName>
    </person>
</people>

JSON


{
  “people”: [
    { “person”: {
         “firstName”: “Charles”,
         “lastName”: “Schulz”
         }
    },
    { “person”: {
         “firstName”: “Walt”,
         “middleName”: “Elias”,
         “lastName”: “Disney”
         }
    },
    { “person”: {
         “firstName”: “Gary”,
         “lastName”: “Larson”
         }
    }
  ]
}

YAML


people: 
  - 
    person: {firstName: Charles, lastName: Schulz }
  - 
    person:
       firstName: Walt
       middleName: Elias
       lastName: Disney
       characters: [ Mickey, Donald, Goofy ] 
  - 
    person:
       firstName: Gary
       lastName: Larson

By |PHP, Symfony, Web Design|Commenti disabilitati su Symfony YAML vs XML JSON

Symfony – Translations and Internationalization

Internationalization è un termine informatico che indica il processo di design di un software atto a renderlo adattabile a linguaggi e regioni differenti. Internationalization può essere abbreviato con la sigla ‘i18n’. Per approfondire vedi en.wikipedia.org/wiki/Internationalization_and_localization.

Symfony ha dei componenti studiati apposta per la traduzione dei testi utilizzando delle traduzioni pre-impostate.

Translator Service – PHP array

1. Creiamo il seguente Controller in src/AppBundle/Controller/Traduttore.php


<?php

// src/AppBundle/Controller/Traduttore.php
namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;// namespace per response()
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;// NECESSARIO per utilizzare @Route

use Symfony\Component\Translation\Translator; // per Translator()
use Symfony\Component\Translation\Loader\ArrayLoader; // per ArrayLoader()

class Traduttore {
    
    /**
    * @Route("/traduci")
    */
    public function translationAction()
   {
        $translator = new Translator('fr_FR'); // istanzia la classe Translator di Symfony
        $translator->addLoader('array', new ArrayLoader()); // crea l'array con le traduzioni
        $translator->addResource('array', array(
                                               'Symfony is great!' => 'J\'aime Symfony!',
                                               ), 'fr_FR'); // francese

        //                  ->traduci recuperando i dati array chiave-=valore
        var_dump($translator->trans('Symfony is great!'));// renderizza J'aime Symfony
   
    return new Response('FINE TRADUZIONE');
   }
    
}// END class

Puntare il browser su http://localhost/symfonytest/first_test_symfony/web/traduci

Renderizza:

C:\wamp64\www\symfonytest\first_test_symfony\src\AppBundle\Controller\Traduttore.php:25:string ‘J’aime Symfony!’ (length=15)
FINE TRADUZIONE

Il mio sito ufficiale:
lucedigitale.com

Bibliografia:
symfony.com/doc/current/translation.html

By |PHP, Symfony, Web Design|Commenti disabilitati su Symfony – Translations and Internationalization

Symfony – Simple Registration Form – Doctrine

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

By |MySQL, PHP, Symfony, Web Design|Commenti disabilitati su Symfony – Simple Registration Form – Doctrine