Création d'un back-office sur Symfony 2.3 avec SonataAdminBundle et FOSUserBundle [SF2 back-office part 1.]

Création d'un back-office de gestions de utilisateurs et groupes avec SonataAdminBundle et FOSUserBundle

Publié le 03/10/2014

Création d'un back-office sur Symfony 2.3 avec SonataAdminBundle et FOSUserBundle [SF2 back-office part 1.] Description : Création d'un back-office de gestions de utilisateurs et groupes avec SonataAdminBundle et FOSUserBundle Keywords : développement, web, php, symfony2, sonata, sonataadminbundle, fos, fosuserbundle

1 - Installation de Symfony 2.3

sudo php composer.phar create-project symfony/framework-standard-edition /Applications/MAMP/htdocs/sf_blog_test/ 2.3.0

2 - Création d'un bundle User

php app/console generate:bundle --namespace=BlogTest/UserBundle

Enregistrement du bundle dans le kernel :

nano AppKernel.php


$bundles = array(
// ...
// app
new BlogTest\MainBundle\BlogTestMainBundle(),
// ...
);

 

3 - Installation des vendors [FOSUserBundle SonataAdminBundle]

On va ajouter à la section "require" de notre fichier composer.json les bundle suivants :

 


{
    "name": "symfony/framework-standard-edition",
    "license": "MIT",
    "type": "project",
    "description": "The \"Symfony Standard Edition\" distribution",
    "autoload": {
        "psr-0": { "": "src/" }
    },
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.3.*",
        "symfony/assetic-bundle": "2.3.*",
        "symfony/swiftmailer-bundle": "2.3.*",
        "symfony/monolog-bundle": "2.3.*",

        "sensio/distribution-bundle": "2.3.*",
        "sensio/framework-extra-bundle": "2.3.*",
        "sensio/generator-bundle": "2.3.*",

        "doctrine/orm": ">=2.2.3,<2.4-dev",
        "doctrine/doctrine-bundle": "1.2.*",
        "doctrine/doctrine-fixtures-bundle": "2.2.*",

        "twig/extensions": "1.0.*",
        "gedmo/doctrine-extensions": "v2.3.9",
        "incenteev/composer-parameter-handler": "~2.0",
        "nervo/yuicompressor": "2.4.8",

        "friendsofsymfony/user-bundle": "v1.3.4",
        "sonata-project/core-bundle": "~2.2",
        "sonata-project/admin-bundle": "~2.2",
        "sonata-project/user-bundle": "~2.2",
        "sonata-project/doctrine-orm-admin-bundle": "~2.1",
        "sonata-project/easy-extends-bundle": "2.1.7"
    },
    "require-dev": {
        "liip/functional-test-bundle": "1.0.2"
    },
    "scripts": {
        "post-install-cmd": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
        ],
        "post-update-cmd": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
        ]
    },
    "config": {
        "bin-dir": "bin"
    },
    "minimum-stability": "stable",
    "extra": {
        "symfony-app-dir": "app",
        "symfony-assets-install": "symlink",
        "symfony-web-dir": "web",
        "incenteev-parameters": {
            "file": "app/config/parameters.yml"
        },
        "branch-alias": {
            "dev-master": "1.0"
        }
    }
}

 

On lance un composer update :

sudo php composer.phar update

Ensuite on enregistre les bundles dans le fichier AppKernel, dans le tableau $bundles = array() :

 


$bundles = array(
            // symfony
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),

            // doctrine
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),

            // user
            new FOS\UserBundle\FOSUserBundle(),
            new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),

            // admin
            new Knp\Bundle\MenuBundle\KnpMenuBundle(),
            new Sonata\CoreBundle\SonataCoreBundle(),
            new Sonata\BlockBundle\SonataBlockBundle(),
            new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
            new Sonata\AdminBundle\SonataAdminBundle(),
            new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
        );

 

Mettre à jour les droits sur les dossiers :

sudo chown -R "user" sf_blog_test sudo chmod -R 755 sf_blog_test sudo chmod -R 777 sf_blog_test/app/cache sf_blog_test/app/logs

4 - Créations des entité User et Group

On va créer deux entités User et Group qui vont étendre les entités des bundle SonataUserBundle et FOSUserBundle.

Créations de l'entité User :

 


<?php

namespace BlogTest\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\UserBundle\Entity\BaseUser;

/**
 * User
 *
 * @ORM\Table(name="fos_user")
 * @ORM\Entity(repositoryClass="BlogTest\UserBundle\Entity\Repository\UserRepository")
 */
