Symfony 4 - Créer un blog pas à pas - Créer une interface d'administration

14 novembre 2019 - : MVC Tutoriel Symfony Live-Coding - : 12 commentaires - : 540 - Tutoriel Framework Base de données MVC Symfony Live-Coding

Visualisez les fichiers de cette série sur GitHub

Après avoir mis en place notre site, il est souvent nécessaire de créer une interface d'administration, qui permettra de créer, modifier, et supprimer différents types de contenus.

Cette interface d'administration peut être créée de toute pièce ou peut être créée en utilisant un bundle disponible sur Symfony, appelé Easy Admin, que nous utiliserons ici.

Avant toute chose, nous aurons besoin d'installer différents outils qui nous permettront :

  • De générer automatiquement les "slugs"
  • De générer automatiquement les dates de création et mise à jour
  • De créer des articles en utilisant un éditeur "Wysiwyg" qui permettra de mettre le texte en forme
  • D'ajouter une image pour illustrer l'article

Création des slugs

Voir cette partie en vidéo

Lors de l'ajout d'un article, nous allons générer une version simplifiée de son titre qui sera utilisée dans les routes pour générer des urls qui seront plus compréhensibles. Il s'agit d'un "slug". Le slug ne contient ni majuscules, ni accents, ni espaces ni caractères spéciaux.

Pour commencer nous allons installer les extensions Doctrine qui nous permettront d'effectuer cette tâche

composer require stof/doctrine-extensions-bundle

Une fois ces extensions installées, nous allons activer l'extension "Sluggable".

Celà se passe dans le fichier "config/packages/stof_doctrine_extensions.yaml", fichier à créer si il n'est pas présent

# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
stof_doctrine_extensions:
    default_locale: fr_FR
    orm:
        default:
            sluggable: true

Nous allons ensuite ajouter le "use" ci-dessous dans la ou les entités contenant des slugs

use Gedmo\Mapping\Annotation as Gedmo;

Enfin, dans ces mêmes entités, nous allons modifier l'annotation de la propriété "slug"

/**
 * @Gedmo\Slug(fields={"titre"})
 * @ORM\Column(length=128, unique=true)
 */
private $slug;

Il est également nécessaire de supprimer le "setter" setSlug.

Mise en place des dates de création et mise à jour

Voir cette partie en vidéo

Lors de l'ajout d'un article, nous allons générer automatiquement la date de création et la date de mise à jour.

Pour commencer nous allons installer les extensions Doctrine qui nous permettront d'effectuer cette tâche (inutile si vous avez effectué la partie précédente)

composer require stof/doctrine-extensions-bundle

Une fois ces extensions installées, nous allons activer l'extension "Timestampable".

Celà se passe dans le fichier "config/packages/stof_doctrine_extensions.yaml", fichier à créer si vous n'avez pas effectué la partie précédente

# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
stof_doctrine_extensions:
    default_locale: fr_FR
    orm:
        default:
            timestampable: true

Nous allons ensuite ajouter le "use" ci-dessous dans la ou les entités contenant des dates

use Gedmo\Mapping\Annotation as Gedmo;

Enfin, dans ces mêmes entités, nous allons modifier l'annotation des propriétés de date

    /**
     * @var \DateTime $created_at
     * 
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(type="datetime")
    */
    private $created_at;

    /**
     * @var \DateTime $updated_at
     *
     * @Gedmo\Timestampable(on="update")
     * @ORM\Column(type="datetime")
     */
    private $updated_at;

Il est également nécessaire de supprimer les "setters" setCreatedAt et setUpdatedAt si ils existent.

L'éditeur Wysiwig

Voir cette partie en vidéo

Nous utiliserons un éditeur appelé CKEditor, disponible en téléchargement pour tout projet sur le site officiel, et adapté à Symfony par le groupe "Friends of Symfony", sous le nom FOSCkeditor.

Installer CKEditor

Pour procéder à l'installation de FOSCkeditor, nous allons utiliser le terminal et composer.

