如何在Sonata Admin表单中使用Ajax?

29

我有一个Merchant实体,它具有以下字段和关联:

/**
 * @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;

标签和分类也有多对多的映射关系。因此我们有Tag_Category、Merchant_Tag、Merchant_Category映射表。

现在我想在这些字段上执行一些ajax操作。

我希望用户首先选择主要标签。根据主要标签,ajax刷新类别只显示属于该标签的类别,并进行一些其他操作。

我该如何实现呢?

谢谢!

4个回答

61

我几个月前能够让这个工作正常运行。虽然a.aitboudad分享的是正确的,但初次接触Symfony/Sonata的人可能会遇到一些棘手的问题。

以下是步骤:

1> 扩展Sonata CRUD的edit.html.twig / base_edit.html.twig 为了简化,我只使用后者。 将vendor/bundles/Sonata/AdminBundle/Resources/views/CRUD/base_edit.html.twig复制到对应于MerchantAdminController的视图文件夹中 - YourBundle/Resources/views/Merchant/base_edit.html.twig

2> 我们需要告诉我们的MerchantAdmin类使用此模板。因此,我们重写SonataAdmin的getEditTemplate方法,如下所示:

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

3> 接下来,我们需要在 base_edit.html.twig 中编写 Ajax 功能代码。标准的 Ajax 包括以下内容:

3.1> -- 在控制器中创建一个用于处理 Ajax 请求的操作。 主要想要获取与特定标签对应的类别 ID 列表。但很可能您只是在使用 Sonata 的 CRUD 控制器。

定义扩展 CRUDController 的 MerchantAdminController。

<?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> -- 在YourBundle/Resources/config/services.yml文件中定义一个新的控制器,并告诉你的管理员服务使用这个新创建的控制器,而不是默认的CRUDController。

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]
请注意,第三个参数是您的控制器的名称。默认情况下它将是null。 3.3-在您的控制器中创建一个名为getCategoryOptionsFromTagAction的操作。您的Ajax调用将针对此操作。
3.3-在您的控制器中创建一个名为getCategoryOptionsFromTagAction的操作。您的Ajax调用将针对此操作。
// 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> -- 在app/config/routing.yml中创建相应的路由。如果您正在使用FOSJsRoutingBundle,请记得公开您的路由(否则您将不得不硬编码,这不是一个好主意)。

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> -- 发送Ajax请求并使用响应

{% 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 %}

提示 1:如何获取添加到所有Sonata元素的唯一ID

解决方案:使用admin变量,它将使您可以访问所有Admin类属性,包括uniqId。请参阅代码以了解如何使用它。

提示 2:如何在JS中获取路由器。

解决方案:默认情况下,Symfony2 Routing在JS中无法运行。您需要使用一个名为FOSJSRouting的bundle(如上所述)并公开该路线。这将使您也可以在JS中访问路由器对象。

我稍微修改了我的解决方案,以使此示例更清晰。如果您注意到任何错误,请随时评论。


3
谢谢,FOSJSRouting 是用来访问路由器对象(Routing.generate(...))以生成路由路径的。如果这是一个普通的 PHP 项目,我们只需要提供文件名 - categories_from_tag.php。但在这里,我们需要调用控制器中的一个操作。 - Amit
3
我实现这个的方法是在我的Admin类(TagAdmin)中添加路由:$collection->add('addToGroup', $this->getRouterIdParameter().'/addToGroup');,然后在ajax url中使用这个路由,例如:url: '{{ admin.generateUrl('addToGroup', {'id': object.id}) }}',并在ajax定义中使用额外的数据参数:data: 'group='+$(this).prevAll('.group-id').val()' - lopsided
1
非常有用!您能否在现有商家的加载时间筛选类别? - Pierre de LESPINAY
3
وˆ‘认ن¸؛ن¸چه†چن½؟用MerchantAdmin::getEditTemplate()م€‚وˆ‘ه؟…é،»ه°†ه…¶و›´و”¹ن¸؛MerchantAdmin::getTemplate($name),ه¹¶هœ¨$name === 'edit'و—¶è؟”ه›‍وˆ‘çڑ„è‡ھه®ڑن¹‰و¨،و‌؟م€‚ - caponica
@lopsided,您能否详细说明一下在ajax post请求中将tagId作为一部分传递的部分?data: 'group='+$(this).prevAll('.group-id').val()我不明白这里发生了什么。可以请您解释一下吗? - Jack Brummer
显示剩余4条评论

4
非常详细的帖子,只是更新了在Admin类中覆盖和使用edit模板的方法。
现在,您应该这样做:
// 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);
    }
}

或者将其注入到使用提供的方法的服务定义中,以使Admin类尽可能干净:
// 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
在Amit和Lumbendil的回答中的第一步,您应该更改


{% extends base_template %}

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

如果您遇到像这样的错误:
Unable to find template "" in YourBundle:YourObject:base_edit.html.twig at line 34.  

2
在块级JavaScript中,您需要将"liszt:updated"更改为"chosen:updated"
希望对某些人有所帮助 ;)

欢迎来到SO :) 你应该避免在回答中提出新问题。可以使用评论或者开启一个新的问题。 - Ivan Gabriele

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接