How to use Ajax within Sonata Admin forms?

后端 未结 4 1703
有刺的猬
有刺的猬 2020-11-28 22:10

I have a Merchant entity with the following fields and associations:-

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


        
相关标签:
4条回答
  • 2020-11-28 22:23

    I was able to make this work a few months back. While what a.aitboudad has shared is accurate. There are a few gotcha's that first timers with Symfony/Sonata might face.

    Here are the steps.

    1> Extend Sonata CRUD's edit.html.twig / base_edit.html.twig . For simplicity, I'll use only the latter. Copy vendor/bundles/Sonata/AdminBundle/Resources/views/CRUD/base_edit.html.twig into the views folder corresponding to the MerchantAdminController - YourBundle/Resources/views/Merchant/base_edit.html.twig

    2> We need to tell our MerchantAdmin class to use this template. So we override SonataAdmin's getEditTemplate method like this:

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

    3> Next we need to code the Ajax functionality in our base_edit.html.twig . Standard Ajax comprises of the following:

    3.1> -- Create an Action in the controller for the Ajax request We primarily want to get a list of category IDs corresponding to a particular tag. But most likely you are just using Sonata's CRUD Controller.

    Define your MerchantAdminController which 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> -- Tell your Admin service to use this newly created controller instead of the default CRUDController by defining it in 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]
    

    Notice that the 3rd argument is the name of your controller. By default it would have been null.

    3.3> -- Create an Action named getCategoryOptionsFromTagAction in your controller. Your Ajax call will be to this Action.

    // 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> -- Create the corresponding route in app/config/routing.yml. Remember to expose your route if you are using the FOSJsRoutingBundle (else you'll have to hardcode which is not a good 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> -- Make the Ajax Request and use the response

    {% 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: How to get the Unique ID that is appended to all Sonata elements

    Solution: Use the admin variable which will give you access to all the Admin Class's properties including uniqId. See code on how to use it.

    Gotcha 2: How to get the Router in your JS.

    Solution: By default Symfony2 Routing doesn't work in JS. You need to use a bundle called FOSJSRouting (explained above) and expose the route. This will give you access to the Router object within your JS too.

    I have modified my solution slightly to make this example clearer. If you notice anything wrong, please feel free to comment.

    0 讨论(0)
  • 2020-11-28 22:23

    in the block javascripts, you have to change "liszt:updated" to "chosen:updated"

    hope it helps someone ;)

    0 讨论(0)
  • 2020-11-28 22:29

    At step 1 of Amit and Lumbendil answer you should change

    {% extends base_template %}
    

    into

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

    if you get an error like

    Unable to find template "" in YourBundle:YourObject:base_edit.html.twig at line 34.  
    
    0 讨论(0)
  • 2020-11-28 22:30

    Very detailed post, just to update the way of override and use the edit template in the Admin class.
    Now, you should do it this way:

    // 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);
        }
    }
    

    Or inject it in the service definition used the provided method, to keep the Admin class as cleaner as possible:

    // 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]]
    
    0 讨论(0)
提交回复
热议问题