web-dev-qa-db-ja.com

Sonata管理フォーム内でAjaxを使用する方法

私は次のフィールドと関連付けを持つマーチャントエンティティを持っています:-

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

タグとカテゴリにも、ManyToManyマッピングがあります。したがって、Tag_Category、Merchant_Tag、Merchant_Categoryマッピングテーブルがあります。

次に、これらのフィールドでajaxを実行したいと思います。

ユーザーが最初にプライマリタグを選択できるようにします。 ajaxは、プライマリタグに基づいて、カテゴリをこのタグに属するカテゴリのみに更新します。

どうすればこれを達成できますか?

ありがとう!

29
Amit

数か月前にこの作品を作ることができました。 a.aitboudadが共有したものは正確ですが。 Symfony/Sonataの最初のタイマーが直面するかもしれないいくつかの落とし穴があります。

手順は次のとおりです。

1> Sonata CRUDのedit.html.twig/base_edit.html.twigを拡張します。簡単にするために、後者のみを使用します。 MerchantAdminControllerに対応するビューフォルダにvendor/bundles/Sonata/AdminBundle/Resources/views/CRUD/base_edit.html.twigをコピーします-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]

3番目の引数はコントローラーの名前です。デフォルトではnullでした。

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

Gotcha 1:すべてのSonata要素に追加される一意のIDを取得する方法

解決策:uniqIdを含むすべての管理クラスのプロパティへのアクセスを提供する管理変数を使用します。それを使用する方法のコードを参照してください。

Gotcha 2:JSでルーターを取得する方法。

解決策:デフォルトでは、Symfony2ルーティングはJSで機能しません。 FOSJSRouting(上記で説明)と呼ばれるバンドルを使用し、ルートを公開する必要があります。これにより、JS内のルーターオブジェクトにもアクセスできます。

この例をより明確にするために、ソリューションを少し変更しました。何かお気づきの点がございましたら、お気軽にコメントください。

59
Amit

オーバーライドの方法を更新し、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
guillermogfer

アミットとルンベンディルの回答のステップ1では、変更する必要があります

{% extends base_template %}

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

次のようなエラーが発生した場合

Unable to find template "" in YourBundle:YourObject:base_edit.html.twig at line 34.  
4
Paul Prijs

ブロックJavaScriptで、"liszt:updated""chosen:updated"に変更する必要があります

それが誰かを助けることを願っています;)

2