Parlons de votre projet
PHP Symfony

Symfony 4 / Sonata : Initialiser un projet Symfony 4 + Sonata + FosUser + Media

Voici les commandes pour initialiser un projet symfony, avec un admin sécurisé. On commence par installer le skeleton du projet.composer create-project symfony/skeleton nom-du-projet On rajoute les briques nécessaires : cd ...

Symfony 4 / Sonata : Initialiser un projet Symfony 4 + Sonata + FosUser + Media

Voici les commandes pour initialiser un projet symfony, avec un admin sécurisé.

On commence par installer le skeleton du projet.
composer create-project symfony/skeleton nom-du-projet

On rajoute les briques nécessaires :

cd nom-du-projet
composer require symfony/debug-pack --no-update
composer require symfony/maker-bundle --dev --no-update
composer require sonata-project/doctrine-orm-admin-bundle --no-update
composer require templating --no-update
composer require symfony/translation --no-update
composer require doctrine/doctrine-fixtures-bundle --dev --no-update
composer require sonata-project/notification-bundle --no-update
composer require jms/serializer-bundle --no-update
composer require sonata-project/user-bundle --no-update
composer require sonata-project/doctrine-orm-admin-bundle --no-update
composer require symfony/serializer-pack --no-update
composer require swiftmailer-bundle --no-update
composer require symfony/apache-pack --no-update
composer require migrations --no-update
composer require symfony/var-dumper --no-update
composer require phpoffice/phpspreadsheet --no-update
composer require pixassociates/sortable-behavior-bundle --no-update
composer require sonata-project/media-bundle --no-update
composer require liip/imagine-bundle --no-update
composer require sonata-project/intl-bundle --no-update
composer require sonata-project/formatter-bundle  --no-update
composer require annotations  --no-update
composer update
composer dump-autoload

Ensuite dans /config/packages/framework.yaml ajouter

framework:
    secret: '%env(APP_SECRET)%'
    #csrf_protection: true
    #http_method_override: true

    # Enables session support. Note that the session will ONLY be started if you read or write from it.
    # Remove or comment this section to explicitly disable session support.
    session:
        handler_id: null
        cookie_secure: auto
        cookie_samesite: lax

    #esi: true
    #fragments: true
    php_errors:
        log: true
    translator: { fallbacks: ['%locale%'] }
    serializer:
        enabled: true
    templating:
        engines: ['twig', 'php']

Ensuite dans /config/packages/doctrine.yaml ajouter

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'
        types:
            json: Sonata\Doctrine\Types\JsonType

        # IMPORTANT: You MUST configure your server version,
        # either here or in the DATABASE_URL env var (see .env file)
        #server_version: '5.7'
    orm:
        auto_generate_proxy_classes: true
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        mappings:
            ApplicationSonataMediaBundle: ~
            SonataMediaBundle: ~
            App:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App

Dans /config/packages/sonata_media.yaml ajouter

