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

Ami*_*mit 29 ajax symfony symfony-sonata sonata-admin

我有一个商家实体,其中包含以下字段和关联: -

/**
 * @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;
Run Code Online (Sandbox Code Playgroud)

标签和类别也有ManyToMany映射.所以我们有Tag_Category,Merchant_Tag,Merchant_Category映射表.

现在我想在这些字段上执行一些ajax.

我想让用户先选择主标签.在主标记的基础上,ajax将类别刷新为仅属于此标记的类别以及更多操作.

我怎样才能做到这一点?

谢谢!

Ami*_*mit 59

几个月前我就能完成这项工作.虽然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对应的views文件夹中 -YourBundle/Resources/views/Merchant/base_edit.html.twig

2>我们需要告诉MerchantAdmin类使用此模板.所以我们覆盖SonataAdmin的getEditTemplate方法如下:

public function getEditTemplate()
{
    return 'YourBundle:Merchant:base_edit.html.twig';
}
Run Code Online (Sandbox Code Playgroud)

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

3.1> - 在控制器中为Ajax请求创建一个Action我们主要想获得一个与特定标签相对应的类别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
{

}
Run Code Online (Sandbox Code Playgroud)

3.2> - 通过定义它来告诉管理服务使用这个新创建的控制器而不是默认的CRUDControllerYourBundle/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]
Run Code Online (Sandbox Code Playgroud)

请注意,第3个参数是控制器的名称.默认情况下,它将为null.

3.3> - 创建getCategoryOptionsFromTagAction控制器中命名的Action .您的Ajax调用将是此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);
    }
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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 %}
Run Code Online (Sandbox Code Playgroud)

问题1:如何获取附加到所有Sonata元素的唯一ID

解决方案:使用admin变量可以访问所有Admin Class的属性,包括uniqId.请参阅有关如何使用它的代码.

问题2:如何在JS中获取路由器.

解决方案:默认情况下,Symfony2 Routing在JS中不起作用.您需要使用名为FOSJSRouting的包(如上所述)并公开路径.这样您也可以访问JS中的Router对象.

我稍微修改了我的解决方案,使这个例子更加清晰.如果您发现任何错误,请随时发表评论.

  • 谢谢老兄,FOSJSRouting是访问Router对象(Routing.generate(...))来生成路由的路径.如果它是一个普通的PHP项目,我们只需要提供文件名 - categories_from_tag.php.但是在这里我们需要在控制器中调用一个动作. (3认同)
  • 我这样做的方法是在我的Admin类(TagAdmin)中添加路由:`$ collection-> add('addToGroup',$ this-> getRouterIdParameter().'/ addToGroup');`,使用此路由ajax url like:`url:'{{admin.generateUrl('addToGroup',{'id':object.id})}}',`带有ajax定义中的额外数据参数:`data:'group =' + $(本).prevAll( '组ID').VAL(),` (3认同)
  • 我不认为使用``MerchantAdmin :: getEditTemplate()``可以工作了.我不得不将其更改为``MerchantAdmin :: getTemplate($ name)``然后在``$ name ==='edit'`时返回我的自定义模板 (3认同)