La 1ère commande à entrer dans le terminal va récupérer CKEditor et l'intégrer à Symfony et au dossier vendor.

composer require friendsofsymfony/ckeditor-bundle

Il faudra ensuite télécharger les ressources nécessaires au fonctionnement de CKEditor (HTML, CSS, Javascript) et les installer.

php bin/console ckeditor:install

Enfin, nous allons installer toutes les ressources nécessaires dans le dossier "public"

php bin/console assets:install public

Voilà CKEditor maintenant installé.

Configurer CKEditor

Une fois installé, CKEditor est configurable directement depuis le fichier "config/packages/fos_ckeditor.yaml" qui a été créé automatiquement

Par défaut, il contient les lignes suivantes

# Read the documentation: https://symfony.com/doc/current/bundles/FOSCKEditorBundle/index.html

twig:
    form_themes:
        - '@FOSCKEditor/Form/ckeditor_widget.html.twig'

Nous allons y ajouter les configurations qui nous intéressent, et principalement la liste des options qui seront proposées aux utilisateurs.

Dans l'exemple ci-dessous, nous créons une configuration appelée "main_config" en ajoutant quelques lignes dans le fichier

# Read the documentation: https://symfony.com/doc/current/bundles/FOSCKEditorBundle/index.html

twig:
    form_themes:
        - '@FOSCKEditor/Form/ckeditor_widget.html.twig'

fos_ck_editor:
    configs:
        main_config:
            toolbar:
                - { name: "styles", items: ['Bold', 'Italic', 'Underline', 'Strike', 'Blockquote', '-', 'Link', '-', 'RemoveFormat', '-', 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'Image', 'Table', '-', 'Styles', 'Format','Font','FontSize', '-', 'TextColor', 'BGColor', 'Source'] }

L'upload d'images

Voir cette partie en vidéo

Pour pouvoir envoyer l'image d'illustration lors de l'ajout d'un article, il va être nécessaire de modifier l'entité "Articles" afin de prendre en compte cette option. A ce stade, notre entité "Articles" contient une propriété de type texte pour stocker le nom du fichier.

Le bundle Vich Uploader va nous permettre de paramétrer l'envoi de fichiers de façon simple.

Installer Vich Uploader

Pour installer le bundle Vich Uploader, nous allons utiliser composer.

composer require vich/uploader-bundle

Après cette installation, nous allons configurer l'emplacement des fichiers après l'upload.

Cette configuration sera à effectuer dans plusieurs fichiers YAML.

Le premier d'entre eux, "services.yaml", contiendra le paramètre qui formalisera le chemin vers le dossier d'upload ainsi qu'un nom qui sera attribué à ce chemin, ici "featured_images". "app.path" permet de spécifier que notre valeur est un chemin.

parameters:
    app.path.featured_images: /uploads/images/featured

Dans le deuxième fichier, "vich_uploader.yaml" nous allons configurer le "mapping", c'est à dire le lien entre le chemin et le nom attribué dans l'uploader. Les différentes valeurs se comprennent comme ceci :

  • uri_prefix : préfixe de l'url, deviendra "/uploads/images/featured" dans notre exemple
  • namer : permet de spécifier quel système de "nommage" nous souhaitons, ici, nommage unique, deux fichiers ne pourront pas avoir le même nom
  • upload_destination : A quel endroit les images doivent-elles être stockées
vich_uploader:
    db_driver: orm
    
    mappings:
        featured_images:
            uri_prefix: '%app.path.featured_images%'
            namer: Vich\UploaderBundle\Naming\UniqidNamer
            upload_destination: '%kernel.project_dir%/public%app.path.featured_images%'

Enfin, nous allons mettre à jour notre entité pour que Vich Uploader fasse le lien entre la propriété "featured_image" et le fichier envoyé.

Les modifications à apporter dans l'entité "Articles" sont les suivantes

// Au début du fichier
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;


// Juste au dessus de la classe Articles
/**
 * @ORM\Entity(repositoryClass="App\Repository\ArticlesRepository")
 * @Vich\Uploadable
 */