sonata_media:
    class:
        media: App\Application\Sonata\MediaBundle\Entity\Media
        gallery: App\Application\Sonata\MediaBundle\Entity\Gallery
        gallery_has_media: App\Application\Sonata\MediaBundle\Entity\GalleryHasMedia
    db_driver: doctrine_orm
    default_context: default
    contexts:
        default:
            providers:
                - sonata.media.provider.dailymotion
                - sonata.media.provider.youtube
                - sonata.media.provider.image
                - sonata.media.provider.file
                - sonata.media.provider.vimeo

            formats:
                small: { width: 100 , quality: 70}
                big:   { width: 500 , quality: 70}

    cdn:
        server:
            path: /upload/media

    filesystem:
        local:
            # Directory for uploads should be writable
            directory: "%kernel.project_dir%/public/upload/media"
            create: false
    providers:
        file:
            allowed_extensions: ['pdf', 'txt', 'rtf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'odg', 'odp', 'ods', 'odc', 'odf', 'odb', 'odt', 'odp', 'csv', 'jpg', 'png', 'jpeg', 'svg']
            allowed_mime_types: ['application/pdf', 'application/vnd.openxmlformats-officedocument.presentationml.presentation',  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',  'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/vnd.ms-word.document.macroEnabled.12', 'application/vnd.ms-word.template.macroEnabled.12',  'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'application/vnd.ms-excel.sheet.macroEnabled.12','application/vnd.ms-excel.template.macroEnabled.12', 'application/vnd.ms-excel.addin.macroEnabled.12',  'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.openxmlformats-officedocument.presentationml.template',  'application/vnd.openxmlformats-officedocument.presentationml.slideshow',  'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',  'application/vnd.ms-powerpoint.template.macroEnabled.12', 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'application/vnd.ms-access', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/x-pdf', 'application/rtf', 'text/html', 'text/rtf', 'text/plain', 'application/vnd.ms-powerpoint', 'image/svg+xml', 'image/gif', 'image/jpeg', 'image/pjpeg', 'image/png']
            service:    sonata.media.provider.file
            resizer:    false
            filesystem: sonata.media.filesystem.local
            cdn:        sonata.media.cdn.server
            generator:  sonata.media.generator.default
            thumbnail:  sonata.media.thumbnail.liip_imagine
        image:
            allowed_extensions: ['gif', 'svg', 'jpg', 'jpeg', 'png']
            allowed_mime_types: ['image/svg+xml', 'image/gif', 'image/jpeg', 'image/pjpeg', 'image/png']
            #thumbnail: sonata.media.thumbnail.format          # default value
            #thumbnail: sonata.media.thumbnail.consumer.format # can be used to dispatch the resize action to async task
            thumbnail: sonata.media.thumbnail.liip_imagine    # use the LiipImagineBundle to resize the image


    resizer:
        simple:
            mode:  outbound

    pixlr:
        enabled:  false
        referrer: Sonata Project

liip_imagine:
     # valid drivers options include "gd" or "gmagick" or "imagick"
    driver: "imagick"
    
    filter_sets:
        test_banner:
            quality: 75
            filters:              
                relative_resize: { widen: 898,  heighten: 610 }
                crop: {size: [ 898, 610 ], start: [ 0, 0 ] }
                background : { size : [898, 610], position : center, color : '#FFFFFF' }
                
        test_thumb_gallery:
            quality: 100
            filters:
                thumbnail: { size: [288, 192], mode: outbound, allow_upscale: true}
                crop: {size: [288, 192], start: [ 0, 0 ] }
                background : { size : [288, 192], position : center, color : '#FFFFFF' }
                
        test_small_center:
            quality: 75
            filters:
                thumbnail: { size: [319, 207], mode: outbound, allow_upscale: true}
                crop: {size: [ 319, 207 ], start: [ 0, 0 ] }
                background : { size : [319, 207], position : center, color : '#FFFFFF' }
                
        test_medium_center_background:
            quality: 75
            filters:
                background : { size : [141, 93], position : center, color : '#FFFFFF' }
                thumbnail: { size: [141, 93], mode: outbound, allow_upscale: true}
                crop: {size: [ 141, 93 ], start: [ 0, 0 ] }
                
        test_widden:
            quality: 75
            filters:
                relative_resize: {widen: 796}

            

Ajouter la configuration du formatter

#config/packages/sonata_formatter.yml

sonata_formatter:
    default_formatter: richhtml
    formatters:
        markdown:
            service: sonata.formatter.text.markdown
            extensions:
                - sonata.formatter.twig.control_flow
                - sonata.formatter.twig.gist
        #        - sonata.media.formatter.twig #keep this commented unless you are using media bundle.


        text:
            service: sonata.formatter.text.text
            extensions:
                - sonata.formatter.twig.control_flow
                - sonata.formatter.twig.gist
        #        - sonata.media.formatter.twig


        rawhtml:
            service: sonata.formatter.text.raw
            extensions:
                - sonata.formatter.twig.control_flow
                - sonata.formatter.twig.gist
        #        - sonata.media.formatter.twig


        richhtml:
            service: sonata.formatter.text.raw
            extensions:
                - sonata.formatter.twig.control_flow
                - sonata.formatter.twig.gist
        #        - sonata.media.formatter.twig


        twig:
            service: sonata.formatter.text.twigengine
            extensions: [] # Twig formatter cannot have extensions
            
    ckeditor:
        templates:
            browser: '@App/Ckeditor/browser.html.twig'
            upload: '@App/Ckeditor/upload.html.twig'
            
            
fos_ck_editor:
    default_config: default
    configs:
        default:
            # default toolbar plus Format button
            toolbar:
            - [Bold, Italic, Underline, -, 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock' ,-, Cut, Copy, Paste, PasteText, PasteFromWord, -, Undo, Redo, -, NumberedList, BulletedList, -, Outdent, Indent, -,Blockquote, -, Image, Link, Unlink, Table]
            - [Format, Maximize, Source, Iframe]

           

Et pour terminer l’implémentation de CKeditor il faut ajouter le theme dans twig

#twig.yaml
twig:
    default_path: '%kernel.project_dir%/templates'
    debug: '%kernel.debug%'
    strict_variables: '%kernel.debug%'
    exception_controller: null

    form_themes:
        - '@SonataFormatter/Form/formatter.html.twig'

Ajouter les routes de media bundle dans config/routes.yaml

#index:
#    path: /
#    controller: App\Controller\DefaultController::index

gallery:
    resource: '@SonataMediaBundle/Resources/config/routing/gallery.xml'
    prefix: /media/gallery

media:
    resource: '@SonataMediaBundle/Resources/config/routing/media.xml'
    prefix: /media
    
#for 3.x change admin with admin_area    
admin:
    resource: "@SonataAdminBundle/Resources/config/routing/sonata_admin.xml"
    prefix: /admin

_sonata_admin:
    resource: .
    type: sonata_admin
    prefix: /admin

Ensuite dans /config/routes/sonata_admin.yaml ajouter

sonata_user_admin_security:
    resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
    prefix: /admin

sonata_user_admin_resetting:
    resource: '@SonataUserBundle/Resources/config/routing/admin_resetting.xml'
    prefix: /admin/resetting    
    
admin_area:
    resource: "@SonataAdminBundle/Resources/config/routing/sonata_admin.xml"
    prefix: /admin

_sonata_admin:
    resource: .
    type: sonata_admin
    prefix: /admin    

Ensuite dans /config/packages/sonata_user.yaml ajouter

sonata_user:
    security_acl: false
    manager_type: orm # can be orm or mongodb
    class:
        user: App\Application\Sonata\UserBundle\Entity\User
        group: App\Application\Sonata\UserBundle\Entity\Group
    

Ensuite dans /config/packages/security.yaml ajouter

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    providers:
        in_memory: { memory: null }
        fos_userbundle:
            id: fos_user.user_provider.username

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        # -> custom firewall for the admin area of the URL
        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
            logout:
                path:           /admin/logout
                target:         /admin/login
            anonymous:          true

        # -> end custom configuration

        # default login area for standard users

        # This firewall is used to handle the public login area
        # This part is handled by the FOS User Bundle
        main:
            pattern:             .*
            context:             user
            form_login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                failure_path:   null
            logout:             true
            anonymous:          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:
        # Admin login page needs to be accessed without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Secured part of the site
        # This config requires being logged for the whole site and having the admin role for the admin part.
        # Change these rules to adapt them to your needs
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN, ROLE_SUPER_ADMIN] }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

Ensuite dans /config/packages/swiftmailer.yaml ajouter

swiftmailer:
    url: '%env(MAILER_URL)%'
    #pour envoyer directement un email sans attendre
    #spool: { type: 'memory' }
    #pour utiliser le spooler d'envois.
    #dans ce cas, inscrire une cron : 
    #* * * * *  cd /var/www/monprojet/ && APP_ENV=prod php bin/console swiftmailer:spool:send --message-limit=3
    spool:
        type: file
        path: '%kernel.project_dir%/var/spool'

Ensuite dans /config/packages/fos_user.yaml ajouter

fos_user:
    db_driver:      orm # can be orm or odm
    firewall_name:  main
    #user_class:     Sonata\UserBundle\Entity\BaseUser
    user_class:     App\Application\Sonata\UserBundle\Entity\User

    group:
        #group_class:   Sonata\UserBundle\Entity\BaseGroup
        group_class:   App\Application\Sonata\UserBundle\Entity\Group
        group_manager: sonata.user.orm.group_manager # If you're using doctrine orm (use sonata.user.mongodb.group_manager for mongodb)

    service:
        user_manager: sonata.user.orm.user_manager

    from_email:
        address: "contact@partitech.com"
        sender_name: "contact@partitech.com"

Configurer les locales pour avoir l’interface en français dans  /config/packages/translation.yaml

framework:
    default_locale: fr
    translator:
        default_path: '%kernel.project_dir%/translations'
        fallbacks:
            - en

Puis configurer le timezone :

#config/packages/sonata_intl.yaml
sonata_intl:
    timezone:
        default: Europe/Paris
        locales:
            fr:    Europe/Paris
            en_UK: Europe/London

Il faut ensuite étendre le bundle :

bin/console sonata:easy-extends:generate SonataUserBundle --dest=src --namespace_prefix=App

Il faut aussi générer les entity de média bundle

bin/console sonata:easy-extends:generate --dest=src SonataMediaBundle --namespace_prefix=App

Et l’enregistrer dans les bundle à loader (/config/bundles.php) ajouter l’appel à ApplicationSonataUserBundle :

<?php

return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Sonata\DatagridBundle\SonataDatagridBundle::class => ['all' => true],
    Sonata\CoreBundle\SonataCoreBundle::class => ['all' => true],
    Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
    Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
    Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true],
    Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true],
    Sonata\AdminBundle\SonataAdminBundle::class => ['all' => true],
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
    Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle::class => ['all' => true],
    Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
    Sonata\EasyExtendsBundle\SonataEasyExtendsBundle::class => ['all' => true],
    Sonata\NotificationBundle\SonataNotificationBundle::class => ['all' => true],
    FOS\UserBundle\FOSUserBundle::class => ['all' => true],
    Sonata\UserBundle\SonataUserBundle::class => ['all' => true],
    Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
    Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
    Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
    Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
    Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
    Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
    JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
    Pix\SortableBehaviorBundle\PixSortableBehaviorBundle::class => ['all' => true],
    Sonata\MediaBundle\SonataMediaBundle::class => ['all' => true],
    App\Application\Sonata\UserBundle\ApplicationSonataUserBundle::class => ['all' => true],
    App\Application\Sonata\MediaBundle\ApplicationSonataMediaBundle::class => ['all' => true],
Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true],
Sonata\IntlBundle\SonataIntlBundle::class => ['all' => true],
];

