Search This Blog

Friday, January 18, 2013

Symfony 2 - Admin Generator - Sonata Admin Bundle

the Sonata Admin bundle -was originally written for Symfony v2.0, I have been attempting to get it installed and working for v2.1

Installation.

There is updated documentation at https://github.com/sonata-project/SonataAdminBundle/pull/814/files.
To summarise:
php composer.phar  require sonata-project/admin-bundle dev-master
php composer.phar update

You will probably need to set to minium stability to dev, not the dev-master version constraint.
I also changed the requirement for the doctrine-bundle to 1.* from 1.1.*

If you have problems updating particular components - e.g. assetic or Trig-extensions (get an error: fatal: No such remote 'composer' message) remove that item from the vendors directory.

Add to app/AppKernel.php:
        new Sonata\AdminBundle\SonataAdminBundle(),
        new Sonata\BlockBundle\SonataBlockBundle(),
        new Sonata\jQueryBundle\SonatajQueryBundle(),
        new Knp\Bundle\MenuBundle\KnpMenuBundle(),

and app/config/config.yml:
sonata_admin:
    title: Jobeet Admin

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:

Configure routing in app/config/routing.yml:
admin:
    resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
    prefix: /admin

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

I also uncommented the translator item under framework as per the documentation. You can then browse to:

http://<app-url>/app_dev.php/admin/dashboard

If the images (and css) don't appear, install the assets:
php app/console assets:install web

Doctrine Support.

Sonata is ORM independent, to install support for Doctrine, add:
php composer.phar require sonata-project/doctrine-orm-admin-bundle  dev-master 

Configuration Administration.


Step 1 Link the Required Models.

Create CRUD controllers for the models you want to have administered. These extend the Sonata CRUD controllers, these map the model and adminstration sections (forms,list,show) for ProductRange:
// src/Test/WebsiteBundle/Controller/ProductRangeAdminController.php
namespace Test\WebsiteBundle\Controller;

use Sonata\AdminBundle\Controller\CRUDController as Controller;

class ProductRangeAdminController extends Controller
{
}

Step 2 Confgure Services.

Configure services in Resourse/config/services.yml
    test.websitebundle.admin.productrange:
        class: Test\WebsiteBundle\Admin\ProductRangeAdmin
        tags:
            - { name: sonata.admin, manager_type: orm, group: website, label: ProductRange }
        arguments: [null, Test\WebsiteBundle\Entity\ProductRange, TestWebsiteBundle:ProductRangeAdmin]

Now when you browse to http://<app-url>/app_dev.php/admin/dashboard, you see the classes you have mapped - togther with CRUD controls and row counts, however the forms are empty.

Step 3 Configure the CRUD Forms.

To show the required fields, edit the admin classes, for ProductRange:
class ProductRangeAdmin extends Admin
{
 
 // setup the default sort column and order
  protected $datagridValues = array(
      '_sort_order' => 'ASC',
      '_sort_by' => 'name'
  );
    
  protected function configureFormFields(FormMapper $formMapper)
  {
      $formMapper
          ->add('name')
          ->add('title')
          ->add('slug')
          ->add('description')
          ->add('shortDescription')
      ;
  }

  protected function configureDatagridFilters(DatagridMapper $datagridMapper)
  {
      $datagridMapper
          ->add('name')
          ->add('title')
      ;
  }

  protected function configureListFields(ListMapper $listMapper)
  {
      $listMapper
          ->addIdentifier('name')
          ->add('title')
      ;
  }

Adding User Authentication.

User authentication is done using the FriendsOfSymfony UserBundle The steps are:
Install bundle as normal:
./composer.phar require friendsofsymfony/user-bundle
./composer.phar require sonata/user-bundle
./composer.phar require sonata/easy-extends-bundle

load in app/AppKernel.php
           new FOS\UserBundle\FOSUserBundle(),
           new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),
           new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
Configure in config.yml, note reference to user entity in the Sonata Userbundle:
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: Application\Sonata\UserBundle\Entity\User
Add 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

soanata_user:
    resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
    prefix: /admin
Use the extends bundle to create a user bundle
php app/console sonata:easy-extends:generate SonataUserBundle

This creates a new user bundle under app/Application
Which needs to be added to app/AppKernel.php:

           new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),

I moved this from app to src.


Configure security:
Section 1 - classes and providers

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are not using acl then this line must be uncommented
    providers:
        fos_userbundle:
            id: fos_user.user_manager

