Aquí están los comandos para inicializar un proyecto Symfony con un administrador seguro.
Para ejecutar Symfony 6 necesitaremos instalar php8.0 o php8.1
Para 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
Para 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
Luego instalamos el esqueleto del proyecto.
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
En este punto, comprobamos que todo funciona lanzando el servidor web incorporado de PHP.
php8.1 -S localhost:8000 -t public
Esto nos da la siguiente URL en nuestro navegador: http://localhost:8000/
![Selection_249 Selection_249](/uploads/media/default/0001/01/e39c2433dfc43c19d23588419b89a4702ca778cb.png)
A continuación, instalamos el user-bundle, que es necesario para el administrador de Sonata. Instalará todas las dependencias necesarias, incluyendo Sonata/admin.
Nos aseguramos de rebajar Symfony/translation que es demasiado reciente en nuestro esqueleto para Sonata en este momento.
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
Nos encontramos con un primer problema:
The child config "resetting" under "sonata_user" must be configured.
Para esto, necesitamos agregar una configuración predeterminada en /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
Y hacemos un console/bin cache:clear
php8.1 bin/console cache:clear
Luego instalamos Sonata/admin. Al instalar doctrine-orm-admin-bundle instalaremos automáticamente admin-bundle, evitando cualquier conflicto.
php8.1 composer.phar require sonata-project/doctrine-orm-admin-bundle
php8.1 bin/console assets:install
Comprobamos nuestro sitio desde el servidor web.
php8.1 -S localhost:8000 -t public
Deberíamos obtener el siguiente error:
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á correctamente instalado. La autenticación aún no está configurada y todavía faltan la gestión de medios y las clasificaciones (veremos las clasificaciones en un artículo futuro).
![image image](/uploads/media/default/0001/01/6636b8f40c696eb08ce8096a2e71be95e2ee7f7e.png)
Instalamos y configuramos los ACLs.
php8.1 composer.phar require symfony/acl-bundle
Configuramos 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
Configuramos 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
Agregamos las rutas de 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
Creamos nuestra entidad de usuario.
#/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;
}
Actualizamos el esquema de la base de datos y creamos nuestro usuario 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
Reiniciamos nuestro servidor y probamos la URL: http://localhost:8000/admin/dashboard
php8.1 -S localhost:8000 -t public
Somos correctamente redirigidos a la pantalla de inicio de sesión.
![Selection_260 Selection_260](/uploads/media/default/0001/01/36d1d69e8cc65dc71b8d4b10789f1158227af404.png)
El usuario está autorizado para entrar en el admin.
![image-1 image-1](/uploads/media/default/0001/01/1c8743551ca54ef8d6af34446c512840f9e101c1.png)
Ahora configuramos la gestión de medios.
Creamos 3 entidades: Galería, Item de Galería y Medio en una estructura de directorios separada para no abarrotar nuestra aplicación. Haremos lo mismo para UserBundle y ClassificationBundle.
Colocaremos todo en 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;
}
}
Agregamos la configuración en 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
Agregamos nuestra estructura de override para MediaBundle en 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
Actualizamos el esquema de la base de datos.
php8.1 bin/console doctrine:schema:update --force
Configuramos nuestro sonata_admin.yml predeterminado.
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
Y todo funciona.
![image-2 image-2](/uploads/media/default/0001/01/0591a0121eb0066350c574bf0b3cb8fffdfdd704.png)
![image-3 image-3](/uploads/media/default/0001/01/1103a095dfee190102d8b22adbdc1cb4e94cb65b.png)