Symfony Cheat Sheet

Symfony 5, Doctrine, forms, twig, authorization

Symfony General

Création d'un contrôleur en lien avec la base

// src/Controller/ProductController.php
namespace App\Controller;

// ...
use App\Entity\Product;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ProductController extends AbstractController
{
    #[Route('/product', name: 'create_product')]
    public function createProduct(ManagerRegistry $doctrine): Response
    {
        $entityManager = $doctrine->getManager();

        $product = new Product();
        $product->setName('Keyboard');
        $product->setPrice(1999);
        $product->setDescription('Ergonomic and stylish!');

        // tell Doctrine you want to (eventually) save the Product (no queries yet)
        $entityManager->persist($product);

        // actually executes the queries (i.e. the INSERT query)
        $entityManager->flush();

        return new Response('Saved new product with id '.$product->getId());
    }
}
Tester le contrôleur avec : http://localhost:8000/product
Vérifier la base de donnée : php bin/console dbal:run-sql 'SELECT * FROM product'
https://symfony.com/doc/current/doctrine.html#migrations-creating-the-database-tables-schema

Option pour les routes

name="routeName"
Donner un nom à une Route
methods={"method1", "method2"}
retreindre l'accès au requête HTTP de type
Type de requête HTTP
GET,POST,DEL,PUT,HEAD etc
host="nom ou IP du host"
retreindre l'accès à un host en particulier
requirements={"param1":"regexp1", "param2":"regexp2"}
appliquer des règles aux paramètres
defaults={"param1":"default1", "param2":"default2"}
Valeur par defaults des paramètres

Création route annotation

<?php 
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class NomDuControleurController {

  /**
   * @Route(
   *      "/chemin_de_url/{param1}}", 
   *      name="nom_de_la_route", 
   *      methods={"GET", "POST","HEAD","PUT",etc}, 
   *      requirements={"param1":"regexpVoulue" }, 
   *      defaults={"param1":"ValeurParDefaut"}
   *      host="my.host"
   *      schemes={"http", "https"}
   *      priority=1
   * )
   */
  public function nomTraitement($param1 ): Response {
     // ...

     return $this->render('nom_rep_template/nom_du_template.html.twig', [
            'nomDuParametre' => $valeur_du_parametre,
            'param1' $param1,
        ]);
  }
}

Usage des annotations pour les routes

Utiliser le package annotation
composer require annotations

Composer require

Liste des packages dispo sur composer
symfony/orm-pack
Installe Doctrine
--dev symfony/maker-bundle
Installe le Maker
symfony/validator
Les contraintes de validation pour Forms et DB
symfony/http-foundation
installe session
symfony/form
Installe les formulaires

php

-m
liste les modules php installés

Exemples de déclaration de route

#[Route('/api/posts/{id}', methods: ['GET', 'HEAD'])]

#[Route( '/contact', name: 'contact',
condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'",
// expressions can also include config parameters:
// condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
)]

#[Route(
'/posts/{id}',
name: 'post_show',
// expressions can retrieve route parameter values using the "params" variable
condition: "params['id'] < 1000"
)]

// Controller (using an alias):
#[Route( '/posts/{id}', name: 'post_show',
condition: "service('route_checker').check(request)")]

use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService;
use Symfony\Component\HttpFoundation\Request;

#[AsRoutingConditionService(alias: 'route_checker')]
class RouteChecker
{
  public function check(Request $request): bool
  {
// ...
  }
}

Les vues

Installation
composer require twig
Les vues = Twig sous symfony

Contrôleur

Créer un contrôleur
php bin/console make:controller nom_du_controleur
 
symfony console make:controller nom_du_controleur

php bin/console

server:dump
??
server:log
list
liste les commandes de console
list make
liste les commandes du maker

symfony console

