formations/developpement/symfony3/presentation/slides.md

13 KiB
Raw Blame History

Remise à niveau Symfony3

William Petit - S.C.O.P. Cadoles


Les principales nouveautés de Symfony 3

  • Simplification de l'authentification avec le système de "Guard"
  • Composant LDAP pour l'authentification (compatible Active Directory)
  • Amélioration des mécanismes d'injection de dépendances: "auto-wiring", services dépréciés...
  • Un "MicroKernel" pour créer des applications minimalistes basées sur Symfony3
  • Et beaucoup d'autres améliorations et composants mineurs...

Structure d'un projet, générateurs et "bundles"

Amorçage d'un projet

Notion de "bundle"

Structuration d'un projet

La console, les commandes et les générateurs


Amorçage d'un projet

Récupération de l'installeur Symfony

sudo mkdir -p /usr/local/bin
sudo curl -LsS https://symfony.com/installer \
	-o /usr/local/bin/symfony
sudo chmod a+x /usr/local/bin/symfony

Création du projet (avec la dernière LTS)

symfony new <my_project_name> 3.4

Notion de "bundle"

Un "bundle" est un ensemble de fichiers (code, templates, etc) représentant une unité fonctionnelle dans le framework Symfony.

Avant Symfony 3 Les "bundles" étaient le moyen utilisé pour structurer le code d'une application. Une application pouvait donc avoir plusieurs bundles "internes", i.e. ne provenant pas d'un développeur tiers.

Après Symfony 3 Les bundles ne sont plus considérés à titre organisationnel mais uniquement comme un moyen de diffuser le code sous forme d'unité réutilisable. Une application ne devrait donc plus qu'avoir un seul bundle interne: AppBundle.


Structuration d'un projet (1)

Les principaux répertoires et leur rôle

Répertoire Rôle
app/config/ Configuration de l'application
app/Resources/ Depuis Symfony3, répertoire contenant les vues ainsi les "assets" de l'application
src/AppBundle/ Sources de l'application

Structuration d'un projet (2)

Répertoire Rôle
tests/ Répertoire contenant les tests de l'application (unitaire comme fonctionnels)
var/ Répertoire contenant toutes les données "vivantes" de l'application
vendor/ Dépendances Composer
web/ Répertoire des ressources publiques de l'application

La console, les commandes et les générateurs (1)

Quelques commandes (très) utiles

Commande Description
server:run Exécuter l'application avec le serveur HTTP PHP
security:check Vérifier que les dépendances du projet ne comportent pas de vulnérabilités connues
debug:container Retrouver le mapping services <-> classe PHP
router:match Vérifier quelle controleur/action sera utilisée pour un chemin donné

La console, les commandes et les générateurs (2)

Les générateurs par défaut

Commande Description
generate:bundle Créer un nouveau bundle
generate:controller Créer un nouveau contrôleur
generate:command Créer une nouvelle commande
doctrine:generate:entity Créer une nouvelle entité Doctrine

Le routage et les contrôleurs

Création d'un nouveau contrôleur

Déclaration des contrôleurs

Actions, routes et verbes HTTP avec les annotations


Création d'un nouveau contrôleur

bin/console generate:controller

Déclaration des contrôleurs


Actions, routes et verbes HTTP avec les annotations (1)

Action annotée basique

class DemoController extends Controller
{
    /**
     * @Route("/demo")
     */
    public function demoAction()
    {
        // ...
    }

}

Actions, routes et verbes HTTP avec les annotations (2)

Action avec méthodes HTTP contraintes

class DemoController extends Controller
{
    /**
     * @Route(
     * 	"/demo", 
     * 	methods = { "POST", "PUT" }
     * )
     */
     public function demoAction()
    {
        // ...
    }
}

Actions, routes et verbes HTTP avec les annotations (3)

Action nommée

class DemoController extends Controller
{
    /**
     * @Route("/demo-named", name="my_demo_action")
     */
    public function demoAction()
    {
        // ...
    }
}

Actions, routes et verbes HTTP avec les annotations (4)

Action avec paramètres

class DemoController extends Controller
{
    /**
     * @Route("/demo/{myParam}")
     */
    public function demoAction($myParam)
    {
        // ...
    }
    
    /**
     * @Route("/demo/{paramWithDefaultVal}")
     */
    public function demoAction($paramWithDefaultVal = 1)
    {
        // ...
    }
}

Actions, routes et verbes HTTP avec les annotations (5)

Action avec paramètres contraints

class DemoController extends Controller
{
    /**
     * @Route(
     * 	"/demo/{myParam}", 
     * 	requirements = { "myParam"="^\d+$" }
     * )
     */
    public function demoAction($myParam)
    {
        // ...
    }
}

Actions, routes et verbes HTTP avec les annotations (6)

Action avec paramètres contraints

class DemoController extends Controller
{
    /**
     * @Route(
     * 	"/demo/{myParam}", 
     * 	requirements = { "myParam"="^\d+$" }
     * )
     */
    public function demoAction($myParam)
    {
        // ...
    }
}

Actions, routes et verbes HTTP avec les annotations (7)

Paramètres spéciaux

Paramètre Description
_locale La "locale" utilisé par l'application pour la requête en cours
_format Le "format" utilisé par l'application pour la requête en cours
_controller L'identifiant du contrôleur et son action utilisés pour traiter la requête en cours

Actions, routes et verbes HTTP avec les annotations (8)

Générer une URL pour une route nommée

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

$this->generateUrl(
  'demo', // Nom de la route
  ['myParam' => 'my-value'], // Paramètres à injecter
  // L'URL générée doit elle être absolue ou non ?
  UrlGeneratorInterface::ABSOLUTE_URL
);

