{"id":15724307,"url":"https://github.com/cedlemo/symfony_34_notes","last_synced_at":"2026-06-09T16:31:38.907Z","repository":{"id":144940323,"uuid":"137392987","full_name":"cedlemo/symfony_34_notes","owner":"cedlemo","description":"Mes notes sur symfony 3.4.* ","archived":false,"fork":false,"pushed_at":"2018-06-28T17:50:49.000Z","size":167,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-06T12:26:02.248Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cedlemo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-14T18:12:46.000Z","updated_at":"2018-06-28T17:50:51.000Z","dependencies_parsed_at":"2023-04-04T21:02:45.614Z","dependency_job_id":null,"html_url":"https://github.com/cedlemo/symfony_34_notes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cedlemo/symfony_34_notes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedlemo%2Fsymfony_34_notes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedlemo%2Fsymfony_34_notes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedlemo%2Fsymfony_34_notes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedlemo%2Fsymfony_34_notes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cedlemo","download_url":"https://codeload.github.com/cedlemo/symfony_34_notes/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cedlemo%2Fsymfony_34_notes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34116456,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-03T22:16:05.366Z","updated_at":"2026-06-09T16:31:38.888Z","avatar_url":"https://github.com/cedlemo.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Symfony 3.4 notes: Creation d'un simple blog.\n\n* [Installation et configuration](#installation-et-configuration)\n* [Generation d'un bundle](#generation-d-'-bundle)\n* [Entités et schema de la base de données](#entités-et-schema-de-la-base-de-données)\n  * [Création d'entités](#création-d'entités)\n    * [Entity Article](#entity-article)\n    * [Entity Category](#entity-category)\n    * [Entity Author](#entity-author)\n    * [Entity Comment](#entity-comment)\n    * [Générer les tables de la base de données](#générer-les-tables-de-la-base-de-données)\n  * [Gestion des relations entre entités](#gestion-des-relations-entre-entités)\n    * [Relation bidirectionnelle ManyToOne OneToMany](#relation-bidirectionnelle-manytoone-onetomany)\n      * [Relation Article Author](#relation-article-author)\n      * [Relation Article Comment](#relation-article-comment)\n    * [Relation bidirectionnelle ManyToMany](#relation-bidirectionnelle-manytomany)\n      * [Relation Article Category](#relation-article-category)\n * [Controleur et vues twig](#contrôleur-et-vues-twig)\n   * [Creation des contrôleurs : CRUD](#creation-des-contrôleurs-:-crud)\n     * [CRUD Article](#crud-article)\n     * [CRUD Category](#crud-category)\n     * [CRUD Comment et Author](#crud-comment-et-author)\n   * [Le routage](#le-routage)\n   * [Les vues twig](#les-vues-twig)\n     * [Configuration des chemins](#configuration-des-chemins)\n     * [Présentation des templates](#présentation-des-templates)\n * [Gestion d'utilisateurs avec FOSUser](#gestion-d-'-utilisateurs-avec-fosuser)\n   * [Installation de FOSUser](#installation-de-fosuser)\n\n## Installation et configuration\n\n```\nmkdir blog\ncd blog\ncomposer create-project symfony/framework-standard-edition . \"3.4.*\"\n```\n\nLors de l'installation, certaines informations doivent être renseignées:\n\n```\nSome parameters are missing. Please provide them.\ndatabase_host (127.0.0.1):\ndatabase_port (null):\ndatabase_name (symfony):\ndatabase_user (root):\ndatabase_password (null):\nmailer_transport (smtp):\nmailer_host (127.0.0.1):\nmailer_user (null):\nmailer_password (null):\nsecret (ThisTokenIsNotSoSecretChangeIt): azerlkjazerlkjazerlkjazerlmkjazer\n```\n\nUtiliser la version \"dev\" pour avoir accès à l'inferface de débug de symfony:\nDans le fichier *web/.htaccess*, il faut remplacer toutes les occurences de\n`app.php` par `app_dev.php`:\n\n```bash\ngrep \"app_dev\" web/.htaccess                                                                                                     public_html/blog\nDirectoryIndex app_dev.php\n    RewriteRule ^app_dev\\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]\n    RewriteRule ^ %{ENV:BASE}/app_dev.php [L]\n```\n\n## Generation d'un bundle\n\nun projet a au moins un bundle, les bundles sont créés en ligne de commande:\n\n```\nphp bin/console generate:bundle\n```\nC'est une commande interactive, voici les réponses pour ce petit projet de test:\n\n* le nom du bundle : *BlogBundle*\n* installation dans le répertoire par défaut: *src/*\n* le format de la configuration : *yml*\n\n```bash\nphp bin/console generate:bundle                                                                                          public_html/blog  master\n\n  Welcome to the Symfony bundle generator!\n\nAre you planning on sharing this bundle across multiple applications? [no]: no\n\nYour application code must be written in bundles. This command helps\nyou generate them easily.\n\nGive your bundle a descriptive name, like BlogBundle.\nBundle name: BlogBundle\n\nBundles are usually generated into the src/ directory. Unless you're\ndoing something custom, hit enter to keep this default!\n\nTarget Directory [src/]:\n\nWhat format do you want to use for your generated configuration?\n\nConfiguration format (annotation, yml, xml, php) [annotation]: yml\n\n\n  Bundle generation\n  ...\n```\n\nUn message d'erreur à l'issue de la création du bundle indique, que ce dernier\nn'est pas chargé par défaut:\n\n```\nThe command was not able to configure everything automatically.\n  You'll need to make the following changes manually.\n\n\n- Edit the composer.json file and register the bundle\n  namespace in the \"autoload\" section:\n```\n\nIl suffit d'ajouter dans la section autoload du fichier *composer.json*, une ligne\npour référencer le nouveau bundle.\n\n```diff\ndiff --git a/composer.json b/composer.json\nindex ba17155..06faed5 100755\n--- a/composer.json\n+++ b/composer.json\n@@ -5,7 +5,8 @@\n     \"description\": \"The \\\"Symfony Standard Edition\\\" distribution\",\n     \"autoload\": {\n         \"psr-4\": {\n-            \"AppBundle\\\\\": \"src/AppBundle\"\n+            \"AppBundle\\\\\": \"src/AppBundle\",\n+           \"BlogBundle\\\\\": \"src/BlogBundle\"\n         },\n         \"classmap\": [ \"app/AppKernel.php\", \"app/AppCache.php\" ]\n     },\n```\n\naprès, il faut lancer la commande suivante afin de regénerer la liste des classes\nà charger automatiquement dans le projet:\n\n```\ncomposer dump-autoload\n```\n\nSi cette commande n'est pas lancée, toutes tentatives d'utiliser la console\n`php bin/console *` ou d'accèder à la page d'accueil du blog, renverra un message\nd'erreur du type:\n\n```\nAttempted to load class \"BlogBundle\" from namespace \"BlogBundle\"\n```\n\nEt bien sur cela ne fonctionne pas après. Il y a le message suivant quand on\nrafraichi la page:\n\n```\nUnable to find template \"BlogBundle:Default:index.html.twig\" (looked into: /home/cedlemo/public_html/blog/app/Resources/views, /home/cedlemo/public_html/blog/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form).\n```\nL'erreur vient du fichier : src/BlogBundle/Controller/DefaultController.php\n\n```php\n\u003c?php\n\nnamespace BlogBundle\\Controller;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller;\n\nclass DefaultController extends Controller\n{\n    public function indexAction()\n    {\n        return $this-\u003erender('BlogBundle:Default:index.html.twig');\n    }\n}\n```\n\nC'est une sorte de bug. En gros depuis la version 3.4 de Symfony, les chemins\nde fichiers twig du type `'BlogBundle:Default:index.html.twig'` ne sont plus\nsupportés, le nouveau format est : `@Blog/Default/index.html.twig`. Il semble\nque le probleme vienne de la commande `generate:bundle` qui n'a pas été mise à\njour.\n\nsource : https://stackoverflow.com/questions/47832977/symfony-3-4-use-view-inside-my-bundle?rq=1\n\n```diff\ndiff --git a/src/BlogBundle/Controller/DefaultController.php b/src/BlogBundle/Controller/DefaultController.php\nindex 0e7ca62..7a062f2 100644\n--- a/src/BlogBundle/Controller/DefaultController.php\n+++ b/src/BlogBundle/Controller/DefaultController.php\n@@ -8,6 +8,6 @@ class DefaultController extends Controller\n {\n     public function indexAction()\n     {\n-        return $this-\u003erender('BlogBundle:Default:index.html.twig');\n+        return $this-\u003erender('@Blog/Default/index.html.twig');\n     }\n }\n```\n\n## Entités et schema de la base de données\n\nLa [configuration de la base de données](http://symfony.com/doc/3.4/best_practices/configuration.html) peut être retrouvée dans le fichier\n*app/config/parameters.yml*. Dans mon cas mon fichier ressemble à ceci:\n\n```yaml\n# This file is auto-generated during the composer install\nparameters:\n    database_host: 127.0.0.1\n    database_port: null\n    database_name: symfony_blog\n    database_user: root\n    database_password: null\n    mailer_transport: smtp\n    mailer_host: 127.0.0.1\n    mailer_user: null\n    mailer_password: null\n    secret: azerlkjazerlkjazerlkjazerlmkjazer\n```\n\nAvant toutes choses, il faut créer la base de données. Les tables de cette base\net leurs relations base seront ajoutées au fur et à mesure de leurs créations.\n\n```bash\nphp bin/console doctrine:database:create\nCreated database `symfony_blog` for connection named default\n```\n\n### Création d'entités\n\nEn considérant que le blog repose sur une base de données contenant quatre\ntables principales:\n\n* article\n* category\n* comment\n* author\n\non créera donc 4 \"entities\" :\n* Article\n* Category\n* Comment\n* Author\n\n#### Entity Article\n\nLes champs de la table *articles* sont:\n* title\n* content\n* publicationDate\n* status\n\nDonc pour créer une entité *Article*, on exécute la commande suivante:\n\n```bash\nphp bin/console doctrine:generate:entity                                                                                 public_html/blog  master\n\n  Welcome to the Doctrine2 entity generator\n\nThis command helps you generate Doctrine2 entities.\n\nFirst, you need to give the entity name you want to generate.\nYou must use the shortcut notation like AcmeBlogBundle:Post.\n\nThe Entity shortcut name: BlogBundle:Article\n\nDetermine the format to use for the mapping information.\n\nConfiguration format (yml, xml, php, or annotation) [annotation]:\n\nInstead of starting with a blank entity, you can add some fields now.\nNote that the primary key will be added automatically (named id).\n\nAvailable types: array, simple_array, json_array, object,\nboolean, integer, smallint, bigint, string, text, datetime, datetimetz,\ndate, time, decimal, float, binary, blob, guid.\n\nNew field name (press \u003creturn\u003e to stop adding fields): title\nField type [string]:\nField length [255]:\nIs nullable [false]:\nUnique [false]:\n\nNew field name (press \u003creturn\u003e to stop adding fields): content\nField type [string]: text\nIs nullable [false]:\nUnique [false]:\n\nNew field name (press \u003creturn\u003e to stop adding fields): publicationDate\nField type [string]: datetime\nIs nullable [false]:\nUnique [false]:\n\nNew field name (press \u003creturn\u003e to stop adding fields): status\nField type [string]: boolean\nIs nullable [false]:\nUnique [false]:\n\nNew field name (press \u003creturn\u003e to stop adding fields):\n\n\n  Entity generation\n\n\n  created ./src/BlogBundle/Entity/\n  created ./src/BlogBundle/Entity/Article.php\n\u003e Generating entity class src/BlogBundle/Entity/Article.php: OK!\n\u003e Generating repository class src/BlogBundle/Repository/ArticleRepository.php: OK!\n\n\n  Everything is OK! Now get to work :).\n```\n\nCette commande génère différents fichiers:\n* src/BlogBundle/Entity/Article.php\n* src/BlogBundle/Repository/ArticleRepository.php\n\nDans *Article.php*, se trouve une classe `Article` avec des variables privées\nnommées selon les champs que l'on a rensignés ainsi que des getters/setters pour\nces variables. De plus on trouve en commentaire des annotations faisant le lien\nentre les variables et les tables/colonnes à générer dans la base de données.\n\nL'annotation suivante:\n\n```php\n/**\n * Article\n *\n * @ORM\\Table(name=\"article\")\n * @ORM\\Entity(repositoryClass=\"BlogBundle\\Repository\\ArticleRepository\")\n */\nclass Article\n{\n```\ndit que la classe Article correspond à la table \"article\".\n\nL'annotation:\n\n```php\n    /**\n     * @var string\n     *\n     * @ORM\\Column(name=\"title\", type=\"string\", length=255)\n     */\n    private $title;\n```\nindique que la variable `$title` de la classe `Article` correspondra à une colonne\nnommée \"title\" dans la table \"article\".\n\n#### Entity Category\n\nLa création de cette entité se fait comme précédement:\n\n```\nNew field name (press \u003creturn\u003e to stop adding fields): name\nField type [string]:\nIs nullable [false]:\nUnique [false]:\n```\n\n#### Entity Author\n\n```\nNew field name (press \u003creturn\u003e to stop adding fields): name\nField type [string]:\nIs nullable [false]:\nUnique [false]:\nNew field name (press \u003creturn\u003e to stop adding fields): biography\nField type [string]: text\nIs nullable [false]:\nUnique [false]:\n```\n\n#### Entity Comment\n\n```\nNew field name (press \u003creturn\u003e to stop adding fields): email\nField type [string]:\nIs nullable [false]:\nUnique [false]:\nNew field name (press \u003creturn\u003e to stop adding fields): content\nField type [string]: text\nIs nullable [false]:\nUnique [false]:\nNew field name (press \u003creturn\u003e to stop adding fields): datePublication\nField type [string]: datetime\nIs nullable [false]:\nUnique [false]:\nNew field name (press \u003creturn\u003e to stop adding fields): status\nField type [string]: boolean\nIs nullable [false]:\nUnique [false]:\n```\n\n#### Générer les tables de la base de données\n\nAprès chaque création d'\"entity\" ou après la création de toutes les \"entities\",\nil est possible de générer les tables correspondantes avec la commande suivante:\n\n```bash\nphp bin/console doctrine:schema:udpate --force\n```\n\n### Gestion des relations entre entités\nAvant de créer les relations, il faut les identifier:\n\n#### Relation bidirectionnelle ManyToOne OneToMany\n\n##### Relation Article Author\n\n* Un article a un auteur, un auteur peut créer plusieurs articles.\n\nIci on se retrouve dans le même cas que dans l'exemple de la [documentation](http://symfony.com/doc/3.4/doctrine/associations.html#relationship-mapping-metadata). D'un point de vue de l'entité\n`Article`, plusieurs articles peuvent être reliés à un auteur :`ManyToOne`. D'un\npoint de vue de l'entité `Author`, un auteur est lié à plusieurs articles : `OneToMany`.\n\nPour mettre en place cette relation bi-directionnelle, il faut:\n* créer une variable `$author` et ses getter/setter dans `Article` ainsi que les\nannotations nécessaires pour `doctrine`.\n* créer une variable `$articles` et ses getter/setter dans `Author` ainsi que les\nannotations nécessaires pour `doctrine`.\n\nUn bon diff vieux diff:\n\n```diff\ndiff --git a/src/BlogBundle/Entity/Article.php b/src/BlogBundle/Entity/Article.php\nindex 8a86a61..a9ea99c 100644\n--- a/src/BlogBundle/Entity/Article.php\n+++ b/src/BlogBundle/Entity/Article.php\n@@ -49,6 +49,10 @@ class Article\n      */\n     private $status;\n\n+    /**\n+     * @ORM\\ManyToOne(targetEntity=\"BlogBundle\\Entity\\Author\", inversedBy=\"articles\")\n+     */\n+    private $author;\n\n     /**\n      * Get id\n@@ -155,5 +159,24 @@ class Article\n     {\n         return $this-\u003estatus;\n     }\n-}\n\n+    /**\n+     * Get author\n+     * @return Author\n+     */\n+    public function getAuthor()\n+    {\n+\treturn $this-\u003eauthor;\n+    }\n+\n+    /**\n+     * Set author\n+     * @param Author $author\n+     * @return Article\n+     */\n+    public function setAuthor($author)\n+    {\n+\t$this-\u003eauthor = $author;\n+\treturn $this;\n+    }\n+}\ndiff --git a/src/BlogBundle/Entity/Author.php b/src/BlogBundle/Entity/Author.php\nindex 3eb009f..351f055 100644\n--- a/src/BlogBundle/Entity/Author.php\n+++ b/src/BlogBundle/Entity/Author.php\n@@ -36,6 +36,11 @@ class Author\n     private $biography;\n\n\n+    /**\n+     * @ORM\\OneToMany(targetEntity=\"BlogBundle\\Entity\\Article\", mappedBy=\"author\")\n+     */\n+    private $articles;\n+\n     /**\n      * Get id\n      *\n@@ -93,5 +98,24 @@ class Author\n     {\n         return $this-\u003ebiography;\n     }\n-}\n\n+    /**\n+     * Set articles\n+     * @param mixed Article\n+     * @return Author\n+     */\n+    public function setArticles($articles)\n+    {\n+\t$this-\u003earticles = $articles;\n+\treturn $this;\n+    }\n+\n+    /**\n+     * Get articles\n+     * @return mixed Article\n+     */\n+    public function getArticles()\n+    {\n+\treturn $this-\u003earticles;\n+    }\n+}\n```\nPour générer la relation, il suffit de mettre à jour la base de données avec:\n\n```\nphp bin/console doctrine:schema:update --force\n```\n\n##### Relation Article Comment\n\n* Un commentaire est lié à un article, un article peut avoir plusieurs commentaires.\nOn se trouve dans le même cas que précédement.  D'un point de vue du commentaire,\nplusieurs commentaires peuvent être liés à un article : `ManyToOne` et un\narticle peut avoir plusieur commentaires `OneToMany`.\n\nDans la classe `Comment`, on crée une variable privée `$articles`, ses setter/getter\nainsi que les annotations décrivant la relation avec la classe Article.\n\n```diff\ndiff --git a/src/BlogBundle/Entity/Comment.php b/src/BlogBundle/Entity/Comment.php\nindex 9dd1e8b..c7f8034 100644\n--- a/src/BlogBundle/Entity/Comment.php\n+++ b/src/BlogBundle/Entity/Comment.php\n@@ -49,6 +49,11 @@ class Comment\n      */\n     private $status;\n\n+    /**\n+     * @var Article\n+     * @ORM\\ManyToOne(targetEntity=\"\\BlogBundle\\Entity\\Article\", inversedBy=\"comments\")\n+     */\n+    private $article;\n\n     /**\n      * Get id\n@@ -155,5 +160,24 @@ class Comment\n     {\n         return $this-\u003estatus;\n     }\n-}\n\n+    /**\n+     * Set article\n+     * @param Article $article\n+     * @return Comment\n+     */\n+    public function setArticle(Article $article)\n+    {\n+       $this-\u003earticle = $article;\n+       return $this;\n+    }\n+\n+    /**\n+     * Get article\n+     * @return Article\n+     */\n+    public function getArticle()\n+    {\n+       return $this-\u003earticle;\n+    }\n+}\n```\n\nDans la classe `Article` on ajoute la variable `$comments`, son getter, son\nsetter ainsi que les annotations nécessaires à la description de la relation\navec la classe `Comment`.\n\n\n```diff\ndiff --git a/src/BlogBundle/Entity/Article.php b/src/BlogBundle/Entity/Article.php\nindex a9ea99c..ac7405b 100644\n--- a/src/BlogBundle/Entity/Article.php\n+++ b/src/BlogBundle/Entity/Article.php\n@@ -54,6 +54,11 @@ class Article\n      */\n     private $author;\n\n+    /**\n+     * @ORM\\OneToMany(targetEntity=\"BlogBundle\\Entity\\Comment\", mappedBy=\"article\")\n+     */\n+    private $comments;\n+\n     /**\n      * Get id\n      *\n@@ -179,4 +184,24 @@ class Article\n        $this-\u003eauthor = $author;\n        return $this;\n     }\n+\n+    /**\n+     * Get articles\n+     * @return mixed Aricle\n+     */\n+    public function getArticles()\n+    {\n+       return $this-\u003earticles\n+    }\n+\n+    /**\n+     * Set articles\n+     * @param mixed Article\n+     * @return Article\n+     */\n+    public function setArticles($articles)\n+    {\n+       $this-\u003earticles = $articles;\n+        return $this;\n+    }\n }\n```\n\nAvant de mettre à jour la base de données, il est possible de valider les\nannotations:\n\n```bash\nphp bin/console doctrine:schema:validate\n```\nComme précédement les changements sur la base de données sont générés avec:\n\n```bash\nphp bin/console doctrine:schema:update --force\n```\n\n#### Relation bidirectionnelle ManyToMany\n\n##### La relation Article Category\n\n* un article peut avoir plusieurs catégories, une catégorie peut décrirent\nplusieurs articles.\n\nLa documentation relative a ces relations `ManyToMany` se trouve [ici](\nhttps://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-many-bidirectional\n).\nDans ce type de relation, il va falloir décider quel entité va être responsable\nde la relation. Ici c'est l'Article puisque l'on associe obligatoirement\nune catégorie lors de la création d'un article.\n\nDonc dans la classe `Article`, on ajoute une variable privée `$categories` avec\nles fonctions qui vont bien et l'annotation suivante:\n\n```php\n/**\n * @ORM\\ManyToMany(targetEntity=\"\\BlogBundle\\Entity\\Category\", inversedBy=\"articles\")\n */\n```\n\nDans la classe `Category`, on ajoute la variables `$articles` avec ses setter/getter\net l'annotation suivante:\n\n```php\n/**\n * @ORM\\ManyToMany(targetEntity=\"\\BlogBundle\\Entity\\Article\", mappedBy=\"categories\")\n */\n```\n\nAprès vérification, on lance la mise à jour de la base de données.\n\n```bash\nphp bin/console doctrine:schema:validate\nphp bin/console doctrine:schema:update --force\n```\n\n## Contrôleur et vues twig.\n\n### Creation des contrôleurs : CRUD.\n\nC'est dans le contrôleur que l'on trouvera la logique applicative concernant les\nactions réalisées par l'utilisateur. On peut tout à fait générer un à un les\ncontrôleurs nécessaires avec la commande:\n\n```\nphp bin/console doctrine:generate:controleur\n```\n\nMais on peut aussi utiliser générer directement un ensemble de contrôleurs\ngénériques pour les actions de base:\n\n- Create\n- Read\n- Update\n- Delete.\n\nDans cet exemple on considère que toutes les actions que l'on va générer, devront\nêtre accessible via les routes suivantes:\n* /admin/article\n* /admin/category\n* /admin/author\n* /admin/comment\nCela permettra de séparer la gestion (suppression, édition, création ...) des\néléments du site afin de faciliter l'authentification.\n\n#### CRUD Category\n\nAvec la commande suivante pour l'entity `BlogBundle:Category`:\n\n```\nphp bin/console doctrine:generate:crud\n```\n\nOn choisira de générer les actions permettant d'écrire, d'insérer des données\ndans la base de données:\n\n```\nDo you want to generate the \"write\" actions [no]? yes\n```\n\nEnsuite les routes seront gérées via des fichiers yml:\n\n```\nDetermine the format to use for the generated CRUD.\nConfiguration format (yml, xml, php, or annotation) [annotation]: yml\n```\n\nLa commande va générer le contrôleur dans :\n- src/BlogBundle/Controller/CategoryController.php\n\nDes fichiers de vues twig et des formulaires pour la création et l'édition\nde l'entité `Category` dans les répertoires:\n- src/BlogBundle/Form/\n- app/Resources/views/category/\n\nRien que avec cela, les urls suivantes sont fonctionnelles:\n\n* http://blog.fr/admin/category/\n* http://blog.fr/admin/category/new\n* http://blog.fr/admin/category/1/show\n* http://blog.fr/admin/category/1/delete\n\n#### CRUD Article.\n\nMême commande que précédement:\n\n```\nphp bin/console doctrine:generate:crud\n```\n\nSi l'erreur suivante apparait:\n\n```\n- Import the bundle's routing resource in the bundle routing file\n  (/home/cedlemo/public_html/blog/src/BlogBundle/Resources/config/routing.yml).\n\n    blog_admin_article:\n        resource: \"@BlogBundle/Resources/config/routing/article.yml\"\n        prefix:   /admin/article\n```\n\nC'est que l'import des routes n'a pu se faire. Il suffit de rajouter la partie\nmanquante dans le `routing.yml` du bundle.\n\n```diff\ndiff --git a/src/BlogBundle/Resources/config/routing.yml b/src/BlogBundle/Resources/config/routing.yml\nindex 7ac2d32..ee41b2f 100644\n--- a/src/BlogBundle/Resources/config/routing.yml\n+++ b/src/BlogBundle/Resources/config/routing.yml\n@@ -2,6 +2,10 @@ blog_admin_category:\n     resource: \"@BlogBundle/Resources/config/routing/category.yml\"\n     prefix:   /admin/category\n\n+blog_admin_article:\n+        resource: \"@BlogBundle/Resources/config/routing/article.yml\"\n+        prefix:   /admin/article\n+\n blog_homepage:\n     path:     /\n     defaults: { _controller: BlogBundle:Default:index }\n```\n\n#### CRUD Comment et Author.\n\nLa generation peut se faire sans le côté interactif avec:\n\n```\nphp bin/console doctrine:generate:crud BlogBundle:Comment -n --format=yml --with-write\nphp bin/console doctrine:generate:crud BlogBundle:Author -n --format=yml --with-write\n```\n\n### Le routage:\n\nLa gestion des \"routes\", c'est à dire les urls correspondant aux contrôleurs et\nà leurs actions, se fait dans le fichier *BlogBundle/Ressources/config/routing.yml*.\nL'ajout des CRUD pour chaque entités a séparer les routes dans différents fichier.\n\n```\ntree Resources/config                                                                                                      src/BlogBundle  master\nResources/config\n├── routing\n│   ├── article.yml\n│   ├── author.yml\n│   ├── category.yml\n│   └── comment.yml\n├── routing.yml\n```\n\nPour le cas `Article`, on le retrouve dans le fichier *routing.yml* :\n\n```yaml\nblog_admin_article:\n    resource: \"@BlogBundle/Resources/config/routing/article.yml\"\n    prefix:   /admin/article\n\n```\nOn prefixe donc toutes les routes en lien avec la gestion des articles par `/admin/article`\net le fichier contenant le reste de ces routes est indiqué par le champ `resource`. Dans\nce fichier se trouve les routes en liens avec les actions du contrôleur `ArticleController`.:\n\n```yaml\nadmin_article_index:\n    path:     /\n    defaults: { _controller: \"BlogBundle:Article:index\" }\n    methods:  GET\n\nadmin_article_show:\n    path:     /{id}/show\n    defaults: { _controller: \"BlogBundle:Article:show\" }\n    methods:  GET\n\nadmin_article_new:\n    path:     /new\n    defaults: { _controller: \"BlogBundle:Article:new\" }\n    methods:  [GET, POST]\n\nadmin_article_edit:\n    path:     /{id}/edit\n    defaults: { _controller: \"BlogBundle:Article:edit\" }\n    methods:  [GET, POST]\n\nadmin_article_delete:\n    path:     /{id}/delete\n    defaults: { _controller: \"BlogBundle:Article:delete\" }\n    methods:  DELETE\n```\n\n### Les vues twig\n\n#### Configuration des chemins\n\nPar défaut, la génération des actions CRUD pour l'entité `Article`, a créé des\nvues dans *app/Resources/views*:\n\n* app/Resources/views/article/edit.html.twig\n* app/Resources/views/article/index.html.twig\n* app/Resources/views/article/new.html.twig\n* app/Resources/views/article/show.html.twig\n\nCes vues sont appelées dans l'action associée. Par exemple, la vue *index.html*,\nest appelée dans `ArticleController`, par:\n\n```php\n   /**\n     * Lists all article entities.\n     *\n     */\n    public function indexAction()\n    {\n        $em = $this-\u003egetDoctrine()-\u003egetManager();\n\n        $articles = $em-\u003egetRepository('BlogBundle:Article')-\u003efindAll();\n\n        return $this-\u003erender('article/index.html.twig', array(\n            'articles' =\u003e $articles,\n        ));\n    }\n```\n\nIl est plus intéressant d'avoir les vues en lien avec un bundle dans ce bundle.\nDonc il faut déplacer les vues de *app/Resources/views* à *BlogBundle/Resources/views*.\n\nDe plus, l'utilisation d'annotations pour faire le lien entre une vue et un\ncontrôleur permet de grandement simplifier le code. Exemple avec `ArticleController.indexAction`:\n\n```diff\ndiff --git a/src/BlogBundle/Controller/ArticleController.php b/src/BlogBundle/Controller/ArticleController.php\nindex 14a97d5..7ca9ca6 100644\n--- a/src/BlogBundle/Controller/ArticleController.php\n+++ b/src/BlogBundle/Controller/ArticleController.php\n@@ -5,6 +5,7 @@ namespace BlogBundle\\Controller;\n use BlogBundle\\Entity\\Article;\n use Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller;\n use Symfony\\Component\\HttpFoundation\\Request;\n+use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Template;\n\n /**\n  * Article controller.\n@@ -15,6 +16,7 @@ class ArticleController extends Controller\n     /**\n      * Lists all article entities.\n      *\n+     * @Template(\"@Blog/article/index.html.twig\")\n      */\n     public function indexAction()\n     {\n@@ -22,9 +24,7 @@ class ArticleController extends Controller\n\n         $articles = $em-\u003egetRepository('BlogBundle:Article')-\u003efindAll();\n\n-        return $this-\u003erender('article/index.html.twig', array(\n-            'articles' =\u003e $articles,\n-        ));\n+        return ['articles' =\u003e $articles];\n     }\n\n```\n\nOn notera que l'on peut ne pas indiquer d'argument avec `@Template`. Symfony fera\nautomatiquement le lien entre une action nommée `trucAction` et un fichier de\ntemplate nommé `truc.html.twig`.\n\nSi l'on décide de ne pas utiliser l'annotation `@Template`, et que l'on décide\nde mettre les vues dans le bundle, il faut utiliser une [notation particulière\npour indiquer le chemin d'accès des vues](https://symfony.com/doc/3.4/templating.html#referencing-templates-in-a-bundle).\n\nPar exemple pour le bundle `BlogBundle` et les fichiers twig mis dans *src/BlogBundle/Resources/views/* :\n\n```diff\ndiff --git a/src/BlogBundle/Controller/CommentController.php b/src/BlogBundle/Controller/CommentController.php\nindex a53dac7..dc6d010 100644\n--- a/src/BlogBundle/Controller/CommentController.php\n+++ b/src/BlogBundle/Controller/CommentController.php\n@@ -22,7 +22,7 @@ class CommentController extends Controller\n\n         $comments = $em-\u003egetRepository('BlogBundle:Comment')-\u003efindAll();\n\n-        return $this-\u003erender('comment/index.html.twig', array(\n+        return $this-\u003erender('@Blog/comment/index.html.twig', array(\n             'comments' =\u003e $comments,\n         ));\n     }\n@@ -45,7 +45,7 @@ class CommentController extends Controller\n             return $this-\u003eredirectToRoute('comment_show', array('id' =\u003e $comment-\u003egetId()));\n         }\n\n-        return $this-\u003erender('comment/new.html.twig', array(\n+        return $this-\u003erender('@Blog/comment/new.html.twig', array(\n             'comment' =\u003e $comment,\n             'form' =\u003e $form-\u003ecreateView(),\n         ));\n@@ -59,7 +59,7 @@ class CommentController extends Controller\n     {\n         $deleteForm = $this-\u003ecreateDeleteForm($comment);\n\n-        return $this-\u003erender('comment/show.html.twig', array(\n+        return $this-\u003erender('@Blog/comment/show.html.twig', array(\n             'comment' =\u003e $comment,\n             'delete_form' =\u003e $deleteForm-\u003ecreateView(),\n         ));\n@@ -81,7 +81,7 @@ class CommentController extends Controller\n             return $this-\u003eredirectToRoute('comment_edit', array('id' =\u003e $comment-\u003egetId()));\n         }\n\n-        return $this-\u003erender('comment/edit.html.twig', array(\n+        return $this-\u003erender('@Blog/comment/edit.html.twig', array(\n             'comment' =\u003e $comment,\n             'edit_form' =\u003e $editForm-\u003ecreateView(),\n             'delete_form' =\u003e $deleteForm-\u003ecreateView(),\n```\n\n##### Présentation des templates\n\n## Gestion d'utilisateurs avec FOSUser\n\nSymfony embarque [des composants liés à la sécurité](http://symfony.com/doc/3.4/security.html)\npermettant de facilement mettre en place une gestion d'utilisateur : authenfification et\nrestriction d'accès à toutes ou parties du site. Il existe un bundle installable\nvia composer simplifiant reposant entièrement sur le système `security` :\n[FOSUserBundle](http://symfony.com/doc/master/bundles/FOSUserBundle/index.html).\nCe bundle permet de mettre en place l'enregistrement d'utilisateur en base de donnée,\nles fonctionnalités d'enregistrement/inscription d'utilisateurs, la création de\npage de profile et le reset de mot de passe.\n\n### Installation de FOSUser\n\nL'installation se fait via `composer` dans le répertoire *blog*:\n\n```bash\ncomposer require friendsofsymfony/user-bundle \"~2.0\"\n```\n\nEnsuite on l'active dans *app/AppKernel.php* :\n\n```diff\niff --git a/app/AppKernel.php b/app/AppKernel.php\nindex b6b11fe..20bb563 100755\n--- a/app/AppKernel.php\n+++ b/app/AppKernel.php\n@@ -18,6 +18,7 @@ class AppKernel extends Kernel\n             new Sensio\\Bundle\\FrameworkExtraBundle\\SensioFrameworkExtraBundle(),\n             new AppBundle\\AppBundle(),\n             new BlogBundle\\BlogBundle(),\n+           new FOS\\UserBundle\\FOSUserBundle(),\n         ];\n\n         if (in_array($this-\u003egetEnvironment(), ['dev', 'test'], true)) {\n```\n\nEtant donné que l'idée est d'enregistrer les utilisateurs en base de données,\nil faut créer une classe `User`, cette classe va étendre la classe `User`\nfournie par `FOSUser` dans le fichier *vendor/friendsofsymfony/user-bundle/Model/User.php*.\nLa classe de base `User` contient déjà la pluspart des champs nécessaires:\n\n```php\nabstract class User implements UserInterface, GroupableInterface                                                                             [491/693]\n{\n    /**\n     * @var mixed\n     */\n    protected $id;\n\n    /**\n     * @var string\n     */\n    protected $username;\n\n    /**\n     * @var string\n     */\n    protected $usernameCanonical;\n\n    /**\n     * @var string\n     */\n    protected $email;\n\n    /**\n     * @var string\n     */\n    protected $emailCanonical;\n\n    /**\n     * @var bool\n     */\n    protected $enabled;\n\n    /**\n     * The salt to use for hashing.\n     *\n     * @var string\n     */\n    protected $salt;\n\n    /**\n     * Encrypted password. Must be persisted.\n     *\n     * @var string\n     */\n    protected $password;\n```\n\nLa création de la classe suivante dans *src/BlogBundle/Entity/* est suffisante:\n\n```php\n\u003c?php\n// src/AppBundle/Entity/User.php\n\nnamespace AppBundle\\Entity;\n\nuse FOS\\UserBundle\\Model\\User as BaseUser;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n/**\n * @ORM\\Entity\n * @ORM\\Table(name=\"fos_users\")\n */\nclass User extends BaseUser\n{\n    /**\n     * @ORM\\Id\n     * @ORM\\Column(type=\"integer\")\n     * @ORM\\GeneratedValue(strategy=\"AUTO\")\n     */\n    protected $id;\n\n    public function __construct()\n    {\n        parent::__construct();\n        // your own logic\n    }\n}\n```\n\nMaintenant, il faut adapter le fichier `app/config/security.yml`. Dans ce fichier,\non définit la façon dont on récupère les informations utilisateurs,\nles rôles et la hiérarchie existant entre ces rôles ainsi que les règles d'accès\nen fonction des roles.\n\n```yaml\nsecurity:\n    encoders:\n        AppBundle\\Entity\\User:\n          algorithm: bcrypt\n\n    # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded\n    providers:\n        database:\n            entity:\n                class: AppBundle:User\n                property: username\n\n    role_hierarchy:\n        ROLE_ADMIN: [ROLE_AUTHOR, ROLE_WRITER]\n\n    firewalls:\n    # disables authentication for assets and the profiler, adapt it according to your needs\n        dev:\n            pattern: ^/(_(profiler|wdt)|css|images|js)/\n            security: false\n\n        main:\n            anonymous: true\n            provider: database\n\n            form_login:\n                login_path: login\n                check_path: login\n\n            logout:\n                path: /logout\n                target: /\n\n    access_control:\n\t- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }\n        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }\n        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }\n        - { path: ^/admin/, roles: [ROLE_AUTHOR, ROLE_WRITER]}\n```\n* Les mots de passes sont chiffrés avec bcrypt.\n* L'identification se fera sur la propriété username.\n* il y a deux role ROLE_AUTHOR et ROLE_WRITER.\n* seul ROLE_AUTHOR et ROLE_WRITER pourront avoir accès à la partie d'administration.\n* les utilisateurs non authentifiés pourront accèder aux pages de login, d'enregistrement\nde \"reset\" de compte.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcedlemo%2Fsymfony_34_notes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcedlemo%2Fsymfony_34_notes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcedlemo%2Fsymfony_34_notes/lists"}