Initiation au framework le plus hypé pour PHP

 

Sommaire

  • Présentation
  • Fonctionnement
  • Documentations
  • Pense-bête

Symfony

Symfony est un framework PHP créé par des Français (SensioLabs, Cocorico !) qui est un des plus populaire sur le langage PHP. 

 

Il est Open Source et bénéficie d'une très grande communauté.

Un framework

Comme tous les Framework il offre un grand nombre d'outils et un cadre de travail pour la création de sites et d'applications web :

  • Gestion du MVC
  • ORM
  • Gestion des assets (css et js)
  • Twig (templates)
  • Routeur
  • ...

Modularité

Avec Symfony, on travaille sur des Bundles qui sont des morceaux d'application (des modules)

 

Chaque bundle apporte ses propres fonctionnalités

Console

Comme beaucoup de Framework actuellement, le parti pris est d'assister le plus possible les développeurs à l'aide d'outils console qui génèrent du code automatiquement

 

On peut bien entendu tout coder à la main ;-)

Structure

  • app : contient les configurations et les vues (templates)
  • src : contient le code PHP (Controllers, Models, autres)
  • bin : contient les utilitaires Symfony (outils console)
  • web : contient les fichiers statiques (images, css, js, etc)
  • ...

Les controllers

Le contrôleur permet d'écouter la requête (Request) d'un utilisateur et afin de créer et de renvoyer une réponse (Response)

 

La réponse peut être une vue HTML, JSON, XML, le téléchargement d'un fichier, une redirection, etc

Suivant le pattern MVC, votre contrôleur peut avoir recours à la base de données afin de lire ou d'écrire des données avant de renvoyer sa réponse.

 

Routage : dans Symfony, on peut assigner une route (URL) à une action du contrôleur

Un contrôleur simple



namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class LuckyController
{
    /**
     * @Route("/lucky/number/{max}")
     */
    public function numberAction($max)
    {
        $number = mt_rand(0, $max);

        return new Response(
            '<html><body>Lucky number: '.$number.'</body></html>'
        );
    }
}

Les entités (ou models)

Dans Symfony, ce qu'on appelle habituellement les models deviennent des entities (entités)

 

Une entité représente habituellement une table de la base de données (Produit, Article, Commentaire, etc)

Grâce à Doctrine, Symfony nous permet de faire le lien entre les entités (fichiers de code) et la base de données de façon "automatique"

 

Doctrine est ce qu'on appelle un ORM - Object Relational Mapping

Une entité

// Une entité simple, sans mapping
// relationnel 

namespace AppBundle\Entity;

class Product
{
    private $name;
    private $price;
}
// Une entité avec mapping relationnel

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $name;

    /**
     * @ORM\Column(type="decimal", scale=2)
     */
    private $price;
}

Doctrine : l'ORM

Symfony utilise la librairie Open Source Doctrine afin de gérer la persistance des données

 

C'est un ORM qui permet de manipuler les données de l'application sans "écrire de SQL", on ne fait qu'utiliser des objets

On peut donc manipuler les entités de Symfony pour accéder aux données  :

  • Une Entity représente une ligne d'une table
  • Le Repository représente la table elle-même
  • On peut représenter les relations entre tables (OneToMany, ManyToOne, ManyToMany, etc)

Les templates (views)

Symfony utilise la librairie Open Source Twig afin de gérer les templates (gabarits, ou vues) et de produire des affichages HTML

 

Le contrôleur peut donc générer des vues à l'aide de fichiers appelés "templates" qui sont écrits avec la syntaxe Twig

Twig a été créé pour palier à certaines limites de la syntaxe de templating classique de PHP

 

La syntaxe Twig permet de faire des boucles, des conditions, et bien plus

Un template


<!-- TEMPLATE CLASSIQUE -->


<h1><?php echo $page_title ?></h1>

<ul id="navigation">
    <?php foreach ($navigation as $item): ?>
        <li>
            <a href="<?php echo $item->getHref() ?>">
                <?php echo $item->getCaption() ?>
            </a>
        </li>
    <?php endforeach ?>
</ul>

<!-- UN TEMPLATE TWIG -->


<h1>{{ page_title }}</h1>

<ul id="navigation">
    {% for item in navigation %}
        <li>
            <a href="{{ item.href }}">
                {{ item.caption }}
            </a>
        </li>
    {% endfor %}
</ul>

Routage

Symfony offre un système de routage qui permet de gérer facilement la ré-écriture d'urls

 

L'idée est de fournir un outil qui permette facilement d'avoir de belles URL et d'indiquer quelles actions elles visent

Le Routeur de Symfony permet  de :

  • Créer des URLs complexes qui mènent vers les contrôleurs
  • Générer ces urls au sein des templates et des contrôleurs

Routage du contrôleur


namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class BlogController extends Controller
{
    /**
     * @Route("/blog", name="blog_list")
     */
    public function listAction()
    {
        // ...
    }

    /**
     * @Route("/blog/{slug}", name="blog_show")
     */
    public function showAction($slug)
    {
        // $slug will equal the dynamic part of the URL
        // e.g. at /blog/yay-routing, then $slug='yay-routing'

        // ...
    }
}