class User extends BaseUser
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var
     *
     * @ORM\ManyToMany(targetEntity="Group", inversedBy="users")
     * @ORM\JoinTable(name="users_groups")
     */
    protected $groups;

    /**
     * @var
     *
     * @ORM\Column(name="address", type="text", nullable=true)
     */
    protected $address;

    /**
     * @var
     *
     * @ORM\Column(name="zip_code", type="string", length=255, nullable=true)
     */
    protected $zipCode;

    /**
     * @var
     *
     * @ORM\Column(name="city", type="string", length=255, nullable=true)
     */
    protected $city;

    /**
     * @param mixed $address
     */
    public function setAddress($address)
    {
        $this->address = $address;
    }

    /**
     * @return mixed
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * @param mixed $zipCode
     */
    public function setZipCode($zipCode)
    {
        $this->zipCode = $zipCode;
    }

    /**
     * @return mixed
     */
    public function getZipCode()
    {
        return $this->zipCode;
    }

    /**
     * @param mixed $city
     */
    public function setCity($city)
    {
        $this->city = $city;
    }

    /**
     * @return mixed
     */
    public function getCity()
    {
        return $this->city;
    }


}

 

On va créer un repository vide pour notre entité User :

nano src/BlogTest/UserBundle/Entity/Repository/UserRepository.php


<?php

namespace BlogTest\UserBundle\Entity\Repository;

use Doctrine\ORM\EntityRepository;

/**
 * UserRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class UserRepository extends EntityRepository
{
}

 

Création de l'entité Group :

 


<?php

namespace BlogTest\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Sonata\UserBundle\Entity\BaseGroup;

/**
 * Group
 *
 * @ORM\Table(name="fos_group")
 * @ORM\Entity
 */
class Group extends BaseGroup
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var
     *
     * @ORM\ManyToMany(targetEntity="User", mappedBy="groups")
     */
    protected $users;
}

 

5 - Surcharge de la class UserAdmin de SonataAdminUser bundle

On va créer une classe UserAdmin :

nano src/BlogTest/UserBundle/Admin/UserAdmin.php


<?php

namespace BlogTest\UserBundle\Admin;

use Sonata\UserBundle\Admin\Entity\UserAdmin as BaseAdmin;

class UserAdmin extends BaseAdmin
{

    /**
     * {@inheritdoc}
     */
    protected $baseRoutePattern = 'utilisateurs';
}

 

6 - Surcharge du controller UserAdminController de SonataAdminUser bundle

On va créer une class UserAdminController :

nano src/BlogTest/UserBundle/Controller/UserAdminController.php


<?php

namespace BlogTest\UserBundle\Controller;

use Sonata\AdminBundle\Controller\CRUDController;

class UserAdminController extends CRUDController
{
    /**
     * {@inheritdoc}
     */
    protected $baseRoutePattern = 'utilisateurs';
}

 

7 - Configuration de du fichier de config de l'app :

On va configurer les directives du fichier config.yml de notre app en modifiant les sections concernées comme suit :

nano app/config/config.yml


#Translator
framework:
    translator:      ~
	session:
        	handler_id: session.handler.native_file
        	save_path: "%kernel.root_dir%/sessions"
        	name: BLOGTEST

# Doctrine Configuration
doctrine:
    dbal:
        driver:   %database_driver%
        host:     %database_host%
        port:     %database_port%
        dbname:   %database_name%
        user:     %database_user%
        password: %database_password%
        charset:  UTF8
        # if using pdo_sqlite as your database driver, add the path in parameters.yml
        # e.g. database_path: %kernel.root_dir%/data/data.db3
        # path:     %database_path%
        types:
            json: Sonata\Doctrine\Types\JsonType

# FOS User Bundle
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: BlogTest\UserBundle\Entity\User
    group:
        group_class: BlogTest\UserBundle\Entity\Group
    from_email:
        address: pa@foulquier.info
        sender_name: paf

# SONATA User Bundle
sonata_user:
    security_acl: false
    manager_type: orm
    class:
        user: BlogTest\UserBundle\Entity\User
        group: BlogTest\UserBundle\Entity\Group
    impersonating:
        route: homepage
    admin:
        user:
            class: BlogTest\UserBundle\Admin\UserAdmin
            controller: SonataAdminBundle:CRUD
            translation: SonataUserBundle

