Install and configure Sonata Admin on Symfony 4


    Welcome all. This article will talk about Symfony 4 and Sonata Admin.


    During the installation process, I encountered a lot of inaccuracies in the documentation and the documentation itself was scattered in several places. Here I will consider the whole process, from the creation of the project to the authorization along with authentication.


    Some parts of the settings were taken from the official documentation, some were taken from the comments on GitHub, where the installation problems were discussed. Possible pitfalls and ways around them are also painted.


    Creating a symfony project


    $ composer create-project symfony/skeleton sonatademo

    $ cd sonatademo

    To use the Symfony Embedded Web Server, you must install the Symfony Client .


    We start.


    $ symfony serve

    We follow the link http://127.0.0.1:8000/ and we will receive the standard Symfony greeting. So everything works correctly.



    Install Sonata Admin


    $ composer require sonata-project/admin-bundle

    Sonata Admin and DB


    In order to interact with the database, you need to install one of three libraries:



    In this article, I use SonataDoctrineORMAdminBundlewhat is more than enough to work with MySQLor Sqlite.


    Install SonataDoctrineORMAdminBundle.


    $ composer require sonata-project/doctrine-orm-admin-bundle

    Now set up work with Sqlite.


    Open the .env file and ###> doctrine/doctrine-bundle ###change it in the section DATABASE_URL.


    DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"

    Since Symfony Flex does almost all the work for us during the installation, it remains to do a few manipulations to get the final result.


    # config/packages/framework.yaml
    framework:
        translator: { fallbacks: ['en'] }

    We carry out


    $ composer dump-env dev

    Now we follow the link http://127.0.0.1:8000/admin and we see an empty standard administrative interface.



    Entity Creation


    Let's create two entities for example, connected by a one-to-many relationship.


    addresses = new ArrayCollection();
        }
        public function getAddresses()
        {
            return $this->addresses;
        }
        /**
         * @return string
         */
        public function getTitle(): ?string
        {
            return $this->title;
        }
        /**
         * @param string $title
         * @return City
         */
        public function setTitle(string $title): City
        {
            $this->title = $title;
            return $this;
        }
        /**
         * @return string
         */
        public function getDescription(): ?string
        {
            return $this->description;
        }
        /**
         * @param string $description
         * @return City
         */
        public function setDescription(string $description): City
        {
            $this->description = $description;
            return $this;
        }
        /**
         * @return bool
         */
        public function isBig(): ?bool
        {
            return $this->isBig;
        }
        /**
         * @param bool $isBig
         * @return City
         */
        public function setIsBig(bool $isBig): City
        {
            $this->isBig = $isBig;
            return $this;
        }
        public function __toString()
        {
            return $this->title;
        }
    }

    title;
        }
        /**
         * @param string $title
         * @return Address
         */
        public function setTitle(string $title): Address
        {
            $this->title = $title;
            return $this;
        }
        /**
         * @return string
         */
        public function getDescription(): ?string
        {
            return $this->description;
        }
        /**
         * @param string $description
         * @return Address
         */
        public function setDescription(string $description): Address
        {
            $this->description = $description;
            return $this;
        }
        /**
         * @return City
         */
        public function getCity(): ?City
        {
            return $this->city;
        }
        /**
         * @param City $city
         * @return Address
         */
        public function setCity(City $city)
        {
            $this->city = $city;
            return $this;
        }
        public function __toString()
        {
            return $this->title;
        }
    }

    We synchronize with the database.


    bin/console doctrine:schema:create

    Entity Configuration for Sonata Admin


    It is necessary for each entity to create a separate file with a description of how Sonata Admin should work.


    add('title', TextType::class);
            $formMapper->add('description', TextareaType::class);
            $formMapper->add('isBig', CheckboxType::class);
        }
        protected function configureDatagridFilters(DatagridMapper $datagridMapper)
        {
            $datagridMapper->add('title');
            $datagridMapper->add('isBig');
        }
        protected function configureListFields(ListMapper $listMapper)
        {
            $listMapper->addIdentifier('title');
            $listMapper->addIdentifier('isBig');
        }
    }

    add('title', TextType::class);
            $formMapper->add('description', TextareaType::class);
            $formMapper->add('city', ModelType::class, [
                'class' => City::class,
                'property' => 'title',
            ]);
        }
        protected function configureDatagridFilters(DatagridMapper $datagridMapper)
        {
            $datagridMapper->add('title');
        }
        protected function configureListFields(ListMapper $listMapper)
        {
            $listMapper->addIdentifier('title');
        }
    }

    These classes need to be described in service.yaml.


    # config/service.yaml
    services:
        ...
        App\Admin\CityAdmin:
            arguments: [~, App\Entity\City, ~]
            tags:
                - { name: sonata.admin, manager_type: orm, label: City }
        App\Admin\AddressAdmin:
            arguments: [~, App\Entity\Address, ~]
            tags:
                - { name: sonata.admin, manager_type: orm, label: Address }

    If errors occur when entering the administrative part, for example Unable to generate a URL for the named route, then you need to clean the application cache and try again.


    bin/console cache:clear

    Теперь, если перейти по ссылке http://127.0.0.1:8000/admin увидим список из двух элементов Address и City, где можно просматривать списки и создавать новые сущности.



    Авторизация и аутентификация


    Для начала создадим сущность пользователя.


    id;
        }
    }

    И заодно создадим сущность группы пользователя.


    id;
        }
    }

    После настроим Twig как движок шаблонов.


    framework:
        ...
        templating:
            engines: ['twig']

    На данный момент есть проблемы с работой Symfony Flex и FOSUserBundle. Более подробно можно узнать по ссылкам #2562, #2708 и #2801.


    Пока данные проблемы не решены, нужно сделать пару дополнительных манипуляций перед установкой Sonata User Bundle.


    # config/service.yaml
    services:
        ...
        mailer:
            alias: fos_user.mailer.noop
            public: true

    # config/packages/fos_user.yaml
    fos_user:
      db_driver: orm
      firewall_name: main
      user_class: App\Entity\User
      registration:
        confirmation:
          enabled: false
      from_email:
        address: '%env(MAILER_USER_ADDRESS)%'
        sender_name: '%env(MAILER_USER_NAME)%'
      service:
        user_manager: sonata.user.orm.user_manager
        mailer: 'fos_user.mailer.noop'
      group:
        group_class:   App\Entity\Group
        group_manager: sonata.user.orm.group_manager

    После чего, можно устанавливать бандл.


    $ composer require sonata-project/user-bundle

    Настройка ACL


    В Symfony 4 ACL вынесли в отдельный бандл symfony/acl-bundle. Поэтому его нужно отдельно установить.


    composer require symfony/acl-bundle

    # config/packages/sonata_user.yaml
    sonata_user:
        security_acl: true
        manager_type: orm

    # config/packages/acl.yaml
    acl:
        connection: default

    Настройка Doctrine


    # config/packages/doctrine.yaml
    doctrine:
        orm:
             mappings:
                 SonataUserBundle: ~
                 FOSUserBundle: ~

    Настройка работы с почтой


    Так как в рамках этой статьи работа с почтой не рассматриватеся, то укажем заглушку вместо реального сервиса.


    # config/packages/sonata_user.yaml
    sonata_user:
        mailer: fos_user.mailer.noop

    Интеграция User Bundle в Sonata Admin


    # config/routes.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

    # config/packages/security.yaml
    security:
        encoders:
            FOS\UserBundle\Model\UserInterface: sha512
        providers:
            fos_userbundle:
                id: fos_user.user_provider.username
        role_hierarchy:
            ROLE_ADMIN:       [ROLE_USER, ROLE_SONATA_ADMIN]
            ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        firewalls:
            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
        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] }
            - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
    

    # config/packages/sonata_user.yaml
    sonata_user:
      ...
      class:
        user: App\Entity\User
        group: App\Entity\Group

    Теперь можно обновить ACL и БД.


    $ bin/console acl:init

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

    Создаем супер пользователя. Указываем имя demo и пароль demo.


    $ bin/console fos:user:create --super-admin
    Please choose a username:demo
    Please choose an email:demo@demo.com
    Please choose a password:
    Created user demo

    Для корректной настройки наших Admin классов вместе с ACL нужно внести изменения в настройки.


    sonata_admin:
        ...
        security:
             handler: sonata.admin.security.handler.acl

    После запустить следующее:


    $ bin/console sonata:admin:setup-acl
    Starting ACL AdminBundle configuration
     > install ACL for App\Admin\AddressAdmin
       - add role: ROLE_APP\ADMIN\ADDRESSADMIN_GUEST, permissions: ["LIST"]
       - add role: ROLE_APP\ADMIN\ADDRESSADMIN_STAFF, permissions: ["LIST","CREATE"]
       - add role: ROLE_APP\ADMIN\ADDRESSADMIN_EDITOR, permissions: ["OPERATOR","EXPORT"]
       - add role: ROLE_APP\ADMIN\ADDRESSADMIN_ADMIN, permissions: ["MASTER"]
     > install ACL for App\Admin\CityAdmin
       - add role: ROLE_APP\ADMIN\CITYADMIN_GUEST, permissions: ["LIST"]
       - add role: ROLE_APP\ADMIN\CITYADMIN_STAFF, permissions: ["LIST","CREATE"]
       - add role: ROLE_APP\ADMIN\CITYADMIN_EDITOR, permissions: ["OPERATOR","EXPORT"]
       - add role: ROLE_APP\ADMIN\CITYADMIN_ADMIN, permissions: ["MASTER"]
     > install ACL for sonata.user.admin.user
       - add role: ROLE_SONATA_USER_ADMIN_USER_GUEST, permissions: ["LIST"]
       - add role: ROLE_SONATA_USER_ADMIN_USER_STAFF, permissions: ["LIST","CREATE"]
       - add role: ROLE_SONATA_USER_ADMIN_USER_EDITOR, permissions: ["OPERATOR","EXPORT"]
       - add role: ROLE_SONATA_USER_ADMIN_USER_ADMIN, permissions: ["MASTER"]
     > install ACL for sonata.user.admin.group
       - add role: ROLE_SONATA_USER_ADMIN_GROUP_GUEST, permissions: ["LIST"]
       - add role: ROLE_SONATA_USER_ADMIN_GROUP_STAFF, permissions: ["LIST","CREATE"]
       - add role: ROLE_SONATA_USER_ADMIN_GROUP_EDITOR, permissions: ["OPERATOR","EXPORT"]
       - add role: ROLE_SONATA_USER_ADMIN_GROUP_ADMIN, permissions: ["MASTER"]

    Теперь при попытке зайти по адресу http://127.0.0.1:8000/admin нас перенаправит на http://127.0.0.1:8000/admin/login.



    Вводим demo/demo и попадаем в административную часть.



    Итоги


    После всех манипуляций мы получили работающую административную часть Sonata Admin на Symfony 4 вместе с аутентификацией и авторизацией c помощью Sonata User + ACL.


    Thanks to all. If you have questions and comments, I will listen to them in the comments.


    Also popular now: