Web Design

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

PHP Lezione Avanzata – Classi Astratte

Classi Astratte

L’Astrazione dei Dati viene utilizzata per gestire al meglio la complessità di un programma, ovvero viene applicata per decomporre sistemi software complessi in componenti più piccoli e semplici.

Creando un esempio semplice.

Class: Animale
Metodi: mangia() respira() peso() altezza() e tutto quello che può caratterizzare un animale

Bisognerebbe però definire le singole istanze $cane = new Animale(); $gatto = new Animale(); etc…
In questo caso non possiamo creare infinite istanze per ogni tipo di animale, quindi Animale è una Classe Astratta, cioè una classe che non può essere istanziata. L’unica cosa a cui ci può servire è per ottenere delle classi derivate più specifiche.

La struttura sarà la seguente:

Class: Animale
1/2 Metodi + Motodi Astratti che sono solo una definizione del metodo stesso

Class: Cane
fornisce obbligatoriamente l’implementazione dei Metodi Astratti

Class: Gatto
fornisce obbligatoriamente l’implementazione dei Metodi Astratti

etc…

In PHP utilizziamo la keyword ‘abstract’

LA CLASSE ASTRATTA


// Team A - Class Product
abstract class Product {
 
    // proprietà pubbliche
    public $name;
    public $price;
 
    // metodi pubblici
    public function __construct($name, $price) {
        $this->name = $name;
        $this->price = $price;   
    }
     
    public function getName() {
        return $this->name;
    }
     
    public function getPrice() {
        return $this->price;
    }
 
    // metodi astratti contengono SOLO delle definizioni
    abstract public function buyProduct($pid, $quantity);
    abstract public function sellProduct($pid, $quantity);
 
}

LE CLASSI CHE ESTENDONO LA CLASSE ATRATTA


// Team B - Class InProduct
class InProduct extends Product {
 
    public function buyProduct($pid, $quantity) {
     
        // connettiamoci al nostro database locale per ottenere i prodotti che dobbiamo comprare con la quanità indicata...
        $prods = connect_to_my_local_db($db->login(), $quantity);
        $prods->sendmail("buy");
         
        // ...
     
    }
     
    public function sellProduct($pid, $quantity) {
     
        // connettiamoci al nostro database locale per ottenere i prodotti che dobbiamo vendere con la quanità indicata...
        $prods = connect_to_my_local_db($db->login(), $quantity);
        $prods->sendmail("sell");
         
        // ...
     
    }
 
}
 
 
// Team C - Class OutProduct
class OutProduct extends Product {
 
    public function buyProduct($pid, $quantity) {
     
        // connettiamoci al database del partner per ottenere i prodotti che dobbiamo comprare con la quanità indicata...
        $prods = connect_to_partner_db($db->login(), $quantity);
        $prods->sendmail("buy");
         
        // ...
     
    }
     
    public function sellProduct($pid, $quantity) {
     
        // connettiamoci al database del partner per ottenere i prodotti che dobbiamo vendere con la quanità indicata...
        $prods = connect_to_partner_db($db->login(), $quantity);
        $prods->sendmail("sell");
         
        // ...
     
    }
 
}

Ciò che è importante capire, è che nonostante le classi InProduct ed OutProduct siano create da diversi team di sviluppo, esse assumono una forma comune, coerente con quella indicata dalla classe astratta Product.

Se volessi creare un’API:


$pen = new InProduct("penna", 3.50);
$pencil = new OutProduct("matita", 3.50);
 
// vendiamo la penna
$pen->sellProduct(1102, 1);
 
// compriamo una matita
$pencil->buyProduct(3498, 1);

Come funziona?
1. $pen = new InProduct(“penna”, 3.50); – creo un’istanza di class InProduct extends Product {

2. $pen->sellProduct(1102, 1); – invoca public function sellProduct($pid, $quantity)

3. $pid, $quantity è comune a – function sellProduct($pid, $quantity) – che risalendo estende – extends Product – che risalendo definisce – abstract public function sellProduct($pid, $quantity);

4. idem per – buyProduct($pid, $quantity) – che deve avere ($pid, $quantity) – perchè così indicato da – abstract public function buyProduct($pid, $quantity);

Il mio sito ufficiale:
www.lucedigitale.com

Bigliografia:
www.html.it/pag/18355/utilizzare-le-classi-astratte/

By |PHP, Web Design|Commenti disabilitati su PHP Lezione Avanzata – Classi Astratte

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