list
donne la liste de toutes les commandes
liste make
donne la liste des commandes du maker
make:controller --help
donne la liste des options de la commande
doctrine:database:create
Crée la db déclarée dans .env
d:d:c
Crée la db déclarée dans .env
make:user
Crée la table user associée à l'authentification
make:entity
Crée une table
make:entity --regenerate
Généré les méthodes getter et setter manquantes
make:entity --overwrite
Regénére toutes les méthodes getter et setter
make:migration
Génère les fichiers de migration
doctrine:migrations:migrate
Exécute les fichiers de migration
m:mig
Génère les fichiers de migration
d:m:m
Exécute les fichiers de migration
doctrine:migrations:diff
Génère un fichier de migration en comparant le schéma interne et le schéma de la base
debug:autowiring
Liste les classes autochargeable
debug:container
Liste les services disponibles pour l'application
dbal:run-sql 'SELECT * FROM product'
interroge la base de données
Les fichiers de migrations sont nommés migrations\Versiondateheure.php et ils contiennent les ordres sql qui vont modifier la base

Symfony

server:start --no-tls
lance le serveur en http
server:ca:install
pour un serveur en https
server:start -d
en background
server:stop
stop le serveur (ctr c)
console about
information version du projet
check:requirements
valide l'environnement

Liste des routes du projet

liste toutes les routes
php bin/console debug:router
liste une route
php bin/console debug:router <nom de la route>
liste l'enchainement des fonctions utilisées
php bin/console router:match <nom de la route>

Création d'une route par configuration

Ajouter dans le fichier config/routes.yaml
nom_de_la_route:
  path: /inscription ou / ou /machin/truc etc (url)
  controller: App\Controller\nom_du_controleurController::nom_de_methode

Création inscription et connexion

 
symfony console
connexion (login)
make:auth
Inscription (register)
make:registration-form
 
make:user
Créer les formulaires pour l'inscription et la connexion des utilisateurs ainsi que le contrôleur user adapté

Création application : symfony new

LTS web
my_project --version=lts --webapp
Version 6.2 web
my_project --version="6.2.*" --webapp
LTS
my_project --version=lts
La dernière version
my_project
la demo
symfony new demo –demo
LTS signifie Long Time Support soit version supportée sur le long terme.

Maker

composer require --dev symfony/maker-bundle
Installer maker pour simplifier les
php bin/console list make
Liste des commandes
make:controller
Faire un nouveau controller
make:entity
Faire une nouvelle entité (pour Doctrine)

Test

composer require annotations
Installer le système d'annotations pour les routes
#[Route('/')]
Annotation à mettre au dessus de la fonction pour sa route. Exemple ici : route pour la homepage (/)
composer require profiler --dev
Installer la débug bar
composer require twig
Installer Twig

Infos de base

symfony new XXX
Créer un projet dans le répertoire courant, qui sera dans un sous dossier XXX
composer create-project symfony/skeleton XXX
Même chose mais en utilisant composer
symfony serve
Démarrer le server
CTRL+C
Arrêter le serveur
Adresse de base du projet

Objets utiles

Request
Représentation « orientée objet » du message HTTP Request
Response
Contient toutes les informations qui doivent être renvoyées au client à partir d'une demande donnée.
Fixture
Permet de seeder (injecter des données) en base de données

Doctrine

doctrine:database:create
Créer la base de donnée si elle n'est pas créée
doctrine:database:drop
Drop la base de données.
doctrine:migrations:diff
Génère une migration en comparant la BDD aux migrations actuelles.
doctrine:schema:create
Execute le SQL pour générer les tables de la BDD
doctrine:schema:update
Execute le SQL pour mettre à jour les tables de la BDD
doctrine:schema:drop
Drop les tables de la BDD
doctrine:fixtures:load
Exécutes les fixtures (seeders)

Définitions

Autowiring
il s'agit de « Définition automatique des dépendances des services » gérer automatiquement par le Framework
Injection de dépendances automatique
= autowiring
MVC
Modele - Vue - Controller | Architecture de projet de développement
Modele
Élément qui contient les données ainsi que de la logique en rapport avec les données : validation, lecture et enregistrement. Bien souvent la BDD
Vue
La présentation de l'interface graphique visible par l'utilisateur.
Controller
Traite les actions de l'utilisateur, modifie les données du modèle et de la vue.

Histoire

Première version
18 octobre 2005
Créateur
Fabien Potencier
Dernière version
6.0.0
Licence
MIT

Response from a controller

