¿Cómo usar Ajax dentro de los formularios de administración de Sonata?


Tengo una entidad mercantil con los siguientes campos y asociaciones:-

/**
 * @ORM\ManyToMany(targetEntity="Category", inversedBy="merchants")
 */
public $categories;

/**
 * @ORM\ManyToMany(targetEntity="Tag", inversedBy="merchants")
 */
public $tags;

/**
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="merchants")
 */
protected $primaryCategory;

/**
 * @ORM\ManyToOne(targetEntity="Tag", inversedBy="merchants")
 */
protected $primaryTag;

Las etiquetas y categorías también tienen una asignación de muchos. Así que tenemos Tag_Category, Merchant_Tag, Merchant_Category tablas de asignación.

Ahora quiero realizar un poco de ajax en estos campos.

Quiero permitir que el usuario seleccione primero la etiqueta Principal. Sobre la base de la Etiqueta Primaria, ajax actualiza las categorías solo a las que pertenecen a esta etiqueta y algunas operaciones más.

Cómo puedo lograr esto?

Gracias!

Author: likeitlikeit, 2012-04-12

4 answers

Pude hacer que esto funcionara hace unos meses. Mientras que lo una.aitboudad ha compartido es exacta. Hay algunos gotcha que los principiantes con Symfony / Sonata podrían enfrentar.

Aquí están los pasos.

1> Extender Sonata CRUD's edit.html.twig / base_edit.html.twig . Por simplicidad, usaré solo el último. Copie vendor/bundles/Sonata/AdminBundle/Resources/views/CRUD/base_edit.html.twig en la carpeta views correspondiente a MerchantAdminController - YourBundle/Resources/views/Merchant/base_edit.html.twig

2> Necesitamos decirle a nuestra clase MerchantAdmin que use esta plantilla. Así que sobrescribimos el método getEditTemplate de SonataAdmin así:

public function getEditTemplate()
{
    return 'YourBundle:Merchant:base_edit.html.twig';
}

3> A continuación necesitamos codificar la funcionalidad Ajax en nuestro base_edit.html.twig . Ajax estándar se compone de lo siguiente:

3.1> -- Crear una acción en el controlador para la solicitud Ajax Principalmente queremos obtener una lista de ID de categoría correspondiente a una etiqueta en particular. Pero lo más probable es que solo estés usando el controlador CRUD de Sonata.

Defina su MerchantAdminController que extends CRUDController

<?php

namespace GD\AdminBundle\Controller;

use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use GD\AdminBundle\Entity\Merchant;

class MerchantAdminController extends Controller
{

}

3.2> -- Dile a tu servicio de administración que use este controlador recién creado en lugar del CRUDController predeterminado definiéndolo en YourBundle/Resources/config/services.yml

gd_admin.merchant:
        class: %gd_admin.merchant.class%
        tags:
            - { name: sonata.admin, manager_type: orm, group: gd_merchant, label: Merchants }
        arguments: [null, GD\AdminBundle\Entity\Merchant, GDAdminBundle:MerchantAdmin]

Observe que el tercer argumento es el nombre de su controlador. Por defecto habría sido null.

3.3> -- Cree una Acción llamada getCategoryOptionsFromTagAction en su controlador. Su llamada Ajax será a esta Acción.

// route - get_categories_from_tag
public function getCategoryOptionsFromTagAction($tagId)
    {   
        $html = ""; // HTML as response
        $tag = $this->getDoctrine()
            ->getRepository('YourBundle:Tag')
            ->find($tagId);

        $categories = $tag->getCategories();

        foreach($categories as $cat){
            $html .= '<option value="'.$cat->getId().'" >'.$cat->getName().'</option>';
        }

        return new Response($html, 200);
    }

3.4> -- Crear el correspondiente ruta en app/config/routing.yml. Recuerde exponer su ruta si está utilizando el FOSJsRoutingBundle (de lo contrario tendrá que codificar duro, lo que no es una buena idea).