sonata_block:
    default_contexts: [cms]
    blocks:
        sonata.admin.block.admin_list:
            contexts: [admin]
        sonata.block.service.text: ~
        sonata.block.service.action: ~
        sonata.block.service.rss: ~
        sonata.admin.block.search_result: ~
	  sonata.user.block.menu: ~

 

Créer un dossier session vide avec un fichier gitkeep à la racine de app afin de pouvoir commiter un dossier de session vide sur le server. Ne pas oublier d'exclure le contenu du dossier dans le fichier ".gitignore".

mkdir app/sessions chmod -R 777 app/sessions

8 - Configuration de security.yml

On configure le fichier security.yml de façon à ce que le login soit pris en charge par FOSUserBundle et que les rôles sur nos entités soient gérés par SonataUserBundle. On va donc modifier les directives de notre fichier security.yml comme suit :

 


security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        ROLE_ADMIN:       [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN: [ROLE_SONATA_ADMIN, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT

    providers:
        fos_userbundle:
            id: fos_user.user_manager

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        # -> custom firewall for the admin area of the URL
        admin:
            pattern:      /admin/
            form_login:
                provider:       fos_userbundle
                login_path:     sonata_user_admin_security_login
                use_forward:    true
                use_referer: true
                check_path:     sonata_user_admin_security_check
                #failure_path:   null
            logout:
                path:           sonata_user_admin_security_logout
                target:         homepage
            anonymous:    true
        # -> end custom configuration

        # defaut login area for standard users
        main:
            pattern:            ^/
            form_login:
                provider:       fos_userbundle
                login_path:     fos_user_security_login
                use_forward:    true
                use_referer:    true
                check_path:     fos_user_security_check
                #failure_path:   null
                default_target_path: fos_user_profile_show
            logout:
                path: fos_user_security_logout
                target: homepage
            anonymous:    true

    access_control:
        - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, roles: ROLE_ADMIN }
        - { path: ^/connexion, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/inscription, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/mot-de-passe-oublie, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/profile, role: IS_AUTHENTICATED_FULLY }
        - { path: ^/deconnexion$, role: IS_AUTHENTICATED_FULLY }

 

9 - Création de la base de données et mise à jour du schéma

Lancer les commandes suivantes :

php app/console doctrine:database:create php app/console doctrine:schema:update --force

10 - Nettoyage du UserBundle

Mise en place d'une route, action de controller et vue pour la home de notre site.

Dans le fichier routing.yml du user bundle, ajouter la route suivante :

nano src/BlogTest/UserBundle/Ressources/config/routing.yml


blog_test_user_homepage:
    path:     /
    defaults: { _controller: BlogTestUserBundle:Default:index }

 

Dans le DefaultController :

nano src/BlogTest/UserBundle/Controller/DefaultController.php


<?php

namespace BlogTest\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    public function indexAction()
    {
        return $this->render('BlogTestUserBundle:Default:index.html.twig');
    }
}

 

Dans la vue index :

nano src/BlogTest/UserBundle/Resources/views/Default/index.html.twig Hello !

A partir de ce moment, on va pouvoir charger la home de notre site dans l'environnement de dev : http://blog-test.dev/app_dev.php

11 - Ajouts des routes

On ajoute les routes pour notre UserBundle et le SonataAdminBundle dans le fichier de routing de l'app :

nano app/config/routing.yml


# USER BUNDLE
BlogTest_user:
    resource: "@BlogTestUserBundle/Resources/config/routing.yml"

# SONATA ADMIN BUNDLE
admin:
    resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
    prefix:   /admin

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

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

sonata_user_impersonating:
    pattern:  /
    defaults: { _controller: SonataPageBundle:Page:catchAll }

 

On ajoute ensuite les routes de FOSUserBundle et SonataAdminBundle dans le fichier routing.yml du UserBundle :

nano app/src/BlogTest/UserBundle/Resources/config/routing.yml


# FOS USER AND SONATA ADMIN ROUTES
fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /change-password

admin:
    resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
    prefix:   /admin

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

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

sonata_user_impersonating:
    pattern: /
    defaults: { _controller: SonataPageBundle:Page:catchAll }

 

Modification des permissions du dossier de session :

sudo chmod -R 777 app/sessions

12 - Création d'un utilisateur admin

php app/console fos:user:create admin admin@example.com admin --super-admin

On met à jour les assets :

rm -rf app/cache/* php app/console assets:install --symlink php app/console assetic:dump

13 - Accès à l'admin

On peut dès maintenant accès à notre admin par l'url suivante > http://blog-test.dev/app_dev.php/admin/