Pro Yearly is on sale from $80 to $50! »

Building great admin panels with Symfony and SonataAdminBundle

Building great admin panels with Symfony and SonataAdminBundle

Coding an admin panel is not the problem you love to face when you wake up in the morning, but is something you need to do very often. Having an environment that allows you to implement this efficiently, is something key for a developer. Symfony and SonataAdminBundle provide a way to do it that gives you as many out-of-the-box functionalities as you could dream of, plus the chance to customize every piece -if you know how to do it. The purpose of this talk is to show how easily can you get profit of SonataAdminBundle once you know how to deal with it.

We will start with a clean installation of Symfony and SonataAdminBundle, showing how to build up form there, and how to solve the main problems and questions that you usually face when having a complex admin panel in your hands. The main purposes are to show how to use SonataAdminBundle in the most standard way, and how to build up from that point, customizing whatever you need to meet the exact requirements of the project.

281604c1a5357a164f2a9cd6e403b4e3?s=128

Victoria Quirante

February 04, 2017
Tweet

Transcript

  1. Building great admin panels with Symfony and SonataAdminBundle Victoria Quirante

  2. I work at Limenius We build tailor-made projects with Symfony

    and React Almost every project needs an admin panel We rely on SonataAdminBundle to deal with this part Victoria Quirante @vicqr victoria@limenius.com
  3. I. Introduction Or why are we here

  4. Some context “A bundle is simply a structured set of

    files within a directory that implement a single feature.“
  5. Some context Released with no ‘official’ admin panel solution “A

    bundle is simply a structured set of files within a directory that implement a single feature.“
  6. SonataAdminBundle: The author

  7. Main contributors

  8. None
  9. None
  10. Main problems historically attributed - Difficult to install

  11. Main problems historically attributed - Difficult to install - Ugly

  12. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation
  13. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation - A lot of code, difficult to dig in
  14. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation - A lot of code, difficult to dig in
  15. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation - A lot of code, difficult to dig in
  16. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation can still improve - A lot of code, difficult to dig in
  17. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation can still improve - A lot of code, difficult to dig in
  18. Main problems historically attributed - Difficult to install - Ugly

    - Poor documentation can still improve - A lot of code, difficult to dig in But that’s why we are here
  19. The problem that we want to solve...

  20. ...is to implement an admin panel

  21. What Sonata is, what it is not It is not

    Michelangelo’s David
  22. What Sonata is, what it is not It is not

    Michelangelo’s David But it is something very useful
  23. What Sonata is, what it is not It is not

    Michelangelo’s David But it is something very useful Like Symfony, like PHP
  24. Practical talk Not an exhaustive review of the documentation A

    walk-through from a clean installation showing:
  25. Practical talk Not an exhaustive review of the documentation A

    walk-through from a clean installation showing: 1. What Sonata gives you “for free”
  26. Practical talk Not an exhaustive review of the documentation A

    walk-through from a clean installation showing: 1. What Sonata gives you “for free” 2. How you customize everything from that point
  27. Facing two opposite forces

  28. Burn it ALL

  29. Do not touch ANYTHING

  30. Practical talk https://github.com/VictoriaQ/sonatademo-en

  31. II. The basic Admin Or what does SonataAdminBundle give you

    for free
  32. Installation, very briefly

  33. Install Symfony • Get the Symfony Installer • symfony new

    my_project_name • php bin/console server:run
  34. Install Symfony • Get the Symfony Installer • symfony new

    my_project_name • php bin/console server:run http://symfony.com/doc/current/setup.html Symfony documentation is very good
  35. Screenshots (II): Install Symfony

  36. Install SonataAdminBundle • composer require sonata-project/admin-bundle • composer require sonata-project/doctrine-orm-admin-bundle

    • Activate bundles • Add configuration to config.yml and routing.yml
  37. Install SonataAdminBundle • composer require sonata-project/admin-bundle • composer require sonata-project/doctrine-orm-admin-bundle

    • Activate bundles • Add configuration to config.yml and routing.yml https://sonata-project.org/bundles/admin/master/doc/reference/installation.html
  38. Screenshots (II): Install SonataAdminBundle

  39. Screenshots (II): Install SonataAdminBundle

  40. Screenshots (II): Install SonataAdminBundle

  41. How do we start?

  42. We need first to have an entity We create our

    first entity with Symfony (Doctrine)
  43. We need first to have an entity We create our

    first entity with Symfony (Doctrine) php bin/console generate:doctrine:entity
  44. We need first to have an entity /** * @ORM\Table(name="gift")

    * @ORM\Entity(repositoryClass="AppBundle\Repository\GiftRepository") */ class Gift { /** * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(name="name", type="string", length=255) */ private $name; }
  45. We need first to have an entity /** * @ORM\Table(name="gift")

    * @ORM\Entity(repositoryClass="AppBundle\Repository\GiftRepository") */ class Gift { /** * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(name="name", type="string", length=255) */ private $name; }
  46. We need first to have an entity /** * @ORM\Table(name="gift")

    * @ORM\Entity(repositoryClass="AppBundle\Repository\GiftRepository") */ class Gift { /** * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(name="name", type="string", length=255) */ private $name; }
  47. We need first to have an entity /** * @ORM\Table(name="gift")

    * @ORM\Entity(repositoryClass="AppBundle\Repository\GiftRepository") */ class Gift { /** * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(name="name", type="string", length=255) */ private $name; } And now we can start working with SonataAdminBundle
  48. Basic Admin: 2 steps, 2 minutes Have entity Gift, want

    to create Admin:
  49. Basic Admin: 2 steps, 2 minutes Have entity Gift, want

    to create Admin: 1) Create GiftAdmin class 2) Configure the service
  50. Basic Admin: 2 steps, 2 minutes Have entity Gift, want

    to create Admin: 1) Create GiftAdmin class 2) Configure the service You may want to read about Symfony’s Service Container http://symfony.com/doc/current/service_container.html
  51. Basic Admin: 2 steps, 2 minutes Have entity Gift, want

    to create Admin: 1) Create GiftAdmin class 2) Configure the service You may want to read about Symfony’s Service Container http://symfony.com/doc/current/service_container.html
  52. Basic Admin - Step 1: Create Admin class # src/AppBundle/Admin/GiftAdmin.php

    class GiftAdmin extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('name'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('name'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('name'); } }
  53. Basic Admin - Step 1: Create Admin class # src/AppBundle/Admin/GiftAdmin.php

    class GiftAdmin extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('name'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('name'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('name'); } }
  54. Basic Admin - Step 1: Create Admin class # src/AppBundle/Admin/GiftAdmin.php

    class GiftAdmin extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('name'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('name'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('name'); } }
  55. Basic Admin - Step 2: Register service # app/config/services.yml services:

    admin.gift class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, ~] tags: - { name: sonata.admin, manager_type: orm, label: Gifts }
  56. Basic Admin - Step 2: Register service # app/config/services.yml services:

    admin.gift: class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, ~] tags: - { name: sonata.admin, manager_type: orm, label: Gifts }
  57. Screenshots (II): First Admin

  58. Screenshots (II): First Admin

  59. Screenshots (II): First Admin

  60. Screenshots (II): First Admin

  61. Basic Admin - Step 3 (optional): Sidebar # app/config/config.yml sonata_admin:

    dashboard: groups: demo.admin.main: label: Admin label_catalogue: AppBundle icon: '<i class="fa fa-database"></i>' items: - admin.gift
  62. Basic Admin - Step 3 (optional): Sidebar # app/config/config.yml sonata_admin:

    dashboard: groups: demo.admin.main: label: Admin label_catalogue: AppBundle icon: '<i class="fa fa-database"></i>' items: - admin.gift
  63. Basic Admin - Step 3 (optional): Sidebar # app/config/config.yml sonata_admin:

    dashboard: groups: demo.admin.main: label: Admin label_catalogue: AppBundle icon: '<i class="fa fa-database"></i>' items: - admin.gift
  64. Screenshots (II): First Admin

  65. Screenshots (II): First Admin

  66. Ok... and what do I get?

  67. First Admin - What do I get? Create, edit, delete…

  68. Screenshots (II): First Admin - The form

  69. Basic Admin - What do I get? Create, edit, delete…

    Sortable, exportable, filterable, paginated list
  70. Basic Admin - What do I get? Create, edit, delete…

    Sortable, exportable, filterable, paginated list
  71. None
  72. Screenshots (II): First Admin - The list

  73. Screenshots (II): First Admin - The list

  74. Screenshots (II): First Admin - The list

  75. Screenshots (II): First Admin - The list

  76. Screenshots (II): First Admin - The list

  77. Screenshots (II): First Admin - The filters

  78. Basic list configuration

  79. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->add('name') ; } }
  80. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->add('name') ->add('price') ->add('description') ->add('addressee') ->add('buyer') ; } }
  81. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('price') ->add('description') ->add('addressee') ->add('buyer') ; } }
  82. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('price', 'currency', array('currency' => 'USD')) ->add('description') ->add('addressee') ->add('buyer') ; } }
  83. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('price', 'currency', array('currency' => 'USD')) ->add('description', null, array('label' => 'Details')) ->add('addressee') ->add('buyer') ; } }
  84. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('price', 'currency', array('currency' => 'USD')) ->add('description', null, array('label' => 'Details')) ->add('addressee', null, array('editable' => true)) ->add('buyer') ; } }
  85. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('name') ->add('price', 'currency', array('currency' => 'USD')) ->add('description', null, array('label' => 'Details')) ->add('addressee', null, array('editable' => true)) ->add('buyer') ; } } https://sonata-project.org/bundles/admin/master/doc/reference/field_types.html
  86. Screenshots (II): First Admin - The list

  87. Screenshots (II): First Admin - The list

  88. Screenshots (II): First Admin - The list

  89. Screenshots (II): First Admin - The list

  90. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ... ->add('_action', null, array( 'actions' => array( 'show' => array(), 'edit' => array(), 'delete' => array(), ) )) ; } }
  91. Basic list configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureListFields(ListMapper $listMapper) { $listMapper ... ->add('_action', null, array( 'actions' => array( 'show' => array(), 'edit' => array(), 'delete' => array(), ) )) ; } }
  92. Screenshots (II): First Admin - The list

  93. Screenshots (II): First Admin - The list

  94. Basic form configuration

  95. Basic form configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('name') ; } }
  96. Basic form configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('name') ->add('price') ->add('description') ->add('addressee') ->add('buyer') ; } }
  97. Screenshots (II): First Admin - The form

  98. Basic form configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('name') ->add('price') ->add('description') ->add('addressee') ->add('buyer') ; } }
  99. Basic form configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureFormFields(FormMapper $formMapper) { $formMapper ->with('Gift', array('class' => 'col-md-6')) ->add('name') ->add('price') ->add('description') ->end() ->with('Participants', array('class' => 'col-md-6')) ->add('addressee') ->add('buyer') ->end() ; } }
  100. Basic form configuration # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin {

    protected function configureFormFields(FormMapper $formMapper) { $formMapper ->with('Gift', array('class' => 'col-md-6')) ->add('name') ->add('price') ->add('description') ->end() ->with('Participants', array('class' => 'col-md-6')) ->add('addressee') ->add('buyer') ->end() ; } }
  101. Screenshots (II): First Admin - The form

  102. Basic form configuration # src/AppBundle/Admin/GiftAdmin.php protected function configureFormFields(FormMapper $formMapper) {

    $formMapper ->tab('Tab 1') ->with('Gift', array('class' => 'col-md-6')) ->add('name') ->add('price') ->add('description') ->end() ->with('Participants', array('class' => 'col-md-6')) ->add('addressee') ->add('buyer') ->end() ->end() ->tab('Tab 2') ->end() ; }
  103. Screenshots (II): First Admin - The form

  104. What if one field has a relation with another entity?

  105. Many-to-one relations # src/AppBundle/Entity/Gift.php class Gift { … /** *

    @Column(type="string") */ private $addressee; … }
  106. Many-to-one relations # src/AppBundle/Entity/Gift.php class Gift { … /** *

    @ORM\ManyToOne(targetEntity="Addressee") */ private $addressee; … }
  107. Many-to-one relations # src/AppBundle/Entity/Gift.php class Gift { … /** *

    @ORM\ManyToOne(targetEntity="Addressee") */ private $addressee; … } We don’t need to do anything in our Admin class
  108. Screenshots (II): First Admin - The form

  109. Many-to-one relations # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin { protected

    function configureFormFields(FormMapper $formMapper) { $formMapper ... ->with('Participants', array('class' => 'col-md-6')) ->add('addressee') ->add('buyer') ->end() ; } }
  110. Many-to-one relations # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin { protected

    function configureFormFields(FormMapper $formMapper) { $formMapper ... ->with('Participants', array('class' => 'col-md-6')) ->add('addressee', null) ->add('buyer', null) ->end() ; } }
  111. Many-to-one relations # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin { protected

    function configureFormFields(FormMapper $formMapper) { $formMapper ... ->with('Participants', array('class' => 'col-md-6')) ->add('addressee', 'entity', array( 'class' => 'AppBundle\Entity\Addressee')) ->add('buyer', 'entity', array( 'class' => 'AppBundle\Entity\Buyer')) ->end() ; } }
  112. Many-to-one relations # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin { protected

    function configureFormFields(FormMapper $formMapper) { $formMapper ... ->with('Participants', array('class' => 'col-md-6')) ->add('addressee', null, array( 'class' => 'AppBundle\Entity\Addressee', 'choice_label' => 'lastName')) ->add('buyer', null, array( 'class' => 'AppBundle\Entity\Buyer', 'choice_label' => 'lastName')) ->end() ; } }
  113. Many-to-one relations # src/AppBundle/Admin/GiftAdmin.php class GiftAdmin extends AbstractAdmin { protected

    function configureFormFields(FormMapper $formMapper) { $formMapper ... ->with('Participants', array('class' => 'col-md-6')) ->add('addressee', null, array( 'class' => 'AppBundle\Entity\Addressee', 'choice_label' => 'fullName')) ->add('buyer', null, array( 'class' => 'AppBundle\Entity\Buyer', 'choice_label' => 'fullName')) ->end() ; } }
  114. Screenshots (II): First Admin - The form

  115. What about the one-to-many?

  116. A collection in the form (one-to-many) # src/AppBundle/Admin/BuyerAdmin.php class BuyerAdmin

    extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper ->with('Personal Data', array('class' => 'col-md-6')) ->add('firstName') ->add('lastName') ->end() ; } }
  117. A collection in the form (one-to-many) # src/AppBundle/Admin/BuyerAdmin.php class BuyerAdmin

    extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper ->with('Personal Data', array('class' => 'col-md-6')) ->add('firstName') ->add('lastName') ->end() ->with('Payments', array('class' => 'col-md-6')) ->add('payments', 'sonata_type_collection', array( ), array( 'edit' => 'inline', 'inline' => 'table', )) ->end(); } }
  118. A collection in the form (one-to-many) # src/AppBundle/Admin/BuyerAdmin.php class BuyerAdmin

    extends AbstractAdmin { protected function configureFormFields(FormMapper $formMapper) { $formMapper ->with('Personal Data', array('class' => 'col-md-6')) ->add('firstName') ->add('lastName') ->end() ->with('Payments', array('class' => 'col-md-6')) ->add('payments', 'sonata_type_collection', array( ), array( 'edit' => 'inline', 'inline' => 'table', )) ->end(); } }
  119. Screenshots (II): A collection in the form

  120. Screenshots (II): A collection in the form

  121. Screenshots (II): A collection in the form

  122. And the many-to-many?

  123. A many-to-many in the form # src/AppBundle/Admin/GiftAdmin.php protected function configureFormFields(FormMapper

    $formMapper) { $formMapper ... ->with('Shops', array('class' => 'col-md-6')) ->add('shops', 'sonata_type_model', array( 'by_reference' => false, 'expanded' => true, 'multiple' => true, 'label' => 'Shops') ) ->end(); }
  124. A many-to-many in the form # src/AppBundle/Admin/GiftAdmin.php protected function configureFormFields(FormMapper

    $formMapper) { $formMapper ... ->with('Shops', array('class' => 'col-md-6')) ->add('shops', 'sonata_type_model', array( 'by_reference' => false, 'expanded' => true, 'multiple' => true, 'label' => 'Shops') ) ->end(); }
  125. A many-to-many in the form # src/AppBundle/Admin/GiftAdmin.php protected function configureFormFields(FormMapper

    $formMapper) { $formMapper ... ->with('Shops', array('class' => 'col-md-6')) ->add('shops', 'sonata_type_model', array( 'by_reference' => false, 'expanded' => true, 'multiple' => true, 'label' => 'Shops') ) ->end(); }
  126. Screenshots (II): Many-to-many relation

  127. Screenshots (II): Many-to-many relation

  128. Screenshots (II): Many-to-many relation

  129. Screenshots (II): Many-to-many relation

  130. Basic filters configuration

  131. Basic filters configuration # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper) {

    $datagridMapper ->add('name') ; }
  132. Basic filters configuration # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper) {

    $datagridMapper ->add('name') ->add('price') ->add('addressee', null, array(), 'entity', array( 'class' => 'AppBundle\Entity\Addressee', 'choice_label' => 'fullName')) ; }
  133. Basic filters configuration # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper) {

    $datagridMapper ->add('name') ->add('price') ->add('addressee', null, array(), 'entity', array( 'class' => 'AppBundle\Entity\Addressee', 'choice_label' => 'fullName')) ; } Careful with the number of arguments, different in configureFormFields
  134. Screenshots (II): Basic filters configuration

  135. So far, what we got “for free”

  136. Basic Admin - How much do I get? With very

    little effort you have a lot Actually, you get most of what you need
  137. Basic Admin - How much do I get? With very

    little effort you have a lot Actually, you get most of what you need But the world is not perfect… … and we will need more stuff
  138. None
  139. And what if I want to… … have a form

    with a different layout? … add something ‘weird’ in a list field? … create a section in the sidebar that is not an Admin? … add something that has nothing to do with the admin panel?
  140. And what if I want to… … have a form

    with a different layout? … add something ‘weird’ in a list field? … create a section in the sidebar that is not an Admin? … add something that has nothing to do with the admin panel? How much effort will that take? Wouldn’t it be easier to start from scratch?
  141. None
  142. III. Customizing Or how to do my own thing

  143. Customizing • Templates • Queries • Actions

  144. Customizing templates

  145. Customizing templates # app/config/config.yml sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig' add_block: 'SonataAdminBundle:Core:add_block.html.twig'

    layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  146. Customizing templates # app/config/config.yml sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig' add_block: 'SonataAdminBundle:Core:add_block.html.twig'

    layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ... https://sonata-project.org/bundles/admin/master/doc/reference/templates.html
  147. Customizing templates # app/config/config.yml sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig' add_block: 'SonataAdminBundle:Core:add_block.html.twig'

    layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  148. Customizing templates # app/config/config.yml sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig' add_block: 'SonataAdminBundle:Core:add_block.html.twig'

    layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'AppBundle:Admin:edit.html.twig' ...
  149. Customizing templates # app/config/config.yml sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig' add_block: 'SonataAdminBundle:Core:add_block.html.twig'

    layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'AppBundle:Admin:edit.html.twig' ... Find the original template and see what exactly you want to override
  150. Customizing templates # vendor/sonata-project/admin-bundle/Resources/views/CRUD/edit.html.twig {% extends 'SonataAdminBundle:CRUD:base_edit.html.twig' %}

  151. Customizing templates # vendor/sonata-project/admin-bundle/Resources/views/CRUD/edit.html.twig {% extends 'SonataAdminBundle:CRUD:base_edit.html.twig' %}

  152. Customizing templates # vendor/sonata-project/admin-bundle/Resources/views/CRUD/edit.html.twig {% extends 'SonataAdminBundle:CRUD:base_edit.html.twig' %} # vendor/sonata-project/admin-bundle/Resources/views/CRUD/base_edit.html.twig

    {% use 'SonataAdminBundle:CRUD:base_edit_form.html.twig' with form as parentForm %} {% block form %} {{ block('parentForm') }} {% endblock %}
  153. Customizing templates # vendor/sonata-project/admin-bundle/Resources/views/CRUD/edit.html.twig {% extends 'SonataAdminBundle:CRUD:base_edit.html.twig' %} # vendor/sonata-project/admin-bundle/Resources/views/CRUD/base_edit.html.twig

    {% use 'SonataAdminBundle:CRUD:base_edit_form.html.twig' with form as parentForm %} {% block form %} {{ block('parentForm') }} {% endblock %} http://twig.sensiolabs.org/doc/2.x/tags/use.html
  154. Customizing templates # vendor/sonata-project/admin-bundle/Resources/views/CRUD/edit.html.twig {% extends 'SonataAdminBundle:CRUD:base_edit.html.twig' %} # vendor/sonata-project/admin-bundle/Resources/views/CRUD/base_edit.html.twig

    {% use 'SonataAdminBundle:CRUD:base_edit_form.html.twig' with form as parentForm %} {% block form %} {{ block('parentForm') }} {% endblock %} # vendor/sonata-project/admin-bundle/Resources/views/CRUD/base_edit_form.html.twig {% block formactions %} ... {% block formactions %}
  155. Screenshots (III): Customizing templates

  156. Screenshots (III): Customizing templates

  157. Customizing templates Our custom edit would look like this #

    app/Resources/views/Admin/edit.html.twig {% extends 'SonataAdminBundle:CRUD:edit.html.twig' %} {% block formactions %} ... {% block formactions %}
  158. Customizing templates Our custom edit would look like this #

    app/Resources/views/Admin/edit.html.twig {% extends 'SonataAdminBundle:CRUD:edit.html.twig' %} {% block formactions %} ... {% block formactions %}
  159. Customizing templates Our custom edit would look like this #

    app/Resources/views/Admin/edit.html.twig {% extends 'SonataAdminBundle:CRUD:edit.html.twig' %} {% block formactions %} ... {% block formactions %} Just Twig inheritance, but important when working with Sonata
  160. What if I only want to override the template for

    one Admin?
  161. Customizing template for a single Admin # app/config/services.yml services: admin.buyer

    class: AppBundle\Admin\BuyerAdmin arguments: [~, AppBundle\Entity\Buyer, ~] tags: - { name: sonata.admin, manager_type: orm, label: Buyers }
  162. Customizing template for a single Admin # app/config/services.yml services: admin.buyer

    class: AppBundle\Admin\BuyerAdmin arguments: [~, AppBundle\Entity\Buyer, ~] tags: - { name: sonata.admin, manager_type: orm, label: Buyers } calls: - [ setTemplate, [edit, :Admin:edit_buyer.html.twig]]
  163. Screenshots (III): Customizing templates

  164. How do I customize a field on the list?

  165. Customizing a single field on the list (step 1) #

    src/AppBundle/Admin/GiftAdmin.php protected function configureListFields(ListMapper $listMapper) { $listMapper ... ; }
  166. Customizing a single field on the list (step 1) #

    src/AppBundle/Admin/GiftAdmin.php protected function configureListFields(ListMapper $listMapper) { $listMapper ... ->add('myField', 'string', array('template' => ':Admin:field_send_email.html.twig')) ; }
  167. Customizing a single field on the list (step 1) #

    src/AppBundle/Admin/GiftAdmin.php protected function configureListFields(ListMapper $listMapper) { $listMapper ... ->add('myField', 'string', array('template' => ':Admin:field_send_email.html.twig')) ; }
  168. Customizing a single field on the list (step 2) #

    app/Resources/views/Admin/field_send_email.html.twig {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %} {% block field %} <a class="btn btn-primary btn-sm" href=""> <i class="fa fa-envelope"></i> Send </a> {% endblock %}
  169. Screenshots (III): Customizing templates

  170. Customizing queries

  171. Screenshots (III): Customizing list query

  172. Customizing the list query # src/AppBundle/Admin/GiftAdmin.php public function createQuery($context =

    'list') { $query = parent::createQuery($context); $rootAlias = $query->getRootAliases()[0]; $query ->andWhere( $query->expr()->eq($rootAlias.'.delivered', ':wasDelivered')); $query->setParameter('wasDelivered', false); return $query; }
  173. Customizing the list query # src/AppBundle/Admin/GiftAdmin.php public function createQuery($context =

    'list') { $query = parent::createQuery($context); $rootAlias = $query->getRootAliases()[0]; $query ->andWhere( $query->expr()->eq($rootAlias.'.delivered', ':wasDelivered')); $query->setParameter('wasDelivered', false); return $query; }
  174. Customizing the list query # src/AppBundle/Admin/GiftAdmin.php public function createQuery($context =

    'list') { $query = parent::createQuery($context); $rootAlias = $query->getRootAliases()[0]; $query ->andWhere( $query->expr()->eq($rootAlias.'.delivered', ':wasDelivered')); $query->setParameter('wasDelivered', false); return $query; } http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html
  175. Screenshots (III): Customizing list query

  176. What about the filter queries?

  177. Screenshots (III): Customizing filter query

  178. Customizing the filter query # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper)

    { $datagridMapper ->add('myFilter', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.status != :status'); $queryBuilder->setParameter('status', 'delivered'); return true; }, 'field_type' => 'checkbox', 'label' => 'Not delivered' )); }
  179. Customizing the filter query # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper)

    { $datagridMapper ->add('myFilter', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.status != :status'); $queryBuilder->setParameter('status', 'delivered'); return true; }, 'field_type' => 'checkbox', 'label' => 'Not delivered' )); }
  180. Customizing the filter query # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper)

    { $datagridMapper ->add('myFilter', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.status != :status'); $queryBuilder->setParameter('status', 'delivered'); return true; }, 'field_type' => 'checkbox', 'label' => 'Not delivered' )); }
  181. Customizing the filter query # src/AppBundle/Admin/GiftAdmin.php protected function configureDatagridFilters(DatagridMapper $datagridMapper)

    { $datagridMapper ->add('myFilter', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.status != :status'); $queryBuilder->setParameter('status', 'delivered'); return true; }, 'field_type' => 'checkbox', 'label' => 'Not delivered' )); }
  182. Screenshots (III): Customizing filter query

  183. Screenshots (III): Customizing filter query

  184. Screenshots (III): Customizing filter query

  185. Customizing controllers

  186. Screenshots (III): Creating custom action

  187. Creating a custom action (step 1) # src/AppBundle/Controller/GiftAdminController.php use Sonata\AdminBundle\Controller\CRUDController

    as Controller; use Symfony\Component\HttpFoundation\RedirectResponse; class GiftAdminController extends Controller { public function sendEmailAction() { $regalo = $this->admin->getSubject(); $email = $regalo->getAddressee()->getEmail(); // Here code to send email $this->addFlash('sonata_flash_success', 'Email sent to '.$email); return new RedirectResponse($this->admin->generateUrl('list')); } }
  188. Creating a custom action (step 1) # src/AppBundle/Controller/GiftAdminController.php use Sonata\AdminBundle\Controller\CRUDController

    as Controller; use Symfony\Component\HttpFoundation\RedirectResponse; class GiftAdminController extends Controller { public function sendEmailAction() { $regalo = $this->admin->getSubject(); $email = $regalo->getAddressee()->getEmail(); // Here code to send email $this->addFlash('sonata_flash_success', 'Email sent to '.$email); return new RedirectResponse($this->admin->generateUrl('list')); } }
  189. Creating a custom action (step 2) # app/config/services.yml services: admin.gift:

    class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, ~] tags: - { name: sonata.admin, manager_type: orm, label: Gifts } calls: - [ setTemplate, [edit, :Admin:edit_gift.html.twig]]
  190. Creating a custom action (step 2) # app/config/services.yml services: admin.gift:

    class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, AppBundle\GiftAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Gifts } calls: - [ setTemplate, [edit, :Admin:edit_gift.html.twig]]
  191. Creating a custom action (step 3) # src/AppBundle/Admin/GiftAdmin.php protected function

    configureRoutes(RouteCollection $collection) { $collection->add('sendEmail', $this->getRouterIdParameter().'/send-email'); }
  192. Creating a custom action # app/Resources/views/Admin/field_send_email.html.twig {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}

    {% block field %} <a class="btn btn-primary btn-sm" href=""> <i class="fa fa-envelope"></i> Send </a> {% endblock %}
  193. Creating a custom action # app/Resources/views/Admin/field_send_email.html.twig {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}

    {% block field %} <a class="btn btn-primary btn-sm" href="{{ admin.generateObjectUrl('sendEmail', object) }}"> <i class="fa fa-envelope"></i> Send </a> {% endblock %}
  194. Screenshots (III): Creating custom action

  195. Screenshots (III): Creating custom batch action

  196. Creating a custom batch action (step 1) # src/AppBundle/Controller/GiftAdminController.php public

    function batchActionSendEmail($selectedModelQuery) { // Here code to send emails return new RedirectResponse($this->admin->generateUrl('list')); }
  197. Creating a custom batch action (step 1) # src/AppBundle/Controller/GiftAdminController.php public

    function batchActionSendEmail($selectedModelQuery) { // Here code to send emails return new RedirectResponse($this->admin->generateUrl('list')); }
  198. Creating a custom batch action (step 2) # src/AppBundle/Admin/GiftAdmin.php public

    function getBatchActions() { $actions = parent::getBatchActions(); if ($this->hasRoute('edit') && $this->isGranted('EDIT')) { $actions['send_email'] = [ 'label' => 'Send email', 'ask_confirmation' => false, ]; } return $actions; }
  199. Creating a custom batch action (step 2) # src/AppBundle/Admin/GiftAdmin.php public

    function getBatchActions() { $actions = parent::getBatchActions(); if ($this->hasRoute('edit') && $this->isGranted('EDIT')) { $actions['send_email'] = [ 'label' => 'Send email', 'ask_confirmation' => false, ]; } return $actions; }
  200. Screenshots (III): Creating custom batch action

  201. Screenshots (III): Creating custom batch action

  202. IV. Useful tips Or how to solve a few things

    that we need in every project
  203. How can I change the admin panel appearance? Tip 1

    / 5
  204. Screenshots (IV): Customizing the styles

  205. Screenshots (IV): Customizing the styles

  206. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP

  207. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

  208. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

    assets: stylesheets: - bundles/sonatacore/vendor/bootstrap/dist/css/bootstrap.min.css - bundles/sonatacore/vendor/ionicons/css/ionicons.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/AdminLTE.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/skins/skin-black.min.css - bundles/sonataadmin/vendor/iCheck/skins/square/blue.css - bundles/sonataadmin/vendor/jqueryui/themes/base/jquery-ui.css - bundles/sonatacore/vendor/select2/select2.css - bundles/sonataadmin/css/styles.css - bundles/sonataadmin/css/layout.css - bundles/sonataadmin/css/tree.css - bundles/sonataadmin/css/colors.css …
  209. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

    assets: stylesheets: - bundles/sonatacore/vendor/bootstrap/dist/css/bootstrap.min.css - bundles/sonatacore/vendor/ionicons/css/ionicons.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/AdminLTE.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/skins/skin-black.min.css - bundles/sonataadmin/vendor/iCheck/skins/square/blue.css - bundles/sonataadmin/vendor/jqueryui/themes/base/jquery-ui.css - bundles/sonatacore/vendor/select2/select2.css - bundles/sonataadmin/css/styles.css - bundles/sonataadmin/css/layout.css - bundles/sonataadmin/css/tree.css - bundles/sonataadmin/css/colors.css … https://sonata-project.org/bundles/admin/master/doc/reference/configuration.html
  210. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

    assets: stylesheets: - bundles/sonatacore/vendor/bootstrap/dist/css/bootstrap.min.css - bundles/sonatacore/vendor/ionicons/css/ionicons.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/AdminLTE.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/skins/skin-black.min.css - bundles/sonataadmin/vendor/iCheck/skins/square/blue.css - bundles/sonataadmin/vendor/jqueryui/themes/base/jquery-ui.css - bundles/sonatacore/vendor/select2/select2.css - bundles/sonataadmin/css/styles.css - bundles/sonataadmin/css/layout.css - bundles/sonataadmin/css/tree.css - bundles/sonataadmin/css/colors.css - css/styles.css …
  211. Screenshots (IV): Customizing the styles

  212. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

    assets: stylesheets: - bundles/sonatacore/vendor/bootstrap/dist/css/bootstrap.min.css - bundles/sonatacore/vendor/ionicons/css/ionicons.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/AdminLTE.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/skins/skin-black.min.css - bundles/sonataadmin/vendor/iCheck/skins/square/blue.css - bundles/sonataadmin/vendor/jqueryui/themes/base/jquery-ui.css - bundles/sonatacore/vendor/select2/select2.css - bundles/sonataadmin/css/styles.css - bundles/sonataadmin/css/layout.css - bundles/sonataadmin/css/tree.css - bundles/sonataadmin/css/colors.css - css/styles.css …
  213. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

    assets: stylesheets: - bundles/sonatacore/vendor/bootstrap/dist/css/bootstrap.min.css - bundles/sonatacore/vendor/ionicons/css/ionicons.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/AdminLTE.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/skins/skin-black.min.css - bundles/sonataadmin/vendor/iCheck/skins/square/blue.css - bundles/sonataadmin/vendor/jqueryui/themes/base/jquery-ui.css - bundles/sonatacore/vendor/select2/select2.css - bundles/sonataadmin/css/styles.css - bundles/sonataadmin/css/layout.css - bundles/sonataadmin/css/tree.css - bundles/sonataadmin/css/colors.css - css/styles.css …
  214. Customizing the styles # app/config/config.php sonata_admin: title: SunshinePHP title_logo: 'img/logo.png'

    assets: stylesheets: - bundles/sonatacore/vendor/bootstrap/dist/css/bootstrap.min.css - bundles/sonatacore/vendor/ionicons/css/ionicons.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/AdminLTE.min.css - bundles/sonataadmin/vendor/admin-lte/dist/css/skins/skin-black.min.css - bundles/sonataadmin/vendor/iCheck/skins/square/blue.css SonataAdminBundle uses AdminLTE, good to have a look there https://almsaeedstudio.com/
  215. Screenshots (IV): Overriding key templates

  216. Screenshots (IV): Overriding key templates

  217. Screenshots (IV): Overriding key templates

  218. Overriding some key templates # app/config/config.php sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig'

    add_block: 'SonataAdminBundle:Core:add_block.html.twig' layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  219. Overriding some key templates # app/config/config.php sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig'

    add_block: 'SonataAdminBundle:Core:add_block.html.twig' layout: 'SonataAdminBundle::standard_layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  220. Overriding some key templates # app/config/config.php sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig'

    add_block: 'SonataAdminBundle:Core:add_block.html.twig' layout: ':Admin:layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  221. Screenshots (IV): Overriding key templates

  222. Screenshots (IV): Overriding key templates

  223. Overriding some key templates # app/config/config.php sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig'

    add_block: 'SonataAdminBundle:Core:add_block.html.twig' layout: ':Admin:layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  224. Overriding some key templates # app/config/config.php sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig'

    add_block: 'SonataAdminBundle:Core:add_block.html.twig' layout: ':Admin:layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: 'SonataAdminBundle:Core:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  225. Overriding some key templates # app/config/config.php sonata_admin: templates: user_block: 'SonataAdminBundle:Core:user_block.html.twig'

    add_block: 'SonataAdminBundle:Core:add_block.html.twig' layout: ':Admin:layout.html.twig' ajax: 'SonataAdminBundle::ajax_layout.html.twig' dashboard: ':Admin:dashboard.html.twig' search: 'SonataAdminBundle:Core:search.html.twig' list: 'SonataAdminBundle:CRUD:list.html.twig' show: 'SonataAdminBundle:CRUD:show.html.twig' show_compare: 'SonataAdminBundle:CRUD:show_compare.html.twig' edit: 'SonataAdminBundle:CRUD:edit.html.twig' ...
  226. Screenshots (IV): Overriding key templates

  227. Screenshots (IV): Overriding key templates

  228. Screenshots (IV): Overriding key templates

  229. Can I create two Admins for the same Entity? Tip

    2 / 5
  230. Creating two Admins for the same Entity # src/AppBundle/ProcessedGiftAdmin.php class

    ProcessedGiftAdmin extends Admin { } Sure. We just create a new Admin class...
  231. Creating two Admins for the same Entity # src/AppBundle/ProcessedGiftAdmin.php class

    ProcessedGiftAdmin extends Admin { protected $baseRouteName = 'processed_gift'; protected $baseRoutePattern = 'processed-gift' ... } (adding these properties for the routing)
  232. Creating two Admins for the same Entity # app/config/services.yml services:

    admin.gift: class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, AppBundle:GiftAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Gifts } calls: - [ setTemplate, [edit, :Admin:edit_gift.html.twig]] ...and register the new service.
  233. Creating two Admins for the same Entity # app/config/services.yml services:

    admin.gift: class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, AppBundle:GiftAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Gifts } calls: - [ setTemplate, [edit, :Admin:edit_gift.html.twig]] admin.processed_gift: class: AppBundle\Admin\ProcessedGiftAdmin arguments: [~, AppBundle\Entity\Gift, ~] tags: - { name: sonata.admin, manager_type: orm, label: Processed } ...and register the new service.
  234. Creating two Admins for the same Entity # app/config/services.yml services:

    admin.gift: class: AppBundle\Admin\GiftAdmin arguments: [~, AppBundle\Entity\Gift, AppBundle:GiftAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Active } calls: - [ setTemplate, [edit, :Admin:edit_gift.html.twig]] admin.processed_gift: class: AppBundle\Admin\ProcessedGiftAdmin arguments: [~, AppBundle\Entity\Gift, ~] tags: - { name: sonata.admin, manager_type: orm, label: Processed } ...and register the new service.
  235. Screenshots (IV): Two Admins for same Entity

  236. Screenshots (IV): Two Admins for same Entity

  237. Screenshots (IV): Two Admins for same Entity

  238. Using Admin as a child of another Admin Tip 3

    / 5
  239. Screenshots (IV): Admin as a child

  240. Screenshots (IV): Admin as a child

  241. Admin as a child in some other Admin’s menu #

    app/config/services.yml admin.buyer: class: AppBundle\Admin\BuyerAdmin arguments: [~, AppBundle\Entity\Buyer, ~] tags: - { name: sonata.admin, manager_type: orm, label: Buyers } calls: - [ addChild, [ '@admin.payment' ] ] admin.payment: class: AppBundle\Admin\PaymentAdmin arguments: [~, AppBundle\Entity\Payment, ~] tags: - { name: sonata.admin, manager_type: orm, label: Payments }
  242. Admin as a child in some other Admin’s menu #

    app/config/services.yml admin.buyer: class: AppBundle\Admin\BuyerAdmin arguments: [~, AppBundle\Entity\Buyer, ~] tags: - { name: sonata.admin, manager_type: orm, label: Buyers } calls: - [ addChild, [ '@admin.payment' ] ] admin.payment: class: AppBundle\Admin\PaymentAdmin arguments: [~, AppBundle\Entity\Payment, ~] tags: - { name: sonata.admin, manager_type: orm, label: Payments }
  243. Admin as a child in some other Admin’s menu #

    app/config/services.yml admin.buyer: class: AppBundle\Admin\BuyerAdmin arguments: [~, AppBundle\Entity\Buyer, ~] tags: - { name: sonata.admin, manager_type: orm, label: Buyers } calls: - [ addChild, [ '@admin.payment' ] ] admin.payment: class: AppBundle\Admin\PaymentAdmin arguments: [~, AppBundle\Entity\Payment, ~] tags: - { name: sonata.admin, manager_type: orm, label: Payments }
  244. Admin as a child in some other Admin’s menu #

    app/config/services.yml admin.buyer: class: AppBundle\Admin\BuyerAdmin arguments: [~, AppBundle\Entity\Buyer, ~] tags: - { name: sonata.admin, manager_type: orm, label: Buyers } calls: - [ addChild, [ '@admin.payment' ] ] admin.payment: class: AppBundle\Admin\PaymentAdmin arguments: [~, AppBundle\Entity\Payment, ~] tags: - { name: sonata.admin, manager_type: orm, label: Payments }
  245. Admin as a child in some other Admin’s menu #

    src/AppBundle/Admin/BuyerAdmin.php protected function configureSideMenu(ItemInterface $menu, $action, AdminInterface $childAdmin = null) { ... $menu->addChild( 'Payments', $admin->generateMenuUrl('admin.buyer|admin.payment.list', array('id' => $id)) ); }
  246. Admin as a child in some other Admin’s menu #

    src/AppBundle/Admin/BuyerAdmin.php protected function configureSideMenu(ItemInterface $menu, $action, AdminInterface $childAdmin = null) { ... $menu->addChild( 'Payments', $admin->generateMenuUrl('admin.buyer|admin.payment.list', array('id' => $id)) ); }
  247. Screenshots (IV): Admin as a child

  248. Admin as a child in some other Admin’s menu #

    src/AppBundle/Admin/PaymentAdmin.php public function configure() { $this->parentAssociationMapping = 'buyer'; }
  249. What if the ‘list’ is not the main thing in

    my Admin? Tip 4 / 5
  250. Linking a custom action from the sidebar We create our

    custom action, the template, and the route...
  251. Linking a custom action from the sidebar We create our

    custom action, the template, and the route... # app/config/config.yml dashboard: groups: ... demo.admin.settings: label: Settings label_catalogue: AppBundle icon: '<i class="fa fa-gear"></i>' items: - admin.config ...and link it from the sidebar
  252. Linking a custom action from the sidebar We create our

    custom action, the template, and the route... # app/config/config.yml dashboard: groups: ... demo.admin.settings: label: Settings label_catalogue: AppBundle icon: '<i class="fa fa-gear"></i>' items: - admin.config - route: config_myEdit label: 'My config' ...and link it from the sidebar
  253. Screenshots (IV): Custom action in sidebar

  254. What if I want to add something that is not

    an Admin? Tip 5 / 5
  255. Your entire application can be “inside” Sonata Your project can

    have parts that are not an admin panel But are perfectly integrated with everything else You can actually put whatever you want in there
  256. Your entire application can be “inside” Sonata

  257. Your entire application can be “inside” Sonata

  258. Your entire application can be “inside” Sonata Create your action

    in your controller (PatientAdminController.php)
  259. Your entire application can be “inside” Sonata Create your action

    in your controller (PatientAdminController.php) Configure the new route in configureRoutes (PatientAdmin.php) $collection->add('editor', $this->getRouterIdParameter().'/editor');
  260. Your entire application can be “inside” Sonata Create your action

    in your controller (PatientAdminController.php) Configure the new route in configureRoutes (PatientAdmin.php) $collection->add('editor', $this->getRouterIdParameter().'/editor'); Do setTemplate in the service configuration (services.yml) admin.patient: class: AppBundle\Admin\PatientAdmin arguments: [~, AppBundle\Entity\Patient, AppBundle:PatientAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Activos } calls: - [ setTemplate, [edit, :Admin:my_template.html.twig]]
  261. Your entire application can be “inside” Sonata Create your action

    in your controller (PatientAdminController.php) Configure the new route in configureRoutes (PatientAdmin.php) $collection->add('editor', $this->getRouterIdParameter().'/editor'); Do setTemplate in the service configuration (services.yml) admin.patient: class: AppBundle\Admin\PatientAdmin arguments: [~, AppBundle\Entity\Patient, AppBundle:PatientAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Activos } calls: - [ setTemplate, [edit, :Admin:my_template.html.twig]] All stuff that we already know!
  262. V. Further Or what else is out there

  263. SonataAdminBundle documentation

  264. FOSUserBundle & SonataUserAdminBundle SonataUserAdminBundle works as a layer on top

    of FOSUserBundle It gives you some additional functionalities But you don’t actually need it, if you don’t want to use it https://sonata-project.org/bundles/user/master/doc/reference/introduction.html
  265. Security, roles Security can be configured in different ways /

    levels With as much detail as you want https://sonata-project.org/bundles/admin/master/doc/reference/security.html
  266. Events There are some events defined in Sonata that can

    be very handy: • sonata.admin.event.persistence.pre_update • sonata.admin.event.persistence.post_update • sonata.admin.event.persistence.pre_persist • sonata.admin.event.persistence.post_persist • sonata.admin.event.persistence.pre_remove • sonata.admin.event.persistence.post_remove https://sonata-project.org/bundles/admin/master/doc/reference/events.html
  267. Sonata Project Demo

  268. Sonata Project Demo

  269. Sonata Project Demo

  270. And, of course… the code

  271. Admin class vendor/sonata-project/admin-bundle/Admin/AbstractAdmin.php Extremely important to look into the Admin

    class
  272. VI. Final thoughts Or what should I remember from all

    this
  273. Final thoughts Has solved problems historically attributed, keeps improving Allows

    you to efficiently solve a tedious problem
  274. Final thoughts Has solved problems historically attributed, keeps improving Allows

    you to efficiently solve a tedious problem In my experience, a good decision from the business point of view
  275. Final thoughts 1. It gives you a lot for free

  276. Final thoughts 1. It gives you a lot for free

    2. You can override whatever you want and do your own thing
  277. NO NEED to start from scratch

  278. DO TOUCH whatever you

  279. https://github.com/VictoriaQ/sonatademo @vicqr victoria@limenius.com Training, consulting and development https://github.com/VictoriaQ/sonatademo @vicqr victoria@limenius.com

  280. https://github.com/VictoriaQ/sonatademo @vicqr victoria@limenius.com Training, consulting and development https://github.com/VictoriaQ/sonatademo @vicqr victoria@limenius.com

    Thanks!