render($view, $parameters, $response = null)
Render the template and return a Response
json($data, $status = 200, $headers = array(), $context = array())
Encode data and return a Json response
file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
Return a file response
redirectToRoute($route, $parameters = array(), $status = 302)
Redirect to route
redirect($url, $status = 302)
Redirect to external URL
forward($controller, $path = array(), $query = array())
Forwards the request to another controller
return $this->render('admin/post/show.html.twig', [
    'post' => $post,
    'delete_form' => $deleteForm->createView(),
]);

return $this->json($data);

TwigBridge - Forms

{{ form(view, variables) }}
Render whole form
{{ form_start(view, variables) }}
Render start tag
{{ form_errors(view) }}
Render global errors
{{ form_row(view, variables) }}
Render all the fields
{{ form_rest(view, variables) }}
Render all other fields
{{ form_end(view, variables) }}
Render end tag +
all other fields
{{ form_row(view.field) }}
Render field label, error
and widget
{{ form_label(view.field, label, variables) }}
Render field label
{{ form_errors(view.field) }}
Render field error
{{ form_widget(view.field, variables) }}
Render field widget
{# render a label, but display 'Your Name' and add a "foo" class to it #}
{{ form_label(form.name, 'Your Name', {'label_attr': {'class': 'foo'}}) }}

{# render a widget, but add a "foo" class to it #}
{{ form_widget(form.name, {'attr': {'class': 'foo'}}) }}

{# render a field row, but display a label with text "foo" #}
{{ form_row(form.name, {'label': 'foo'}) }}

Basic form

class TaskForm extends AbstractType
{
public function buildForm(
FormBuilderInterface $builder,
array $options)
{
$builder
->add('dueDate', DateType::class, array(
'widget' => 'single_text',
'label' => 'Due Date',
'required' => false,
'attr' => array('maxlength' => 10),
'constraints' => array(new Length(
array('max' => 10)))
))
->add('save', SubmitType::class);
}
public function configureOptions(
OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'method' => 'GET',
));
}
}

Console

bin\console
List available commands and show the Symfony version
server:run
Run the built-in web server
assets:install --symlink
Install bundle assets as a symlink or hardcopy
debug:autowire
Lists classes/interfaces you can use for autowiring
debug:config
Dumps the current configuration for an extension
debug:container
Displays current services for an application
debug:form
Lists classes/interfaces you can use for autowiring
debug:route
Displays current routes for an application

Usage:
php bin\console command [options] [arguments]

Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-n, --no-interaction Do not ask any interactive question
-e, --env=ENV The environment name [default: "dev"]
--no-debug Switches off debug mode
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Log Levels / Console Verbosity (OutputInterface)

emergency()
System is unusable.
alert()
Action must be taken immediately.
critical()
Critical conditions.
error()
Runtime errors that do not require immediate action but should typically be logged and monitored.
VERBOSITY_QUIET (-1)
-q / stderr
warning()
Exceptional occurrences that are not errors.
VERBOSITY_NORMAL (0)
(non) / stdout
notice()
Normal but significant events.
VERBOSITY_VERBOSE (1)
-v
info()
Interesting events.
VERBOSITY_VERY_VERBOSE (2)
-vv
debug()
Detailed debug information.
VERBOSITY_DEBUG (3)
-vvv
use Psr\Log\LoggerInterface;

public function index(LoggerInterface $logger) {
$logger->info('I just got the logger');
}

Form Options

'action' => ''
Where to send the form's data on submission (usually a URI)
'allow_extra_fields' => false
Allow additional fields to be submitted
'error_mapping' => array('matchingCityAndZipCode' => 'city')
Modify the target of a validation error
'extra_fields_message' => 'This form should not contain extra fields.'
Validation error message if additional fields are submitted
'inherit_data' => false
Inhered data from parent form or not
'method' => 'POST'
HTTP method used to submit the form
'post_max_size_message' => 'The uploaded file was too large.'
Validation message for size of post form data
'validation_groups' => false
Disable the Validation of Submitted Data
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
# Options go here #
));
}

Create a form from a class in a controller

# Create a form from a form class with a default name #

$form = $this->createForm(TaskForm::class, $data, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));

# Create a form from a form class with a non-default name #

