Voici les commandes pour initialiser un projet Symfony avec un admin sécurisé.
Pour faire tourner Symfony 6 nous devrons installer php8.0 ou php8.1
Pour php8.0
sudo apt-get install php8.0-cli libapache2-mod-php8.0 php8.0-common php8.0-opcache php8.0-igbinary php8.0-imagick php8.0-msgpack php8.0-readline php8.0-memcached php8.0-xml php8.0-mbstring php8.0-gd php8.0-mysql php8.0-curl php8.0-intl php8.0-memcache php8.0-memcached memcached libapache2-mod-php8.0 php8.0-zip php8.0-mysql
Pour php8.1
sudo apt-get install php8.1-cli libapache2-mod-php8.1 php8.1-common php8.1-opcache php8.1-igbinary php8.1-imagick php8.1-msgpack php8.1-readline php8.1-memcached php8.1-xml php8.1-mbstring php8.1-gd php8.1-mysql php8.1-curl php8.1-intl php8.1-memcache php8.1-memcached memcached libapache2-mod-php8.1 php8.1-zip php8.1-mysql
Ensuite on installe le skeleton du projet.
php8.1 composer.phar create-project symfony/skeleton:"6.0.x-dev" skeleton-sf6
cp composer.phar skeleton-sf6/
cd skeleton-sf6
php8.1 composer.phar require webapp
On vérifie que tout fonctionne bien à ce stade en lançant le serveur web intégré à php.
php8.1 -S localhost:8000 -t public
Ce qui nous donne l’url suivante dans notre navigateur : http://localhost:8000/
On installe ensuite user-bundle qui nous est nécessaire dans
l’admin Sonata. Il va nous installer toutes les
dépendances nécessaires, y compris
Sonata/admin.
On prend soin de downgrader Syfony/translation
qui est trop récent dans notre skeleton pour Sonata
à ce jour.
php8.1 composer.phar require symfony/translation-contracts:2.5
php8.1 composer.phar require sonata-project/user-bundle:5.x-dev
php8.1 bin/console assets:install
On se heurte à un premier problème :
The child config "resetting" under "sonata_user" must be configured.
Il faut pour cela ajouter une configuration par défaut dans /config/packages/sonata_user.yaml
#####/config/packages/sonata_user.yaml
sonata_user:
class:
user: App\Entity\User
resetting:
email:
address: "test@test.com"
sender_name: Backoffice
Et faire un console/bin cache:clear
php8.1 bin/console cache:clear
On installe ensuite Sonata/admin. En installant doctrine-orm-admin-bundle on va installer automatiquement admin-bundle en évitant les conflits.
php8.1 composer.phar require sonata-project/doctrine-orm-admin-bundle
php8.1 bin/console assets:install
On contrôle notre site depuis le serveur web.
php8.1 -S localhost:8000 -t public
On devrait avoir l’erreur suivante :
An exception has been thrown during the rendering of a template ("Asset manifest file "/public/build/manifest.json" does not exist.").
Il faudra installer webpack
yarn add --dev @symfony/webpack-encore
yarn add webpack-notifier --dev
yarn encore dev
Sonata admin est correctement installé. L’authentification n’est pas encore configurée et il nous manque encore la gestion des médias et les classifications (on verra les classifications dans un article suivant).
On installe et configure les ACL.
php8.1 composer.phar require symfony/acl-bundle
On configure sonata_user.yml
#####/config/packages/sonata_user.yaml
sonata_user:
class:
user: App\Entity\User
resetting:
email:
address: "test@test.com"
sender_name: Backoffice
security_acl: true
manager_type: orm # can be orm or mongodb
On configure security.yaml
#####/config/packages/security.yaml
security:
enable_authenticator_manager: true
password_hashers:
Sonata\UserBundle\Model\UserInterface:
algorithm: auto
providers:
sonata_user_bundle:
id: sonata.user.security.user_provider
access_decision_manager:
strategy: unanimous
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
lazy: true
pattern: /admin(.*)
provider: sonata_user_bundle
context: user
switch_user: true
form_login:
login_path: sonata_user_admin_security_login
check_path: sonata_user_admin_security_check
default_target_path: sonata_admin_dashboard
logout:
path: sonata_user_admin_security_logout
target: sonata_user_admin_security_login
remember_me:
#secret: "%env(APP_SECRET)%"
secret: "123456"
lifetime: 2629746
path: /admin
access_control:
- { path: ^/admin/login$, role: PUBLIC_ACCESS }
- { path: ^/admin/logout$, role: PUBLIC_ACCESS }
- { path: ^/admin/login_check$, role: PUBLIC_ACCESS }
- { path: ^/admin/request$, role: PUBLIC_ACCESS }
- { path: ^/admin/check-email$, role: PUBLIC_ACCESS }
- { path: ^/admin/reset/.*$, role: PUBLIC_ACCESS }
- { path: ^/admin/, role: ROLE_ADMIN }
role_hierarchy:
ROLE_ADMIN:
- ROLE_USER
- ROLE_SONATA_ADMIN
- ROLE_SONATA_USER_ADMIN_USER_VIEW
ROLE_SUPER_ADMIN:
- ROLE_ADMIN
- ROLE_ALLOWED_TO_SWITCH
On ajoute les routes de l’admin.
#####/config/route.yaml
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
On créé notre entité user.
#/src/Entity/User.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sonata\UserBundle\Entity\BaseUser;
/**
* @ORM\Entity
* @ORM\Table(name="user__user")
*/
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
protected $id;
}
On met à jour le schéma de notre base de données et on crée notre utilisateur admin.
php8.1 bin/console doctrine:schema:update --force
php8.1 bin/console sonata:user:create admin admin@test.com admin123456
php8.1 bin/console sonata:user:promote --super-admin admin
On relance notre serveur et on teste l’url : http://localhost:8000/admin/dashboard
php8.1 -S localhost:8000 -t public
On est bien redirigé vers la mire de login.
L’utilisateur est bien autorisé à rentrer dans l’admin.
On configure maintenant la gestion des médias.
On crée 3 entités : Gallery, GalleryItem, et Media
dans une structure de répertoires à part pour ne pas
polluer notre application. On fera la même chose pour
UserBundle et ClassificationBundle.
Nous allons tout placer
dans src/Application/Sonata/MediaBundle/Entity
<?php
declare(strict_types=1);
namespace App\Application\Sonata\MediaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sonata\MediaBundle\Entity\BaseGallery;
/**
* @phpstan-extends BaseGallery<GalleryItem>
*
* @ORM\Entity
* @ORM\Table(name="media__gallery")
*/
class Gallery extends BaseGallery
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
public function getId(): ?int
{
return $this->id;
}
}
<?php
declare(strict_types=1);
namespace App\Application\Sonata\MediaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sonata\MediaBundle\Entity\BaseGalleryItem;
/**
* @ORM\Entity
* @ORM\Table(name="media__gallery_media")
*/
class GalleryItem extends BaseGalleryItem
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
public function getId(): ?int
{
return $this->id;
}
}
<?php
declare(strict_types=1);
namespace App\Application\Sonata\MediaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sonata\MediaBundle\Entity\BaseMedia;
use App\Application\Sonata\ClassificationBundle\Entity\SonataClassificationCategory as SonataClassificationCategory;
/**
* @ORM\Entity
* @ORM\Table(name="media__media")
*/
class Media extends BaseMedia
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* @var SonataClassificationCategory|null
*/
protected ?object $category = null;
public function getId(): ?int
{
return $this->id;
}
public function getCategory(): ?object
{
return $this->category;
}
public function setCategory(?object $category = null): void
{
$this->category = $category;
}
}
On ajoute la configuration dans sonata_media.yaml
sonata_media:
class:
media: App\Application\Sonata\MediaBundle\Entity\Media
gallery: App\Application\Sonata\MediaBundle\Entity\Gallery
gallery_item: App\Application\Sonata\MediaBundle\Entity\GalleryItem
default_context: default
db_driver: doctrine_orm
providers:
file:
allowed_extensions: [jpg, png, jpeg, pdf, ogv, mp4, webm]
allowed_mime_types:
- image/pjpeg
- image/jpeg
- image/png
- image/x-png
- application/pdf
- application/x-pdf
- application/ogg
- video/mp4
- video/webm
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
On ajoute notre structure de surcharge de MediaBundle dans config/packages/doctrine.yaml
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '13'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
App\Application\Sonata\MediaBundle:
is_bundle: false
dir: '%kernel.project_dir%/src/Application/Sonata/MediaBundle/Entity'
prefix: 'App\Application\Sonata\MediaBundle\Entity'
alias: App\Application\Sonata\MediaBundle
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
On met à jour le schéma de la base de données.
php8.1 bin/console doctrine:schema:update --force
On configure notre sonata_admin.yml par défaut.
sonata_admin:
title: Backoffice
title_logo: /bundles/sonataadmin/images/logo_title.png
show_mosaic_button: false
security:
handler: sonata.admin.security.handler.role
options:
default_admin_route: edit
html5_validate: false
global_search:
admin_route: edit
breadcrumbs:
child_admin_route: edit
dashboard:
groups:
users:
label: Users
icon: <i class="fa fa-users"></i>
on_top: true
items:
- sonata.user.admin.user
media:
label: Media
icon: <i class="fa fa-photo"></i>
on_top: true
items:
- sonata.media.admin.media
Et tout fonctionne.