Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cómo domar SonataAdminBundle

Cómo domar SonataAdminBundle

Exposición de lo que nos ofrece SonataAdminBundle y recorrido práctico desde su instalación, aprendiendo a personalizarlo y solventar diversos casos que nos encontramos con frecuencia en la implementación de un panel de administración con este bundle. Charla impartida en deSymfony 2016.

Victoria Quirante

September 16, 2016
Tweet

More Decks by Victoria Quirante

Other Decks in Programming

Transcript

  1. Trabajo en Limenius Hacemos proyectos a medida en Symfony y

    React Raro es el proyecto que no requiera un panel de administración Desde hace bastante resolvemos esa parte con SonataAdminBundle Victoria Quirante @vicqr [email protected]
  2. Principales problemas históricamente atribuidos - Difícil de instalar - Feo

    - Mala documentación - Mucho código, difícil de investigar
  3. Principales problemas históricamente atribuidos - Difícil de instalar - Feo

    - Mala documentación - Mucho código, difícil de investigar
  4. Principales problemas históricamente atribuidos - Difícil de instalar - Feo

    - Mala documentación - Mucho código, difícil de investigar
  5. Principales problemas históricamente atribuidos - Difícil de instalar - Feo

    - Mala documentación regular - Mucho código, difícil de investigar
  6. Principales problemas históricamente atribuidos - Difícil de instalar - Feo

    - Mala documentación regular - Mucho código, difícil de investigar
  7. Lo que es y lo que no es Sonata No

    es el David de Miguel Ángel Ni la teoría de cuerdas Ni el número áureo
  8. Lo que es y lo que no es Sonata No

    es el David de Miguel Ángel Ni la teoría de cuerdas Ni el número áureo Pero sí es algo muy útil
  9. Lo que es y lo que no es Sonata No

    es el David de Miguel Ángel Ni la teoría de cuerdas Ni el número áureo Pero sí es algo muy útil Como Symfony, como PHP
  10. Charla práctica - No es una revisión exhaustiva de la

    documentación - Vamos a mostrar cómo se usa en la práctica - Cuál es el recorrido desde la instalación limpia
  11. Charla práctica - No es una revisión exhaustiva de la

    documentación - Vamos a mostrar cómo se usa en la práctica - Cuál es el recorrido desde la instalación limpia 1) Qué te da Sonata “gratis” 2) Cómo personalizo lo que quiera a partir de ahí
  12. II. El Admin básico O cómo sacar provecho del sudor

    de otros de forma que llega a dar hasta un poco de vergüenza
  13. El Admin básico: 3 pasos, 2 minutos Tenemos la entidad

    Regalo para la que queremos crear un Admin
  14. El Admin básico: 3 pasos, 2 minutos Tenemos la entidad

    Regalo para la que queremos crear un Admin: 1) Creamos la clase RegaloAdmin 2) Registramos el servicio
  15. El Admin básico - Paso 1: Creamos la clase Admin

    # src/AppBundle/Admin/RegaloAdmin.php class RegaloAdmin extends Admin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('nombre'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('nombre'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('nombre'); } }
  16. El Admin básico - Paso 1: Creamos la clase Admin

    # src/AppBundle/Admin/RegaloAdmin.php class RegaloAdmin extends Admin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('nombre'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('nombre'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('nombre'); } }
  17. El Admin básico - Paso 1: Creamos la clase Admin

    # src/AppBundle/Admin/RegaloAdmin.php class RegaloAdmin extends Admin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('nombre'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('nombre'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('nombre'); } }
  18. El Admin básico - Paso 1: Creamos la clase Admin

    # src/AppBundle/Admin/RegaloAdmin.php class RegaloAdmin extends Admin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('nombre'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('nombre'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('nombre'); } }
  19. El Admin básico - Paso 1: Creamos la clase Admin

    # src/AppBundle/Admin/RegaloAdmin.php class RegaloAdmin extends Admin { protected function configureFormFields(FormMapper $formMapper) { $formMapper->add('nombre'); } protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper->add('nombre'); } protected function configureListFields(ListMapper $listMapper) { $listMapper->addIdentifier('nombre'); } }
  20. El Admin básico - Paso 2: Registramos el servicio #

    app/config/services.yml services: admin.regalo: class: AppBundle\Admin\RegaloAdmin arguments: [~, AppBundle\Entity\Regalo, ~] tags: - { name: sonata.admin, manager_type: orm, label: Regalos }
  21. El Admin básico - Paso 2: Registramos el servicio #

    app/config/services.yml services: admin.regalo: class: AppBundle\Admin\RegaloAdmin arguments: [~, AppBundle\Entity\Regalo, ~] tags: - { name: sonata.admin, manager_type: orm, label: Regalos }
  22. El Admin básico - Paso 3 (opcional): Menú lateral #

    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.regalo
  23. El Admin básico - Paso 3 (opcional): Menú lateral #

    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.regalo
  24. El Admin básico - Paso 3 (opcional): Menú lateral #

    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.regalo
  25. Lo que Sonata te da hecho - Create, edit, delete...

    - Listado paginado, ordenable, filtrable y exportable
  26. Lo que Sonata te da hecho - Create, edit, delete...

    - Listado paginado, ordenable, filtrable y exportable
  27. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ->add('nombre') ->add('precio') ->add('descripcion') ->add('destinatario') ->add('comprador') ; }
  28. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ->addIdentifier('nombre') ->add('precio') ->add('descripcion') ->add('destinatario') ->add('comprador') ; }
  29. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ->addIdentifier('nombre') ->add('precio', 'currency', array('currency' => 'EUR')) ->add('descripcion') ->add('destinatario') ->add('comprador') ; }
  30. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ->addIdentifier('nombre') ->add('precio', 'currency', array('currency' => 'EUR')) ->add('descripcion', null, array('label' => 'Descripción')) ->add('destinatario') ->add('comprador') ; }
  31. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ->addIdentifier('nombre') ->add('precio', 'currency', array('currency' => 'EUR')) ->add('descripcion', null, array('label' => 'Descripción')) ->add('destinatario', null, array('editable' => true) ->add('comprador') ; }
  32. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ->addIdentifier('nombre') ->add('precio', 'currency', array('currency' => 'EUR')) ->add('descripcion', null, array('label' => 'Descripción')) ->add('destinatario', null, array('editable' => true)) ->add('comprador') ; } https://sonata-project.org/bundles/admin/3-x/doc/reference/field_types.html
  33. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ... ->add('_action', null, array( 'actions' => array( 'show' => array(), 'edit' => array(), 'delete' => array(), ) )) ; }
  34. Configuración básica del listado # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper)

    { $listMapper ... ->add('_action', null, array( 'actions' => array( 'show' => array(), 'edit' => array(), 'delete' => array(), ) )) ; }
  35. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ->add('nombre') ->add('precio') ->add('descripcion') ->add('destinatario') ->add('comprador') ; }
  36. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ->add('nombre') ->add('precio') ->add('descripcion') ->add('destinatario') ->add('comprador') ; }
  37. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ->with('Regalo', array('class' => 'col-md-6')) ->add('nombre') ->add('precio') ->add('descripcion') ->end() ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario') ->add('comprador') ->end() ; }
  38. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ->with('Regalo', array('class' => 'col-md-6')) ->add('nombre') ->add('precio') ->add('descripcion') ->end() ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario') ->add('comprador') ->end() ; }
  39. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ->tab('Tab 1') ->with('Regalo', array('class' => 'col-md-6')) ->add('nombre') ->add('precio') ->add('descripcion') ->end() ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario') ->add('comprador') ->end() ->end() ->tab('Tab 2') ->end() ; }
  40. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ... ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario') ->add('comprador') ->end() ; }
  41. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ... ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario', null) ->add('comprador', null) ->end() ; }
  42. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ... ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario', 'entity', array( 'class' => 'AppBundle\Entity\Destinatario')) ->add('comprador', 'entity', array( 'class' => 'AppBundle\Entity\Comprador')) ->end() ; }
  43. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ... ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario', null, array( 'class' => 'AppBundle\Entity\Destinatario', 'choice_label' => 'apellidos')) ->add('comprador', null, array( 'class' => 'AppBundle\Entity\Comprador', 'choice_label' => 'apellidos')) ->end() ; }
  44. Configuración básica del formulario # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper $formMapper)

    { $formMapper ... ->with('Participantes', array('class' => 'col-md-6')) ->add('destinatario', null, array( 'class' => 'AppBundle\Entity\Destinatario', 'choice_label' => 'nombreCompleto')) ->add('comprador', null, array( 'class' => 'AppBundle\Entity\Comprador', 'choice_label' => 'nombreCompleto')) ->end() ; }
  45. Una collection en el form (one-to-many) # src/AppBundle/Admin/CompradorAdmin.php protected function

    configureFormFields(FormMapper $formMapper) { $formMapper ->with('Datos personales', array('class' => 'col-md-6')) ->add('nombre') ->add('apellidos') ->end() ; }
  46. Una collection en el form (one-to-many) # src/AppBundle/Admin/CompradorAdmin.php protected function

    configureFormFields(FormMapper $formMapper) { $formMapper ->with('Datos personales', array('class' => 'col-md-6')) ->add('nombre') ->add('apellidos') ->end() ->with('Pagos', array('class' => 'col-md-6')) ->add('pagos', 'sonata_type_collection', array( ), array( 'edit' => 'inline', 'inline' => 'table', )) ->end() ; }
  47. Una collection en el form (one-to-many) # src/AppBundle/Admin/CompradorAdmin.php protected function

    configureFormFields(FormMapper $formMapper) { $formMapper ->with('Datos personales', array('class' => 'col-md-6')) ->add('nombre') ->add('apellidos') ->end() ->with('Pagos', array('class' => 'col-md-6')) ->add('pagos', 'sonata_type_collection', array( ), array( 'edit' => 'inline', 'inline' => 'table', )) ->end() ; }
  48. Una collection en el form (one-to-many) # src/AppBundle/Admin/CompradorAdmin.php protected function

    configureFormFields(FormMapper $formMapper) { $formMapper ->with('Datos personales', array('class' => 'col-md-6')) ->add('nombre') ->add('apellidos') ->end() ->with('Pagos', array('class' => 'col-md-6')) ->add('pagos', 'sonata_type_collection', array( ), array( 'edit' => 'inline', 'inline' => 'table', )) ->end() ; } Y creamos un Admin para la entidad Pago
  49. Una many-to-many en el form # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper

    $formMapper) { $formMapper ... ->with('Establecimientos', array('class' => 'col-md-6')) ->add('tiendas', 'sonata_type_model', array( 'by_reference' => false, 'expanded' => true, 'multiple' => true, 'label' => 'Tiendas') ) ->end() ; }
  50. Una many-to-many en el form # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper

    $formMapper) { $formMapper ... ->with('Establecimientos', array('class' => 'col-md-6')) ->add('tiendas', 'sonata_type_model', array( 'by_reference' => false, 'expanded' => true, 'multiple' => true, 'label' => 'Tiendas') ) ->end() ; }
  51. Una many-to-many en el form # src/AppBundle/Admin/RegaloAdmin.php protected function configureFormFields(FormMapper

    $formMapper) { $formMapper ... ->with('Establecimientos', array('class' => 'col-md-6')) ->add('tiendas', 'sonata_type_model', array( 'by_reference' => false, 'expanded' => true, 'multiple' => true, 'label' => 'Tiendas') ) ->end() ; }
  52. Configuración básica de los filtros # src/AppBundle/Admin/RegaloAdmin.php protected function configureDatagridFilters(DatagridMapper

    $datagridMapper) { $datagridMapper ->add('nombre') ->add('precio') ->add('destinatario', null, array(), 'entity', array( 'class' => 'AppBundle\Entity\Destinatario', 'choice_label' => 'nombreCompleto')) ; }
  53. ¿Tenemos mucho o poco? - Con muy poco esfuerzo tienes

    muchísimo - De hecho tienes la mayor parte de lo que necesitas - Pero el mundo no es perfecto, y vas a necesitar algunas otras cosas en tu panel casi con seguridad
  54. ¿Y si quiero... … tener un formulario maquetado de otra

    forma? … meter algo “extraño” en un campo del listado? … crear una sección del menú que no sea un listado? … meter algo dentro de Sonata que no tenga nada que ver con el panel de administración…?
  55. ¿Y si quiero... … tener un formulario maquetado de otra

    forma? … meter algo “extraño” en un campo del listado? … crear una sección del menú que no sea un listado? … meter algo dentro de Sonata que no tenga nada que ver con el panel de administración…? ¿Cuánto me va a costar todo eso? ¿No será mejor empezar de cero?
  56. Sobrescribir 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' ...
  57. Sobrescribir 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/3-x/doc/reference/templates.html
  58. Sobrescribir 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' ...
  59. Sobrescribir 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' ...
  60. Sobrescribir 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' ... Fundamental buscar la template original y ver qué queremos sobrescribir exactamente
  61. Sobrescribir templates # app/config/services.yml services: admin.regalo: class: AppBundle\Admin\RegaloAdmin arguments: [~,

    AppBundle\Entity\Regalo, ~] tags: - { name: sonata.admin, manager_type: orm, label: Regalos }
  62. Sobrescribir templates # app/config/services.yml services: admin.regalo: class: AppBundle\Admin\RegaloAdmin arguments: [~,

    AppBundle\Entity\Regalo, ~] tags: - { name: sonata.admin, manager_type: orm, label: Regalos } calls: - [ setTemplate, [edit, :Admin:edit_regalo.html.twig]]
  63. Sobrescribir la template de un field en el list #

    src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper) { $listMapper ... ; }
  64. Sobrescribir la template de un field en el list (paso

    1) # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper) { $listMapper ... ->add('miField', 'string', array('template' => ':Admin:field_envio_email.html.twig')) ; }
  65. Sobrescribir la template de un field en el list (paso

    1) # src/AppBundle/Admin/RegaloAdmin.php protected function configureListFields(ListMapper $listMapper) { $listMapper ... ->add('miField', 'string', array('template' => ':Admin:field_envio_email.html.twig')) ; }
  66. Sobrescribir la template de un field en el list (paso

    2) # app/Resources/views/Admin/field_envio_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> Enviar </a> {% endblock %}
  67. Modificar la query del list # src/AppBundle/Admin/RegaloAdmin.php public function createQuery($context

    = 'list') { $query = parent::createQuery($context); $rootAlias = $query->getRootAliases()[0]; $query ->andWhere( $query->expr()->eq($rootAlias.'.entregado', ':entregado')); $query->setParameter('entregado', false); return $query; }
  68. Modificar la query del list # src/AppBundle/Admin/RegaloAdmin.php public function createQuery($context

    = 'list') { $query = parent::createQuery($context); $rootAlias = $query->getRootAliases()[0]; $query ->andWhere( $query->expr()->eq($rootAlias.'.entregado', ':entregado')); $query->setParameter('entregado', false); return $query; }
  69. Modificar la query de un filtro # src/AppBundle/Admin/RegaloAdmin.php protected function

    configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('miFiltro', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.estado != :estado'); $queryBuilder->setParameter('estado', 'entregado'); return true; }, 'field_type' => 'checkbox', 'label' => 'No entregados' )) ; }
  70. Modificar la query de un filtro # src/AppBundle/Admin/RegaloAdmin.php protected function

    configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('miFiltro', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.estado != :estado'); $queryBuilder->setParameter('estado', 'entregado'); return true; }, 'field_type' => 'checkbox', 'label' => 'No entregados' )) ; }
  71. Modificar la query de un filtro # src/AppBundle/Admin/RegaloAdmin.php protected function

    configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('miFiltro', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.estado != :estado'); $queryBuilder->setParameter('estado', 'entregado'); return true; }, 'field_type' => 'checkbox', 'label' => 'No entregados' )) ; }
  72. Modificar la query de un filtro # src/AppBundle/Admin/RegaloAdmin.php protected function

    configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('miFiltro', 'doctrine_orm_callback', array( 'callback' => function($queryBuilder, $alias, $field, $value) { if (!$value['value']) { return; } $queryBuilder->andWhere($alias.'.estado != :estado'); $queryBuilder->setParameter('estado', 'entregado'); return true; }, 'field_type' => 'checkbox', 'label' => 'No entregados' )) ; }
  73. Crear un action custom (paso 1) # src/AppBundle/Controller/RegaloAdminController.php use Sonata\AdminBundle\Controller\CRUDController

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

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

    as Controller; use Symfony\Component\HttpFoundation\RedirectResponse; class RegaloAdminController extends Controller { public function sendEmailAction() { $regalo = $this->admin->getSubject(); $email = $regalo->getDestinatario()->getEmail(); // Here code to send email $this->addFlash('sonata_flash_success', 'Email enviado a '.$email); return new RedirectResponse($this->admin->generateUrl('list')); } }
  76. Crear un action custom (paso 2) # app/config/services.yml services: admin.regalo:

    class: AppBundle\Admin\RegaloAdmin arguments: [~, AppBundle\Entity\Regalo, ~] tags: - { name: sonata.admin, manager_type: orm, label: Regalos } calls: - [ setTemplate, [edit, :Admin:edit_regalo.html.twig]]
  77. Crear un action custom (paso 2) # app/config/services.yml services: admin.regalo:

    class: AppBundle\Admin\RegaloAdmin arguments: [~, AppBundle\Entity\Regalo, AppBundle\RegaloAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Regalos } calls: - [ setTemplate, [edit, :Admin:edit_regalo.html.twig]]
  78. Crear un action custom (paso 3) # src/AppBundle/Admin/RegaloAdmin.php protected function

    configureRoutes(RouteCollection $collection) { $collection->add('sendEmail', $this->getRouterIdParameter().'/send-email'); }
  79. Crear un action custom (paso 3) # src/AppBundle/Admin/RegaloAdmin.php protected function

    configureRoutes(RouteCollection $collection) { $collection->add('sendEmail', $this->getRouterIdParameter().'/send-email'); }
  80. Crear un action custom # app/Resources/views/Admin/field_envio_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> Enviar </a> {% endblock %}
  81. Crear un action custom # app/Resources/views/Admin/field_envio_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> Enviar </a> {% endblock %}
  82. Crear un batch action custom (paso 1) # src/AppBundle/Controller/RegaloAdminController.php public

    function batchActionSendEmail($selectedModelQuery) { // Here code to send emails return new RedirectResponse($this->admin->generateUrl('list')); }
  83. Crear un batch action custom (paso 1) # src/AppBundle/Controller/RegaloAdminController.php public

    function batchActionSendEmail($selectedModelQuery) { // Here code to send emails return new RedirectResponse($this->admin->generateUrl('list')); }
  84. Crear un batch action custom (paso 2) # src/AppBundle/Admin/RegaloAdmin.php public

    function getBatchActions() { $actions = parent::getBatchActions(); if ($this->hasRoute('edit') && $this->isGranted('EDIT')) { $actions['send_email'] = [ 'label' => 'Enviar email', 'ask_confirmation' => false, ]; } return $actions; }
  85. Crear un batch action custom (paso 2) # src/AppBundle/Admin/RegaloAdmin.php public

    function getBatchActions() { $actions = parent::getBatchActions(); if ($this->hasRoute('edit') && $this->isGranted('EDIT')) { $actions['send_email'] = [ 'label' => 'Enviar email', 'ask_confirmation' => false, ]; } return $actions; }
  86. Aspecto general # app/config/config.yml sonata_admin: title: deSymfony 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 …
  87. Aspecto general # app/config/config.yml sonata_admin: title: deSymfony 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
  88. Aspecto general # app/config/config.yml sonata_admin: title: deSymfony 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 …
  89. Aspecto general # app/config/config.yml sonata_admin: title: deSymfony 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 …
  90. Aspecto general # app/config/config.yml sonata_admin: title: deSymfony 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 …
  91. Aspecto general # app/config/config.yml sonata_admin: title: deSymfony 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 … https://almsaeedstudio.com/ Sonata utiliza AdminLTE, para lo visual generalmente hay que indagar allí
  92. Aspecto general - Layout # 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' ...
  93. Aspecto general - Layout # 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' ...
  94. Aspecto general - Layout # app/config/config.yml 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' ...
  95. Aspecto general - Dashboard # 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' ...
  96. Aspecto general - Dashboard # 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' ...
  97. Aspecto general - Dashboard # 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: ':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' ...
  98. Crear dos admins de la misma entidad # src/AppBundle/Admin/RegaloPasadoAdmin.php class

    RegaloPasadoAdmin extends Admin { } Sin problema. Creamos una nueva clase Admin…
  99. Crear dos admins de la misma entidad # src/AppBundle/Admin/RegaloPasadoAdmin.php class

    RegaloPasadoAdmin extends Admin { protected $baseRouteName = 'regalo_pasado'; protected $baseRoutePattern = 'regalo-pasado' ... } (sin olvidar estas propiedades para que Sonata no se líe con el routing)
  100. Crear dos admins de la misma entidad # app/config/services.yml services:

    admin.regalo: class: AppBundle\Admin\RegaloAdmin arguments: [~, AppBundle\Entity\Regalo, AppBundle:RegaloAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Regalos } calls: - [ setTemplate, [edit, :Admin:edit_regalo.html.twig]] … y registramos el nuevo servicio
  101. Crear dos admins de la misma entidad # app/config/services.yml services:

    admin.regalo: class: AppBundle\Admin\RegaloAdmin arguments: [~, AppBundle\Entity\Regalo, AppBundle:RegaloAdmin] tags: - { name: sonata.admin, manager_type: orm, label: Activos } calls: - [ setTemplate, [edit, :Admin:edit_regalo.html.twig]] admin.regalo_pasado: class: AppBundle\Admin\RegaloPasadoAdmin arguments: [~, AppBundle\Entity\Regalo, ~] tags: - { name: sonata.admin, manager_type: orm, label: Pasados } … y registramos el nuevo servicio
  102. Admin como child en el menú de otro Admin #

    app/config/services.yml admin.comprador: class: AppBundle\Admin\CompradorAdmin arguments: [~, AppBundle\Entity\Comprador, ~] tags: - { name: sonata.admin, manager_type: orm, label: Compradores } calls: - [ addChild, [ '@admin.pago' ] ] admin.pago: class: AppBundle\Admin\PagoAdmin arguments: [~, AppBundle\Entity\Pago, ~] tags: - { name: sonata.admin, manager_type: orm, label: Pagos }
  103. Admin como child en el menú de otro Admin #

    app/config/services.yml admin.comprador: class: AppBundle\Admin\CompradorAdmin arguments: [~, AppBundle\Entity\Comprador, ~] tags: - { name: sonata.admin, manager_type: orm, label: Compradores } calls: - [ addChild, [ '@admin.pago' ] ] admin.pago: class: AppBundle\Admin\PagoAdmin arguments: [~, AppBundle\Entity\Pago, ~] tags: - { name: sonata.admin, manager_type: orm, label: Pagos }
  104. Admin como child en el menú de otro Admin #

    app/config/services.yml admin.comprador: class: AppBundle\Admin\CompradorAdmin arguments: [~, AppBundle\Entity\Comprador, ~] tags: - { name: sonata.admin, manager_type: orm, label: Compradores } calls: - [ addChild, [ '@admin.pago' ] ] admin.pago: class: AppBundle\Admin\PagoAdmin arguments: [~, AppBundle\Entity\Pago, ~] tags: - { name: sonata.admin, manager_type: orm, label: Pagos }
  105. Admin como child en el menú de otro Admin #

    app/config/config.yml admin.comprador: class: AppBundle\Admin\CompradorAdmin arguments: [~, AppBundle\Entity\Comprador, ~] tags: - { name: sonata.admin, manager_type: orm, label: Compradores } calls: - [ addChild, [ '@admin.pago' ] ] admin.pago: class: AppBundle\Admin\PagoAdmin arguments: [~, AppBundle\Entity\Pago, ~] tags: - { name: sonata.admin, manager_type: orm, label: Pagos }
  106. Admin como child en el menú de otro Admin #

    src/AppBundle/Admin/CompradorAdmin.php protected function configureSideMenu(ItemInterface $menu, $action, AdminInterface $childAdmin = null) { ... $menu->addChild( 'Pagos', $admin->generateMenuUrl('admin.comprador|admin.pago.list', array('id' => $id)) ); }
  107. Admin como child en el menú de otro Admin #

    src/AppBundle/Admin/CompradorAdmin.php protected function configureSideMenu(ItemInterface $menu, $action, AdminInterface $childAdmin = null) { ... $menu->addChild( 'Pagos', $admin->generateMenuUrl('admin.comprador|admin.pago.list', array('id' => $id)) ); }
  108. Enlazar un action custom desde la sidebar # app/config/config.yml dashboard:

    groups: ... demo.admin.settings: label: Configuración label_catalogue: AppBundle icon: '<i class="fa fa-gear"></i>' items: - admin.configuracion Creamos nuestro action custom, la template y la ruta... … la enlazamos desde el sidebar
  109. Enlazar un action custom desde la sidebar # app/config/config.yml dashboard:

    groups: ... demo.admin.settings: label: Configuración label_catalogue: AppBundle icon: '<i class="fa fa-gear"></i>' items: - admin.configuracion - route: config_myEdit label: 'Mi configuración' Creamos nuestro action custom, la template y la ruta... … la enlazamos desde el sidebar
  110. Toda tu aplicación puede estar dentro de Sonata Tu aplicación

    puede tener partes que no sean un Admin Y sin embargo estén integradas con lo demás Puedes meter cualquier cosa ahí dentro
  111. Toda tu aplicación puede estar dentro de Sonata Creas tu

    action en el controlador (PacienteAdminController.php)
  112. Toda tu aplicación puede estar dentro de Sonata Creas tu

    action en el controlador (PacienteAdminController.php) Configuras tu ruta en configureRoutes (PacienteAdmin.php) $collection->add('editor', $this->getRouterIdParameter().'/editor');
  113. Toda tu aplicación puede estar dentro de Sonata Creas tu

    action en el controlador (PacienteAdminController.php) Configuras tu ruta en configureRoutes (PacienteAdmin.php) $collection->add('editor', $this->getRouterIdParameter().'/editor'); Haces setTemplate en la declaración del servicio (services.yml) <call method="setTemplates"> <argument type="collection"> <argument key="editor">:editor:editor.html.twig</argument> </argument> </call>
  114. FOSUserBundle & SonataUserAdminBundle SonataUserAdminBundle es una capa sobre FOSUserBundle que

    aporta algunas cosas (pero no es imprescindible para usar FOSUserBundle) https://sonata-project.org/bundles/user/3-x/doc/reference/introduction.html
  115. Eventos https://sonata-project.org/bundles/admin/3-x/doc/reference/events.html Hay una serie de eventos definidos por Sonata

    que pueden resultar muy útiles • 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
  116. Conclusiones • Los principales problemas se han ido solucionando. Y

    sigue mejorando. • Te da MUCHO hecho. Hay que sacar el máximo provecho a esa parte.
  117. Conclusiones • Los principales problemas se han ido solucionando. Y

    sigue mejorando. • Te da MUCHO hecho. Hay que sacar el máximo provecho a esa parte. • Puedes sobrescribir lo que quieras y meter tu propio código donde quieras.
  118. Conclusiones Te permite solucionar un problema aburrido de una forma

    eficiente Para que puedas dedicarte a otra cosa más emocionante
  119. Victoria Quirante Ruiz Madrid, 16-17 Sep. 2016 #deSymfony @vicqr [email protected]

    https://github.com/VictoriaQ/sonatademo Formación, consultoría y desarrollo de proyectos
  120. Victoria Quirante Ruiz Madrid, 16-17 Sep. 2016 #deSymfony @vicqr [email protected]

    https://github.com/VictoriaQ/sonatademo Formación, consultoría y desarrollo de proyectos Gracias!