Section 2 - firewalls:

    firewalls:
        # -> custom firewall for the admin area of the URL
        admin:
            pattern:      /admin(.*)
            form_login:
                provider:       fos_userbundle
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
            logout:
                path:           /admin/logout
            anonymous:    true
        # -> end custom configuration
        # defaut login area for standard users
        main:
            pattern:      .*
            form_login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                failure_path:   null
            logout:       true
            anonymous:    true

Section 3 - acl:

   # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/_wdt, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/_profiler, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        # -> custom access control for the admin area of the URL
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login-check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        # -> end
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/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 }

Update the schema:
php app/console doctrine:schema:update --force


Note: if you get the error:

[Doctrine\DBAL\DBALException]                                                               
  Unknown column type "json" requested.
Add to the config.yml:


doctrine:
    dbal:
        ...
        types:
            json: Sonata\Doctrine\Types\JsonType


You might need to install the sonata doctrine extensions:

        "sonata-project/doctrine-extensions": "dev-master",

Also note, the schema update command should return a status similar to:
Updating database schema...
Database schema updated successfully! "5" queries were executed

If it doesn't, check the ApplicationSonataUserBundle is loaded in AppKernel.
If the bundle is getting loaded check if auto_mapping is configured for the ORM, either:
Short form:
    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        auto_mapping: true

Longer form:
    orm:
        auto_generate_proxy_classes: "%kernel.debug%"

        default_entity_manager: default
        entity_managers:
          default:
            auto_mapping: true
            mappings:
              ACOCUSAWebsiteBundle: ~





Final Steps.

clear the cache and install any missing assets:

php app/console assets:install web
php app/console cache:clear


Then you can add 2 users, a 'super-admin' and a regular user:

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

php app/console fos:user:create testuser test@example.com password


Final Step, check translation configuration and add form text:
In app/config/config.yml, uncomment and edit:
    translator:      { fallback: "en" }

Create the file: ../Application/Sonata/UserBundle/Resources/translations/SonataUserBundle.en.yml
# app/Application/Sonata/UserBundle/Recources/translations/SonataUserBundle.en.yml
form:
  label_username:             Username
  label_email:                Email
  label_plain_password:       Password (Plain)
  label_groups:               Groups
  label_roles:                Roles
  label_locked:               Locked
  label_expired:              Expired
  label_enabled:              Enabled
  label_credentials_expired:  Credentials Expired
  label_name:                 Name
list:
  label_username:             Username
  label_email:                Email
  label_enabled:              Enabled
  label_locked:               Locked
  label_created_at:           Created At
  label_roles:                Roles
  label_name:                 Name
filter:
  label_username:             Username
  label_locked:               Locked
  label_email:                Email
  label_id:                   ID
  label_name:                 Name

If you get an error similar to: The locale resource bundle could not be loaded for locale "en", check you have done the translation settings in config.yml.
Also check that you have the intl php extension installed.

The symfony config.php script will warn if you have an older version of the icu libraries. On a Centos 5.4 server, the version installed by yum was version 3.0. I downloaded and installed v50.1site.icu-project.org download page icu library from


Further Customisation.

Once running you are likely to want to customise the admin forms.
This is done by editing and Admin/<Entity>Admin.php classes.
This page has some more information. In particular Sonata will base the field type on the Doctrine column type.
Also by default all fields are assumed as required (ie. required==true).
To override a field, for example to set a field to be a true/false field, edit the configFormFields method:

protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('partNo') ->add('productGroup') ->add('description') ->add('length') ->add('slotSize') ->add('intakeArea') ->add('lockingType') ->add('enabled', 'sonata_type_boolean') ; }

This is then handled by the class ./vendor/sonata-project/admin-bundle/Sonata/AdminBundle/Form/Type/BooleanType.php; looking at the source for this you can see that
    const TYPE_YES = 1;

    const TYPE_NO = 2;

Which may not be what you expect.

Adding __toString Methods.


Also you are likely to want to added __toString methods to each of the classes handled by the Admin bundle. This is needed for display when editing a object and to enable assignment of child/parent mapping through a drop down menu.
For example:

    /**
     * Get string value
     *
     * @return string
     */
    public function __toString()
    {
        return $this->title;
    }


2 comments:

  1. I just like the post shared and it would be worth when we see people giving their valuable feedback which motivates the author.Thanks for sharing.for more knowledge- https://www.facebook.com/hiremagentodeveloper

    ReplyDelete
  2. Thank you very much!!! Works perfectly!

    ReplyDelete