class Articles


// Près de la propriété $featured_image
/**
 * @ORM\Column(type="string", length=255)
 * @var string
 */
private $featured_image;

/**
 * @Vich\UploadableField(mapping="featured_images", fileNameProperty="featured_image")
 * @var File
 */
private $imageFile;


// Dans les Getters/setters
public function setImageFile(File $image = null)
{
    $this->imageFile = $image;

    if ($image) {
        $this->updated_at = new \DateTime('now');
    }
}

public function getImageFile()
{
    return $this->imageFile;
}

public function getFeaturedImage()
{
    return $this->featured_image;
}

public function setFeaturedImage($featured_image)
{
    $this->featured_image = $featured_image;

    return $this;
}

Voilà, le terrain est prêt pour créer l'interface d'administration.

L'interface d'administration

Voir cette partie en vidéo

Installer Easy Admin

Nous allons maintenant passer à l'installation d'Easy Admin afin de mettre en place l'interface d'administration du site.

Comme pour les bundles précédents, nous allons utiliser composer

composer require admin

Configurer Easy Admin

Une fois installé, la configuration d'Easy Admin se déroule dans le fichier "config/packages/easy_admin.yaml".

Nous allons pouvoir définir de nombreuses propriétés générales ainsi que pour chacune de nos entités.

Les propriétés générales

Quelques propriétés nous permettent de définir le titre de l'administration, l'affichage de l'utilisateur, les menus situés à gauche, entre autres, très nombreuses. L'exemple ci-dessous en définit certaines.

easy_admin:
    # On définit le nom de l'interface d'administration
    site_name: 'Gestion de mon blog'
    # On définit l'affichage de l'utilisateur
    user:
        display_name: true
        display_avatar: false
    design:
        # Ces lignes sont utiles pour CKEditor
        form_theme:
            - "@EasyAdmin/form/bootstrap_4.html.twig"
            - "@FOSCKEditor/Form/ckeditor_widget.html.twig"
        # Ces lignes définiront notre menu
        menu:
            - { label: 'Articles' }
            - { entity: 'Articles', label: 'Articles', icon: 'book' }
            - { entity: 'Categories', label: 'Catégories', icon: 'tag' }
            - { entity: 'MotsCles', label: 'Mots Clés', icon: 'tag' }
            - { label: 'Utilisateurs' }
            - { entity: 'Users', label: 'Utilisateurs', icon: 'user' }
    formats:
        # Ici on définit le format des dates
        datetime: 'd/m/Y à H:i'

Les entités

Pour chacune des entités, nous pourrons définir quelles informations s'affichent dans la liste, lesquelles s'affichent dans le formulaire d'ajout, entre autres.

L'exemple ci-dessous correspond à la configuration de l'entité "Articles"

    entities:
        Articles:
            # Correspond au fichier Articles.php
            class: App\Entity\Articles
            # On définit ci-dessous le contenu de la liste qui affichera les articles et les critères de tri
            list:
                fields:
                    - id
                    - titre
                    # Le champ ci-dessous affichera l'image de l'article
                    - { property: 'featured_image', label: 'Image', type: 'image', base_path: '%app.path.featured_images%' }
                    - { property: 'created_at', label: 'Créé' }
                    # Les catégories et les mots-clé sont listés ci-dessous
                    - { property: 'categories', label: 'Catégories', type: 'array'}
                    - { property: 'motsCles', label: 'Mots-Clés', type: 'array'}
                sort: ['created_at', 'desc']
            # On définit ci-dessous le contenu du formulaire d'ajout ou modification d'article
            form:
                fields:
                    - titre
                    # Affichage de l'éditeur Wysiwyg
                    - { property: 'contenu', type: 'fos_ckeditor', type_options: { config_name: 'main_config' }}
                    # Affichage du champ d'ajout d'image
                    - { property: 'imageFile', type: 'vich_image', label: 'Image' }
                    - users
                    # Les catégories et mots-clés peuvent s'afficher avec une sélection multiple
                    - { property: 'categories', label: 'Catégories', type: 'entity', type_options: { class: 'App\Entity\Categories', multiple: true,by_reference: false, attr: { data-widget: 'select2' }}}
                    - { property: 'motsCles', label: 'Mots Clés', type: 'entity', type_options: { class: 'App\Entity\MotsCles', multiple: true,by_reference: false, attr: { data-widget: 'select2' }}}

