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/