Annotations

Plusieurs langages de programmations utilisent les annotations qui permettent de spécifier des méta-données (informations supplémentaires) concernant des variables, fonctions, classes, etc

Symfony utilise les annotations de PHP dans pas mal de situations :

  • Pour préciser la route qui permet d'accéder à une fonction d'un contrôleur
  • Pour préciser le "mapping" entre les données d'une table et d'une entité
  • ...
/**
 * @Route("/blog", name="blog_list")
 */
public function listAction()
{
    // ...
}

A vous maintenant !

Ca va aller !

Documentations

Pense-bête : contrôleur


// A écrire dans src/AppBundle/Controller/LuckyController.php

namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class LuckyController
{
    /**
     * @Route("/lucky/number/{max}")
     */
    public function numberAction($max)
    {
        $number = mt_rand(0, $max);

        return new Response(
            '<html><body>Lucky number: '.$number.'</body></html>'
        );
    }
}

Pense-bête : afficher un template


// A la fin d'une fonction du controller, on peut générer un affichage
// à partir d'un template

// génère le template qui se trouve dans 
// app/Resources/views/lottery/lucky/number.html.twig
// en lui passant la donnée "name"

return $this->render('lottery/lucky/number.html.twig', array(
    'name' => $name,
));

Pense-bête : redirections

// Différentes façon de faire une redirection dans un controller

public function indexAction()
{
    // redirect to the "homepage" route
    return $this->redirectToRoute('homepage');

    // do a permanent - 301 redirect
    return $this->redirectToRoute('homepage', array(), 301);

    // redirect to a route with parameters
    return $this->redirectToRoute('blog_show', array('slug' => 'my-page'));

    // redirect externally
    return $this->redirect('http://symfony.com/doc');
}

Pense-bête : Entité

// A écrire dans src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $name;

    /**
     * @ORM\Column(type="decimal", scale=2)
     */
    private $price;
}

Pense-bête : Relations

// Un produit est lié à une catégorie
// A écrire dans src/AppBundle/Entity/Product.php
{

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     */
    private $category;
}



// Une catégorie est liée à plusieurs produits
// A écrire dans src/AppBundle/Entity/Category.php
use Doctrine\Common\Collections\ArrayCollection;

class Category
{
    /**
     * @ORM\OneToMany(targetEntity="Product", mappedBy="category")
     */
    private $products;

    public function __construct()
    {
        $this->products = new ArrayCollection();
    }
}

Pense-bête : Persistance

// A écrire danssrc/AppBundle/Controller/DefaultController.php

// ...
use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;

// ...
public function createAction()
{
    $product = new Product();
    $product->setName('Keyboard');
    $product->setPrice(19.99);
    $product->setDescription('Ergonomic and stylish!');

    $em = $this->getDoctrine()->getManager();

    // dit à Doctrine qu'on va vouloir sauvegarder ce produit (mais ne le fait pas encore)
    $em->persist($product);

    // déclenche l'enregistrement i.e. the INSERT query)
    $em->flush();

    return new Response('Saved new product with id '.$product->getId());
}

Pense-bête : Persistance



// A écrire danssrc/AppBundle/Controller/DefaultController.php

public function showAction($productId)
{
    // On veut récupérer le produit qui a l'identifiant $productId
    $product = $this->getDoctrine()
        ->getRepository('AppBundle:Product')
        ->find($productId);

    if (!$product) {
        throw $this->createNotFoundException(
            'No product found for id '.$productId
        );
    }

    // ... do something, like pass the $product object into a template
}

Pense-bête : Persistance


// Certaines choses qu'on peut faire avec le repository

$repository = $this->getDoctrine()->getRepository('AppBundle:Product');

// query for a single product by its primary key (usually "id")
$product = $repository->find($productId);

// dynamic method names to find a single product based on a column value
$product = $repository->findOneById($productId);
$product = $repository->findOneByName('Keyboard');

// dynamic method names to find a group of products based on a column value
$products = $repository->findByPrice(19.99);

// find *all* products
$products = $repository->findAll();

// query for a single product matching the given name and price
$product = $repository->findOneBy(
    array('name' => 'Keyboard', 'price' => 19.99)
);

// query for multiple products matching the given name, ordered by price
$products = $repository->findBy(
    array('name' => 'Keyboard'),
    array('price' => 'ASC')
);

Pense-bête : console


# Créer un projet Symfony grâce à Composer 
composer create-project symfony/framework-standard-edition mon-projet

# Lancer le serveur web pour le projet
cd mon-projet
php bin/console server:run

# Générer un nouveau bundle
php bin/console generate:bundle

# Générer une entité Doctrine
php bin/console doctrine:generate:entity

# Générer les getters et setters pour une entité existante (Product)
php bin/console doctrine:generate:entities AppBundle/Entity/Product

# Demander à Doctrine de créer/modifier les tables correspondantes
php bin/console doctrine:schema:update --force

Made with Slides.com