$form = $this->container->get('form.factory')
->createNamed('form1', TaskForm::class, $data, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));

Create a new form in a controller

# Create a new form with a default name #

$form = $this->createFormBuilder($data)
->add('dueDate', null, array(
'widget' => 'single_text'))
->add('save', SubmitType::class)
->getForm();

# Create a form with a non-default name #

$form = $this->container->get('form.factory')
->createNamedBuilder(
'form1', FormType::class, $data)
->add('dueDate', null, array(
'widget' => 'single_text'))
->add('save', SubmitType::class)
->getForm();

Authorization in template

{% if is_granted('ROLE_ADMIN') %}
<a href="...">Delete</a>
{% endif %}

Authorization via security.yaml

# config/packages/security.yaml
security:

access_control:
# public access only for /
- { path: ^/$, roles: PUBLIC_ACCESS }
# public access to login page /login
- { path: ^/login, roles: PUBLIC_ACCESS }
# or require ROLE_ADMIN or ROLE_USER_ADMIN for /admin/users*
- { path: '^/admin/users', roles: [ROLE_USER_ADMIN, ROLE_ADMIN] }
# require ROLE_ADMIN for /admin*
- { path: ^/admin, roles: ROLE_ADMIN }
# authenticated users only to the rest /*
- { path: ^/, roles: IS_AUTHENTICATED_FULLY
No limit on amount of URL patterns. Each is a regular expression. First match will be used.

Prepend the path with ^ to ensure only URLs beginning with the pattern are matched.

Persisting Entities

// get the entity manager that manages this entity
$em = $this->getDoctrine()->
getManagerForClass(MyEntity::class);

// create a new entity
$entity = new MyEntity();

// populate / alter properties
$entity->setName('Default Entity');

// tell Doctrine you want to (eventually) save
$em->persist($entity);

// actually executes the queries
$em->flush();

Authorization in a controller

# Example of using wrapper #

public function hello($name)
{
// The second parameter is used to specify on what object the role is tested.
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');

// ...
}

# Example of using AuthorizationChecker

use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

public function hello($name, AuthorizationCheckerInterface $authChecker)
{
if (false === $authChecker->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException('Unable to access this page!');
}

// ...
}

# Example of using annotation #

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

/**
* @Security("has_role('ROLE_ADMIN')")
*/
public function hello($name)
{
// ...
}

Authorization

IS_AUTHENTICATED_FULLY
User has successfully authenticated, not via 'remember me cookie'
IS_AUTHENTICATED_REMEMBERED
All logged in users
IS_REMEMBERED
Only users authenticated using the remember me functionality
IS_IMPERSONATOR
When the current user is impersonating another user in this session
All roles you assign to a user must begin with the ROLE_ prefix in order for the default symfony RoleVoter to vote. Other prefixes require a custom voter.

Doctrine raw SQL

$Connection= $EntityManager->getConnection(): Connection;
Get connection
$Statement = $Connection->prepare(string $sql): Statement;
Prepare statement from string
$Statement->bindValue(string|int $param, $value, $type = null): bool
Bind a value to a corresponding name
$Statement->execute(?array $params = null): bool
Execute the statement
$Statement->fetchAll(int $fetchStyle = PDO::FETCH_BOTH): array
Fetch all results
$Statement->fetchOne(int $fetchStyle = PDO::FETCH_BOTH): array
Fetch single result
$Statement->rowCount(): int
Return the number of rows
$Statement->free(): void
Free stored result memory
$connection = $entityManager->getConnection();

$sql = 'SELECT * FROM events WHERE start_date >= :startdate';

$statement = $conn->prepare($sql);
$statement->bindValue('startdate', $startDate->format('Y-m-d H:i:s'));
$statement->execute();

return $statement->fetchAll();

Doctrine Query Builder

setParameter($parameter, $value)
addCriteria(Criteria $criteria)
[get/set]MaxResults($maxResults)
[get/set]FirstResult($firstResult)
getQuery()
add($dqlPartName, $dqlPart, $append = false)
$dqlPartName: select, from, join, set, where, groupBy, having, orderBy
Wrappers for add():
[add]select($select= null)
delete($delete = null, $alias = null)
update($update = null, $alias = null)
set($key, $value)
from($from, $alias, $indexBy = null)
[inner/left]join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
[and/or]where($where)
[add]groupBy($groupBy)
[and/or]having($having)
[add]orderBy($field, $order = null)

Doctrine ExpressionBuilder and Expr

andX($arg1, $arg2, ...)
Multiple arguments AND
orX($arg1, $arg2, ...)
Multiple arguments OR
[n]eq($field, $value)
[Not] Equal
gte
Greater than [or equal]
lte
Less than [or equal]
isNull($field)
Is Null
[not]in($field, array $values)
[not] In
memberOf($field, $value)
ExpressionBuilder only Member of
isMemberOf($field, $value)
Expr only Member of
isNotNull($field)
Expr only Is not Null
between($field, $x, $y)
Expr only Between $x and $y
trim($field)
Expr only Trim
concat($x, $y)
Expr only Concat
literal($string)
Expr only Literal
lower($field)
Expr only Lower case
upper($field)
Expr only Upper case
count($field)
Expr only count
# Doctrine Expr #
Criteria::expr()->orX(
Criteria::expr()->eq('id', $facilityId),
Criteria::expr()->eq('active', TRUE)
);

# Doctrine ExpressionBuilder #
$qb = $this->createQueryBuilder('f');

$qb->where($qb->expr()->eq('id', '?arg1'))
->setParameter('arg1', $facilityId);

$qb->select($qb->expr()->substring('o.orderNumber', 4, 3));

$qb->select($qb->expr()->concat('p1.surname', $qb->expr()->literal(','), 'p1.firstName');

Doctrine criteria - filtering collections

where($Expression)
Replaces previous statement
[and/or]Where($Expression)
Adds AND /OR where statement
orderBy($array)
Sets OrderBy
setFirstResult($firstResult)
Sets first result
setMaxResult($max)
Sets Maximum results
$userCollection = $group->getUsers();

$criteria = Criteria::create()
->where(Criteria::expr()->eq("birthday", "1982-02-17"))
->orderBy(array("username" => Criteria::ASC))
->setFirstResult(0)
->setMaxResults(20)
;
$birthdayUsers = $userCollection->matching($criteria);

Magic repository functions

find($id)
Returns one entity
findOneBy($criteria, $orderBy)
Returns one entity
findBy($criteria, $orderBy, $limit, $offset)
Returns collection
$criteria = ['field' => 'value']
Single field / value
$criteria = ['field' => ['value1', 'value2']]
Single where-in
$criteria = ['field1' => 'value', 'field2' => 'value2']
multiple fields / values
$repository = $this->getDoctrine()->getRepository(Product::class);
$product = $repository->findOneBy(['name' => 'Keyboard']);

Doctrine Query

getResult($hydrationMode = self::HYDRATE_OBJECT)
Retrieve a collection
getSingleResult($hydrationMode = null)
Retrieve a single result or exception
getOneOrNullResult($hydrationMode = null)
Retrieve a result or NULL
getArrayResult()
Retrieve an array
getScalarResult()
Retrieves a flat/rectangular result set of scalar values
getSingleScalarResult()
Retrieves a single scalar value or exception
AbstractQuery::HYDRATE_OBJECT
Object graph default
AbstractQuery::HYDRATE_ARRAY
Array graph
AbstractQuery::HYDRATE_SCALAR
Flat rectangular result with scalars
AbstractQuery::HYDRATE_SINGLE_SCALAR
Single scalar value
AbstractQuery::HYDRATE_SIMPLEOBJECT
Very simple object fast
setCacheMode($cacheMode)
Cache::MODE_GET
may read, not write
Cache::MODE_PUT
no read, write all
Cache::MODE_NORMAL
read and write
Cache::MODE_REFRESH
no read, only refresh
setCacheable($trueFalse)
Enable/disable result caching

Repositories in a controller

$this->getDoctrine()->getRepository(MyEntity::class);

Entity Manager in a controller

$this->getDoctrine()->getManager();
Get default entity manager
$this->getDoctrine()->getManagerForClass(MyEntity::class)
Get entity specific manager