Les autres entités pourront être affichées de la même façon.

Pour l'entité Users, nous allons également pouvoir modifier le rôle de l'utilisateur.

La configuration de cette entité sera donc la suivante (avec rôles user et admin)

Users:
    class: App\Entity\Users
    label: 'Utilisateurs'
    list:
        fields:
            - id
            - email
            - { property: 'roles', label: 'Rôles', type: json_array}
    form:
        fields:
            - email
            - { property: 'roles', label: 'Rôles', type: choice, type_options: {expanded: true, multiple: true, choices: {'Utilisateur':'ROLE_USER', 'Administrateur':'ROLE_ADMIN'}}}

Gérer les relations

Dans le cas d'entités avec relations, comme par exemple les entités "Articles" et "Categories", il est nécessaire de définir quelle valeur sera affichée pour les catégories lors de l'ajout ou la modification d'un article.

Nous allons afficher le nom de la catégorie, option la plus logique.

Afin de permettre cet affichage, il sera nécessaire d'implémenter la méthode magique "__toString" dans les entités qui le nécessitent, "Categories" dans cet exemple.

Nous ajouterons donc cette méthode comme ci-dessous

public function __toString()
{
    return $this->nom;
}

Protéger notre administration

Voir cette partie en vidéo

L'interface d'administration ne doit pas être accessible par tous les utilisateurs.

Afin de la protéger, nous allons demander l'authentification des utilisateurs avant de pouvoir y accéder, et vérifier qu'ils sont administrateurs.

Pour ce faire, rendez-vous à la fin de "config/packages/security.yaml" et retirez le # devant la ligne ci-dessous

- { path: ^/admin, roles: ROLE_ADMIN }

C'est tout, merci de votre fidélité.

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder aux forums de Nouvelle-Techno.fr ou au serveur Discord pour une entraide par chat

Visualisez les fichiers de cette série sur GitHub

Partager

Partager sur Facebook Partager sur Twitter Partager sur LinkedIn

Commentaires

Ecrire un commentaire

Thom a écrit le 21 novembre 2019 à 17:05

Re bonjour,

J'ai recréee l'entité Category, mais hélas j'ai toujours la meme erreur.

Merci de votre aide.

Répondre

Nouvelle-Techno.fr a répondu le 21 novembre 2019 à 17:27

Re bonjour,

Est-il possible d'avoir le code complet de l'entité "Category" ?

Merci

Répondre

Thom a écrit le 21 novembre 2019 à 15:18

Re bonjour,

Dans mon entité Category, en principe je n'ai qu'une propriété $name pour le nom des categories.

Je vais donc recréer l'entité category.

Merci

Répondre

billy a écrit le 20 novembre 2019 à 09:14

Bonjour,

 

J'ai suivi le tuto complet, toutes les étapes en faisant exactement la même chose que vous, je vous remerci d'ailleur car j'ai beaucoup appris.

 

En revanche il y a un problème que je n'arrive pas à solutionner 

Quand je me reconnect après avoir décommenter la partie : - { path: ^/admin, roles: ROLE_ADMIN }

J'ai le message d'erreur " Access Denied. ".

Du coupe je n'ai plus accès à la page http://127.0.0.1:8000/ /admin

J'ai tenté de vérifier si mon compte été bien en Admi ce qui est le cas dans la Base de donnée : 

roles :

" [
    "ROLE_USER",
    "ROLES_ADMIN"
]

Comment puis-je faire pour que mon accés soit autorisé ? 

 

Voici ma page security.yaml :

 

***************

 

security:

    encoders:

        App\Entity\Users:

            algorithm: auto

 

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers

    providers:

        # used to reload user from session & other features (e.g. switch_user)

        app_user_provider:

            entity:

                class: App\Entity\Users

                property: email

    firewalls:

        dev:

            pattern: ^/(_(profiler|wdt)|css|images|js)/

            security: false

        main:

            anonymous: true

            guard:

                authenticators:

                    - App\Security\UsersAuthentificatorAuthenticator

            logout:

                path: app_logout

                # where to redirect after logout

                # target: app_any_route

 

            # activate different ways to authenticate

            # https://symfony.com/doc/current/security.html#firewalls-authentication

 

            # https://symfony.com/doc/current/security/impersonating_user.html

            # switch_user: true

 

    # Easy way to control access for large sections of your site

    # Note: Only the *first* access control that matches will be used

    access_control:

         - { path: ^/admin, roles: ROLE_ADMIN }

        # - { path: ^/profile, roles: ROLE_USER }

 

 

***************

Merci de votre aide.

Répondre

Nouvelle-Techno.fr a répondu le 20 novembre 2019 à 09:18

Bonjour,

Attention, le rôle de l'administrateur est "ROLE_ADMIN" et pas "ROLES_ADMIN"

Répondre

billy a répondu le 20 novembre 2019 à 09:46

Résolu ! 

 

Merci beaucoup.

Répondre

Thom a écrit le 19 novembre 2019 à 22:02

Bonsoir,

Je suis votre tuto sur la partie Admin, cependant j'essai de faire ma partie admin avec le bundle sonataAdmin.

Aussi, je rencontre un problème, je comprends ce que ça veut dire mais hélas j'arrive pas à trouver la solution..

J'ai toujours ce message :

Expected argument of type "string", "App\Entity\Category" given at property path "category".

Voici mon entité Category

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity(repositoryClass="App\Repository\CategoryRepository")
 */
class Category
{

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

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

    

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

    public function getBlog_Posts()
    {
        return $this->blog_Posts;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getCategory(): ?string
    {
        return $this->category;
    }

    public function setCategory(string $category): self
    {
        $this->category = $category;

        return $this;
    }

    public function getProperty(): ?string
    {
        return $this->property;
    }

    public function setProperty(string $property): self
    {
        $this->property = $property;

        return $this;
    }
}

Merci de votre aide.

Bonne soirée.

Répondre

Nouvelle-Techno.fr a répondu le 20 novembre 2019 à 08:14

Bonjour,

Il semble que dans l'entité, le "setter" pour la propriété "category" attende une chaîne de caractères, et que ce soit un élément de type "Category" qui lui est envoyé. Qui fait appel à "setCategory" et comment ?

Répondre

Thom a répondu le 21 novembre 2019 à 11:14

Bonjour,

Je reviens vers vous hélas je suis bloqué.

Je ne vois pas ce qu'il y a lieu de faire.

Merci de votre réponse.

 

Répondre

Nouvelle-Techno.fr a répondu le 21 novembre 2019 à 11:50

Bonjour,

Dans un premier temps, il faut identifier le contrôleur qui appelle le "setter" setCategory, pour savoir ce qui lui est envoyé.

Comment avez-vous créé l'entité "Category" ?

Répondre

Thom a répondu le 21 novembre 2019 à 13:31

Bonjour,

Pour créer l'entité Category, je suis passé par la console:

php bin/console make:entity Category

puis j'ai renseigné les prpriétés

puis j'ai fais la relation avec l'ntité blogpost.

Pour l'instant j'ai de controller. Lerreur vient donc? peut-être de là?

étant entendu que je suis dans la partie admin(creation du dashboard).

Merci encore.

Répondre

Nouvelle-Techno.fr a répondu le 21 novembre 2019 à 13:37

Bonjour,

C'est surprenant, il semble manquer des propriétés dans l'entité.

Serait-il envisageable de recréer l'entité ?

Merci

Répondre

Ecrire un commentaire