get_categories_from_tag:
    pattern: /{_locale}/admin/gd/admin/merchant/get-categories-from-tag/{tagId}
    defaults: {_controller: GDAdminBundle:MerchantAdmin:getCategoryOptionsFromTag}
    options:
        expose: true

3.5> -- Haga la solicitud Ajax y utilice la respuesta

{% block javascripts %}
    {{ parent() }}
    <script type="text/javascript">

        $(document).ready(function(){
            var primaryTag = $("#{{ admin.uniqId }}_primaryTag");
            primaryTag.change(updateCategories()); // Bind the function to updateCategories
            primaryTag.change(); // Manual trigger to update categories in Document load.

            function updateCategories(){
                return function () {
                    var tagId = $("#{{ admin.uniqId }}_primaryTag option:selected").val();
                    var primaryCategory = $("#{{ admin.uniqId }}_primaryCategory");
                    primaryCategory.empty();
                    primaryCategory.trigger("liszt:updated");
                    var locale = '{{ app.request.get('_locale') }}';

                    var objectId = '{{ admin.id(object) }}'

                    var url = Routing.generate('get_categories_from_tag', { '_locale': locale, 'tagId': tagId, _sonata_admin: 'gd_admin.merchant', id: objectId });
                    $.post(url, { tagId: tagId }, function(data){
                        primaryCategory.empty().append(data);
                        primaryCategory.trigger("liszt:updated");
                    },"text");

                    primaryCategory.val("option:first").attr("selected", true);
                };
            }
        });
    </script>
{% endblock %}

Gotcha 1: Cómo obtener el ID único que se adjunta a todos los elementos Sonata

Solución: Utilice la variable admin que le dará acceso a todas las propiedades de la clase Admin incluyendo uniqId. Consulte el código sobre cómo usarlo.

Gotcha 2: Cómo obtener el Router en su JS.

Solución: Por defecto Symfony2 Routing no funciona en JS. Necesitas usar un paquete llamado FOSJSRouting (explicado arriba) y exponer la ruta. Esto le dará acceso al objeto Router dentro de su JS también.

He modificado ligeramente mi solución para hacer este ejemplo más claro. Si usted nota algo malo, por favor no dude en comentar.

 57
Author: Amit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-29 13:12:21

En el paso 1 de la respuesta Amit y Lumbendil debe cambiar

{% extends base_template %}

Hacia

{% extends 'SonataAdminBundle::standard_layout.html.twig' %}

Si obtienes un error como

Unable to find template "" in YourBundle:YourObject:base_edit.html.twig at line 34.  
 4
Author: Paul Prijs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-31 12:47:42

Post muy detallado, solo para actualizar la forma de override y usar la plantilla edit en la clase Admin.
Ahora, usted debe hacerlo de esta manera:

// src/AppBundle/Admin/EntityAdmin.php  

class EntityAdmin extends Admin
{  
    public function getTemplate($name)
    {
        if ( $name == "edit" ) 
        {
            // template 'base_edit.html.twig' placed in app/Resources/views/Entity
            return 'Entity/base_edit.html.twig' ;
        }
        return parent::getTemplate($name);
    }
}

O inyectarlo en la definición del servicio utiliza el método proporcionado, para mantener la clase Admin lo más limpia posible:

// app/config/services.yml  

app.admin.entity:
    class: AppBundle\Admin\EntityAdmin
    arguments: [~, AppBundle\Entity\Entity, ~]
    tags:
        - {name: sonata.admin, manager_type: orm, group: "Group", label: "Label"}
    calls:
        - [ setTemplate, [edit, Entity/base_edit.html.twig]]
 4
Author: guillermogfer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-24 14:36:44

En el bloque javascripts, tienes que cambiar "liszt:updated" a "chosen:updated"

Espero que ayude a alguien;)

 2
Author: Hibatallah Aouadni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-24 13:59:23