Il faut exclure le répertoire Application de l’autowireing des services

# config/services.yaml

services:
    App\:
        resource: '../src/*'
        exclude: '../src/{Entity,Tests,Application}'

Derniers points, configurer la base de donnée dans le fichier .env

DATABASE_URL=mysql://root:toor@127.0.0.1:3306/delef?serverVersion=5.7

Lancer la création des tables :

php bin/console doctrine:schema:update --force

Installer les assets :

php bin/console assets:install
php bin/console ckeditor:install

Créer le répertoire de stockage des fichiers

mkdir -p public/uploads/media

Vider le cache :

php bin/console cache:clear

Et créez l’utilisateur admin

php bin/console fos:user:create admin mail@mail.com p@ssword --super-admin

Adapter l’exemple à la version installée

Les exemples Symfony et Sonata restent attachés aux versions indiquées dans l’article. Symfony 4.x et Symfony 6.2 ne sont plus maintenus : pour un projet actuel, utilisez une branche maintenue et la documentation correspondant exactement à vos dépendances.

php bin/console about
composer show symfony/framework-bundle sonata-project/admin-bundle

Avant de reprendre le code, contrôlez les signatures, les services, les routes et les templates concernés. Ajoutez ensuite un test fonctionnel couvrant les autorisations, le code HTTP et le résultat visible.

Référence : versions Symfony maintenues.

Partager cet article