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. View Slide

  2. View Slide

  3. 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]

    View Slide

  4. I. Introducción
    O acerca de por
    qué y para qué
    estamos aquí

    View Slide

  5. El creador

    View Slide

  6. Principales colaboradores

    View Slide

  7. View Slide

  8. Principales problemas históricamente atribuidos
    - Difícil de instalar

    View Slide

  9. Principales problemas históricamente atribuidos
    - Difícil de instalar
    - Feo

    View Slide

  10. Principales problemas históricamente atribuidos
    - Difícil de instalar
    - Feo
    - Mala documentación

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. El problema que queremos
    resolver...

    View Slide

  17. ...es implementar un panel de
    administración

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. 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í

    View Slide

  23. Dos tipos de desarrolladores

    View Slide

  24. View Slide

  25. View Slide

  26. Charla práctica
    https://github.com/VictoriaQ/sonatademo

    View Slide

  27. 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

    View Slide

  28. Screenshots (II): Instalación limpia

    View Slide

  29. Screenshots (II): Instalación limpia

    View Slide

  30. Screenshots (II): Instalación limpia

    View Slide

  31. ¿Cómo empezamos?

    View Slide

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

    View Slide

  33. 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

    View Slide

  34. 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');
    }
    }

    View Slide

  35. 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');
    }
    }

    View Slide

  36. 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');
    }
    }

    View Slide

  37. 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');
    }
    }

    View Slide

  38. 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');
    }
    }

    View Slide

  39. 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 }

    View Slide

  40. 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 }

    View Slide

  41. Screenshots (II): Primer Admin

    View Slide

  42. Screenshots (II): Primer Admin

    View Slide

  43. Screenshots (II): Primer Admin

    View Slide

  44. Screenshots (II): Primer Admin

    View Slide

  45. 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: ''
    items:
    - admin.regalo

    View Slide

  46. 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: ''
    items:
    - admin.regalo

    View Slide

  47. 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: ''
    items:
    - admin.regalo

    View Slide

  48. Screenshots (II): Primer Admin (ítem en menú lateral)

    View Slide

  49. Ok, ¿y con eso que tengo?

    View Slide

  50. ¿Y con eso qué tengo?
    - Create, edit, delete...

    View Slide

  51. Screenshots (II): Primer Admin - El formulario

    View Slide

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

    View Slide

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

    View Slide

  54. View Slide

  55. Screenshots (II): Primer Admin - El listado

    View Slide

  56. Screenshots (II): Primer Admin - El listado

    View Slide

  57. Screenshots (II): Primer Admin - Los filtros

    View Slide

  58. Configuración básica del
    listado

    View Slide

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

    View Slide

  60. 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')
    ;
    }

    View Slide

  61. 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')
    ;
    }

    View Slide

  62. 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')
    ;
    }

    View Slide

  63. 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')
    ;
    }

    View Slide

  64. 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')
    ;
    }

    View Slide

  65. 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

    View Slide

  66. Screenshots (II): Primer Admin - El listado

    View Slide

  67. Screenshots (II): Primer Admin - El listado

    View Slide

  68. Screenshots (II): Primer Admin - El listado

    View Slide

  69. Screenshots (II): Primer Admin - El listado

    View Slide

  70. 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(),
    )
    ))
    ;
    }

    View Slide

  71. 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(),
    )
    ))
    ;
    }

    View Slide

  72. Screenshots (II): Primer Admin - El listado

    View Slide

  73. Screenshots (II): Primer Admin - El listado

    View Slide

  74. Configuración básica del
    formulario

    View Slide

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

    View Slide

  76. 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')
    ;
    }

    View Slide

  77. Screenshots (II): Primer Admin - El formulario

    View Slide

  78. 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')
    ;
    }

    View Slide

  79. 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()
    ;
    }

    View Slide

  80. 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()
    ;
    }

    View Slide

  81. Screenshots (II): Primer Admin - El formulario

    View Slide

  82. 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()
    ;
    }

    View Slide

  83. Screenshots (II): Primer Admin - El formulario

    View Slide

  84. ¿Y si alguno de los campos
    tiene una relación con otra
    entidad?

    View Slide

  85. Screenshots (II): Primer Admin - El formulario

    View Slide

  86. 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()
    ;
    }

    View Slide

  87. 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()
    ;
    }

    View Slide

  88. 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()
    ;
    }

    View Slide

  89. 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()
    ;
    }

    View Slide

  90. 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()
    ;
    }

    View Slide

  91. Screenshots (II): Primer Admin - El formulario

    View Slide

  92. ¿Y los one-to-many y
    many-to-many?

    View Slide

  93. 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()
    ;
    }

    View Slide

  94. 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()
    ;
    }

    View Slide

  95. 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()
    ;
    }

    View Slide

  96. 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

    View Slide

  97. Una collection en el form (one-to-many)

    View Slide

  98. Una collection en el form (one-to-many)

    View Slide

  99. Una collection en el form (one-to-many)

    View Slide

  100. Una many-to-many en el form

    View Slide

  101. 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()
    ;
    }

    View Slide

  102. 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()
    ;
    }

    View Slide

  103. 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()
    ;
    }

    View Slide

  104. Una many-to-many en el form

    View Slide

  105. Una many-to-many en el form

    View Slide

  106. Una many-to-many en el form

    View Slide

  107. Configuración básica de
    los filtros

    View Slide

  108. Configuración básica de los filtros
    # src/AppBundle/Admin/RegaloAdmin.php
    protected function configureDatagridFilters(DatagridMapper
    $datagridMapper)
    {
    $datagridMapper
    ->add('nombre')
    ;
    }

    View Slide

  109. 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'))
    ;
    }

    View Slide

  110. Screenshots (II): Primer Admin - Los filtros

    View Slide

  111. Hasta aquí lo “gratis”

    View Slide

  112. ¿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

    View Slide

  113. View Slide

  114. ¿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…?

    View Slide

  115. ¿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?

    View Slide

  116. View Slide

  117. III.
    Personalizando
    O cómo campar a
    mis anchas paso a
    paso

    View Slide

  118. Personalizando
    ● Templates
    ● Queries
    ● Actions

    View Slide

  119. Sobrescribiendo templates

    View Slide

  120. 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'
    ...

    View Slide

  121. 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

    View Slide

  122. 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'
    ...

    View Slide

  123. 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'
    ...

    View Slide

  124. 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

    View Slide

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

    View Slide

  126. Sobrescribir 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
    {% block formactions %}
    ...
    {% block formactions %}

    View Slide

  127. Screenshots (III): Sobrescribir templates

    View Slide

  128. Screenshots (III): Sobrescribir templates

    View Slide

  129. ¿Y si solo quiero sobrescribir la
    template de un Admin
    concreto?

    View Slide

  130. 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 }

    View Slide

  131. 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]]

    View Slide

  132. Screenshots (III): Sobrescribir templates

    View Slide

  133. ¿Cómo sobrescribo la template
    de un campo concreto del
    listado?

    View Slide

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

    View Slide

  135. 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'))
    ;
    }

    View Slide

  136. 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'))
    ;
    }

    View Slide

  137. 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 %}

    Enviar

    {% endblock %}

    View Slide

  138. Screenshots (III): Sobrescribir la template de un field en el list

    View Slide

  139. Modificando las queries

    View Slide

  140. Screenshots (III): Modificar la query del list

    View Slide

  141. 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;
    }

    View Slide

  142. 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;
    }

    View Slide

  143. Screenshots (III): Modificar la query del list

    View Slide

  144. Screenshots (III): Modificar la query de un filtro

    View Slide

  145. Screenshots (III): Modificar la query de un filtro

    View Slide

  146. Screenshots (III): Modificar la query de un filtro

    View Slide

  147. 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'
    ))
    ;
    }

    View Slide

  148. 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'
    ))
    ;
    }

    View Slide

  149. 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'
    ))
    ;
    }

    View Slide

  150. 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'
    ))
    ;
    }

    View Slide

  151. Screenshots (III): Modificar la query de un filtro

    View Slide

  152. Screenshots (III): Modificar la query de un filtro

    View Slide

  153. Escribiendo en el
    controlador

    View Slide

  154. Screenshots (III): Crear un action custom

    View Slide

  155. 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'));
    }
    }

    View Slide

  156. 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'));
    }
    }

    View Slide

  157. 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'));
    }
    }

    View Slide

  158. 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]]

    View Slide

  159. 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]]

    View Slide

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

    View Slide

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

    View Slide

  162. Crear un action custom
    # app/Resources/views/Admin/field_envio_email.html.twig
    {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
    {% block field %}

    Enviar

    {% endblock %}

    View Slide

  163. Crear un action custom
    # app/Resources/views/Admin/field_envio_email.html.twig
    {% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
    {% block field %}

    Enviar

    {% endblock %}

    View Slide

  164. Screenshots (III): Crear un action custom

    View Slide

  165. Screenshots (III): Crear una batch action custom

    View Slide

  166. 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'));
    }

    View Slide

  167. 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'));
    }

    View Slide

  168. 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;
    }

    View Slide

  169. 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;
    }

    View Slide

  170. Screenshots (III): Crear una batch action custom

    View Slide

  171. IV. Consejos
    prácticos
    O conjunto de ideas
    varias que pueden
    venir bien en
    cualquier proyecto

    View Slide

  172. ¿Cómo toco el aspecto
    general del panel?

    View Slide

  173. Aspecto general

    View Slide

  174. Aspecto general

    View Slide

  175. Aspecto general
    # app/config/config.yml
    sonata_admin:
    title: deSymfony

    View Slide

  176. Aspecto general
    # app/config/config.yml
    sonata_admin:
    title: deSymfony
    title_logo: 'img/logo.png'

    View Slide

  177. 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

    View Slide

  178. 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

    View Slide

  179. 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

    View Slide

  180. Aspecto general

    View Slide

  181. 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

    View Slide

  182. 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

    View Slide

  183. 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í

    View Slide

  184. Aspecto general - Layout

    View Slide

  185. Aspecto general - Layout

    View Slide

  186. Aspecto general - Layout

    View Slide

  187. 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'
    ...

    View Slide

  188. 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'
    ...

    View Slide

  189. 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'
    ...

    View Slide

  190. Aspecto general - Layout

    View Slide

  191. Aspecto general - Dashboard

    View Slide

  192. 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'
    ...

    View Slide

  193. 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'
    ...

    View Slide

  194. 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'
    ...

    View Slide

  195. Aspecto general - Dashboard

    View Slide

  196. Aspecto general - Dashboard

    View Slide

  197. Aspecto general - Dashboard

    View Slide

  198. ¿Puedo hacer dos admins
    de la misma entidad?

    View Slide

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

    View Slide

  200. 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)

    View Slide

  201. 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

    View Slide

  202. 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

    View Slide

  203. Crear dos admins de la misma entidad

    View Slide

  204. Crear dos admins de la misma entidad

    View Slide

  205. Crear dos admins de la misma entidad

    View Slide

  206. Admin como child
    en el menú de otro
    Admin

    View Slide

  207. Admin como child en el menú de otro Admin

    View Slide

  208. Admin como child en el menú de otro Admin

    View Slide

  209. 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 }

    View Slide

  210. 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 }

    View Slide

  211. 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 }

    View Slide

  212. 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 }

    View Slide

  213. 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))
    );
    }

    View Slide

  214. 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))
    );
    }

    View Slide

  215. Admin como child en el menú de otro Admin

    View Slide

  216. ¿Qué pasa si el listado no
    es lo principal?

    View Slide

  217. Enlazar un action custom desde la sidebar
    Creamos nuestro action custom, la template
    y la ruta...

    View Slide

  218. Enlazar un action custom desde la sidebar
    # app/config/config.yml
    dashboard:
    groups:
    ...
    demo.admin.settings:
    label: Configuración
    label_catalogue: AppBundle
    icon: ''
    items:
    - admin.configuracion
    Creamos nuestro action custom, la template
    y la ruta...
    … la enlazamos desde el sidebar

    View Slide

  219. Enlazar un action custom desde la sidebar
    # app/config/config.yml
    dashboard:
    groups:
    ...
    demo.admin.settings:
    label: Configuración
    label_catalogue: AppBundle
    icon: ''
    items:
    - admin.configuracion
    - route: config_myEdit
    label: 'Mi configuración'
    Creamos nuestro action custom, la template
    y la ruta...
    … la enlazamos desde el sidebar

    View Slide

  220. Enlazar un action custom desde la sidebar

    View Slide

  221. ¿Y si quiero meter algo que
    no sea un Admin?

    View Slide

  222. 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

    View Slide

  223. Toda tu aplicación puede estar dentro de Sonata

    View Slide

  224. Toda tu aplicación puede estar dentro de Sonata

    View Slide

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

    View Slide

  226. 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');

    View Slide

  227. 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)


    :editor:editor.html.twig


    View Slide

  228. V. Más allá
    O qué otras cosas
    hay ahí fuera y
    dónde puedo
    encontrarlas

    View Slide

  229. Documentación de
    SonataAdminBundle

    View Slide

  230. 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

    View Slide

  231. Seguridad, roles
    https://sonata-project.org/bundles/admin/3-x/doc/reference/security.html
    La seguridad se puede configurar de
    muchas formas distintas y con tanto detalle
    como quieras

    View Slide

  232. 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

    View Slide

  233. Sonata Project Demo

    View Slide

  234. Sonata Project Demo

    View Slide

  235. Sonata Project Demo

    View Slide

  236. Y a la hora de la verdad..
    pues el código

    View Slide

  237. Admin class
    Conviene mucho mirarse la Admin class
    vendor/sonata-project/admin-bundle/Admin/AbstractAdmin.php

    View Slide

  238. VI.
    Conclusiones
    O con qué me
    quedo de todo esto

    View Slide

  239. Conclusiones
    ● Los principales problemas se han ido
    solucionando. Y sigue mejorando.

    View Slide

  240. 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.

    View Slide

  241. 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.

    View Slide

  242. Conclusiones
    Te permite solucionar un problema
    aburrido de una forma eficiente

    View Slide

  243. Conclusiones
    Te permite solucionar un problema
    aburrido de una forma eficiente
    Para que puedas dedicarte a otra cosa
    más emocionante

    View Slide

  244. Conclusiones
    Y desde el punto de vista del negocio
    suele ser muy buena decisión

    View Slide

  245. 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

    View Slide

  246. 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!

    View Slide