Services

Utilisation des services

Création de nouveaux services


Utilisation des services (1)

Lister les services existants

bin/console debug:container

Utilisation des services (2)

Utiliser un service (méthode classique)

class DemoController extends Controller
{
    /**
     * @Route("/demo")
     */
    public function demoAction()
    {
        $logger = $this->get('logger');
        $logger->info('Hello world !');
    }
}

Utilisation des services (3)

Utiliser un service (Via le "type hint")

use Psr\Log\LoggerInterface;

class DemoController extends Controller
{
    /**
     * @Route("/demo")
     */
    public function demoAction(LoggerInterface $logger)
    {
        $logger->info('Hello world !');
    }
}

Tip Pour identifier les différentes interfaces disponibles, utilisez la commande bin/console debug:autowiring


Création d'un service (1)

Création de la classe PHP

namespace AppBundle\Service;

class MyService {
  public function doNothing() {}
}

Tip On peut vérifier que le service est bien détecté par l'application avec la commande bin/console debug:autowiring


Création d'un service (2)

Utilisation du service dans un contrôleur

use AppBundle\Service\MyService;

class DemoController extends Controller
{
    /**
     * @Route("/demo")
     */
    public function demoAction(MyService $my)
    {
        $my->doNothing();
    }
}

Création d'un service (3)

Déclaration de dépendances inter-services

namespace AppBundle\Service;

use Psr\Log\LoggerInterface;

class MyService {
  
  private $logger;
  
  public function __construct(LoggerInterface $logger) {
  	$this->logger = $logger;
  }

  public function log($message) {
    $this->logger->info($message);
  }
  
}

Création d'un service (4)

Déclaration de dépendances vers des valeurs de configuration

namespace AppBundle\Service;

class MyService {
  
  private $myCustomParameter;
  
  public function __construct($myCustomParameter) {
  	$this->myCustomParameter = $myCustomParameter;
  }
  
}

Création d'un service (5)

Déclaration explicite de la dépendance

Dans app/config/services.yml, déclarer la dépendance explicitement:

services:
    AppBundle\Service\MyService:
        arguments:
          $myCustomParameter: 'test'

Authentification et autorisation

Le fichier app/config/security.yml

Firewalls, providers et encoders

Gestion des rôles et contrôle des accès

Méthode d'authentification personnalisée


Le fichier app/config/security.yml

Authentifications et autorisations


Firewalls (1)

Déclarer une nouvelle méthode d'authentification dans un firewall

security:
  firewalls:
    main:
      anonymous: ~
      http_basic: ~

Firewalls (2)

Multiple firewalls

security:

 firewalls:
 
   authenticated_area:
     pattern: ^/secured
     http_basic: ~
     
   public_area:
     anonymous: ~
     

Providers (1)

Utilisation du provider memory

security:

  providers:
    in_memory:
      memory:
        users:
          bob:
            password: bob
            roles: 'ROLE_USER'
          admin:
            password: 123456
            roles: 'ROLE_ADMIN'
     

Providers (2)

Déclaration de l'encoder pour la gestion des mots de passe

security:
  encoders: 
    Symfony\Component\Security\Core\User\User: plaintext

Providers (3)

Utilisation d'un encoder apportant un meilleur niveau de sécurité

security:
  encoders: 
    Symfony\Component\Security\Core\User\User:
      algorithm: bcrypt
      cost: 12

Calcul de l'empreinte d'un mot de passe

bin/console security:encode-password

Gestion des rôles et contrôle des accès (1)

Définition de rôles

security:
  access_control:
    - { path: ^/admin, roles: ROLE_ADMIN }
    - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Gestion des rôles et contrôle des accès (2)

Restriction d'accès par adresse IP

security:
  access_control:
    - path: ^/local-api
      roles: ROLE_API_USER
      ips:  [127.0.0.1]

Gestion des rôles et contrôle des accès (3)

Définition de règles d'accès pour les actions

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

class DemoController extends Controller
{
    /**
     * @Route("/demo")
     * @Security("has_role('ROLE_ADMIN')")
     */
    public function demoAction()
    {
        // ...
    }

}

Méthode d'authentification personnalisée

Exercice

Implémenter une classe Guard pour créer un firewall pour un serveur d'authentification "passwordless" basé sur JWT.

Ressources


Les vues et le moteur de templating Twig

Organisation des vues

Syntaxe Twig

Héritage et composition

Étendre Twig


Organisation des vues dans le projet


Syntaxe Twig

<html>
  <body>
    <p>{{ myVar }}</p>
    <ul>
      {% for i in items %}
      <li>{{ i.text }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

Héritage et composition (1)

Héritage

// parent.html.twig
<html>
	<body>
    	{% block body %}
        <div>
        	Default content
        </div>
        {% endblock %}
    </body>
</html>
// child.html.twig
{% extends 'parent.html.twig' %}
{% block body %}
	<div>
    	Child content
    </div>
{% endblock %}

Héritage et composition (21)

Composition

// layout.html.twig
<html>
  <body>
    {% for i in items %}
    	{{ include('_partial.html.twig', { 'item': i }) }}
    {% endfor %}
  </body>
</html>
// _partial.html.twig
<div>
	Item #{{ item.id }}
</div>

Étendre Twig


Les formulaires

Création et traitement de formulaires

Validation des données

Les évènements


L'ORM Doctrine et le modèle de données

Concept d'ORM

Entité et Dépôt

Les évènements


Mise en production

Gestion des environnements

Cache applicatif


Licence

CC BY-NC-SA 3.0 FR

Creative Commons - Attribution - Pas dUtilisation Commerciale - Partage dans les Mêmes Conditions 3.0 France