在Symfony2 Entity字段中添加JQuery自动完成

时间:2022-08-24 21:55:58

I have a city and voters entity in a one to many relationships.I want to convert the entity field(thousands of records in a dropdown) to a text input so that I can implement the JQuery autocomplete when users start typing 2 letters.After almost two weeks,I successfully created the DataTransformer which transform entity field in to a text input.Now my problem is I am still learning the JQuery/Ajax and I am confused how to implement it in Symfony2 forms.

我有一个城市和选民实体的一对多关系。我想将实体字段(下拉列表中的数千条记录)转换为文本输入,以便我可以在用户开始输入2个字母时实现JQuery自动完成。两周,我成功创建了DataTransformer,它将实体字段转换为文本输入。现在我的问题是我还在学习JQuery / Ajax,我很困惑如何在Symfony2表单中实现它。

//formtype.php


private $entityManager;

public function __construct(ObjectManager $entityManager)
{
  $this->entityManager = $entityManager;
}
$builder
        ->add('address', null, array(
        'error_bubbling' => true
      ))
        ->add('city', 'text', array(
        'label' => 'Type your city',
        //'error_bubbling' => true,
        'invalid_message' => 'That city you entered is not listed',
      ))
 $builder->get('city')
      ->addModelTransformer(new CityAutocompleteTransformer($this->entityManager));

//datatransformer.php

class CityAutocompleteTransformer implements DataTransformerInterface
{
private $entityManager;

public function __construct(ObjectManager $entityManager)
{
    $this->entityManager = $entityManager;
}

public function transform($city)
{
    if (null === $city) {
        return '';
    }

    return $city->getName();
}

public function reverseTransform($cityName)
{
    if (!$cityName) {
        return;
    }

    $city = $this->entityManager
        ->getRepository('DuterteBundle:City')->findOneBy(array('name' => $cityName));

    if (null === $city) {
        throw new TransformationFailedException(sprintf('There is no "%s" exists',
            $cityName
        ));
    }

    return $city;
 }
}

//controller.php

//controller.php

public function createAction(Request $request)
{
    $entity = new Voters();
    $form = $this->createCreateForm($entity);
    $form->handleRequest($request);

    $validator = $this->get('validator');
    $errors = $validator->validate($entity);
    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush();


        $this->addFlash('danger', 'You are successfully added!, Welcome to the growing Supporters, dont forget to share and invite this to your friends and relatives, click share buttons below, have a magical day!');

        //return $this->redirect($this->generateUrl('voters_show', array('id' => $entity->getId())));
        return $this->redirect($this->generateUrl('voters_list'));
    } else {

        $this->addFlash('danger', 'Oppss somethings went wrong, check errors buddy!');

        return $this->render('DuterteBundle:Voters:neww.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView(),
        ));
    }
}

/**
 * Creates a form to create a Voters entity.
 *
 * @param Voters $entity The entity
 *
 * @return \Symfony\Component\Form\Form The form
 */
private function createCreateForm(Voters $entity)
{   
    $entityManager = $this->getDoctrine()->getManager();
    $form = $this->createForm(new VotersType($entityManager), $entity, //here i passed the entity manager to make it work
array(
        'action' => $this->generateUrl('voters_create'),
        'method' => 'POST',
    ));

    $form->add('submit', 'submit', array(
        'label' => 'I Will Vote Mayor Duterte'
    ));

    return $form;
}

With this code, I can successfully create a new voter and will throw validation errors(invalid_message in formtype) when a user entered a city name that does not match those already saved in the database.What I am lacking now is I want to implement the JQuery autocomplete when the user type enter at least two letters

使用此代码,我可以成功创建一个新选民,并在用户输入的城市名称与数据库中已保存的城市名称不匹配时抛出验证错误(form_type中的invalid_message)。我现在缺少的是我想实现的当用户输入至少两个字母时,JQuery自动完成

The Twig part

Twig部分

//twig.php

  {{ form_start(form, {attr: {novalidate: 'novalidate'}} ) }}
        {{ form_errors(form) }}
        {{ form_row(form.comments,{'attr': {'placeholder': 'Why You Want '}}) }}
        {{ form_row(form.email,{'attr': {'placeholder': 'Email is optional, you may leave it blank.But if you want to include your email, make sure it is your valid email '}}) }}
        {{ form_end(form) }}    

在Symfony2 Entity字段中添加JQuery自动完成

As you can see, the form itself is consist of many fields, aside from the city field.Here, the city field is a dropdown consists of more than one thousand entries from database.I can successfully convert this dropdown into textfield by using DataTransformer.So the problem here is how to implement JQuery Autocomplete inside this form with many fields.

正如您所看到的,表单本身除了城市字段外还包含许多字段。在这里,城市字段是一个下拉列表,包含来自数据库的一千多个条目。我可以使用DataTransformer将此下拉列表成功转换为文本字段。所以这里的问题是如何在这个包含许多字段的表单中实现JQuery Autocomplete。

Any help is appreciated

任何帮助表示赞赏

Update

更新

Based on user Frankbeen's answer, I Added an action inside my controller

根据用户Frankbeen的回答,我在控制器中添加了一个动作

public function autocompleteAction(Request $request)
{
    $names = array();
    $term = trim(strip_tags($request->get('term')));

    $em = $this->getDoctrine()->getManager();

    $entities = $em->getRepository('DuterteBundle:City')->createQueryBuilder('c')
       ->where('c.name LIKE :name')
       ->setParameter('name', '%'.$term.'%')
       ->getQuery()
       ->getResult();

    foreach ($entities as $entity)
    {
        $names[] = $entity->getName()."({$entity->getProvince()})";
    }

    $response = new JsonResponse();
    $response->setData($names);

    return $response;
}

And also the js file

还有js文件

{% block javascripts %}
{{ parent() }}
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script>
    $(function() {
        function log( message ) {
            $( "<div>" ).text( message ).prependTo( "#log" );
            $( "#log" ).scrollTop( 0 );
        }

        $( "#project_bundle_dutertebundle_voters_city").autocomplete({
            source: "{{ path('city_autocomplete') }}",
            minLength: 2,
            select: function( event, ui ) {
            log( ui.item ?
                "Selected: " + ui.item.value + " aka " + ui.item.id :
                "Nothing selected, input was " + this.value );
            }
        });
    });
</script>
{% endblock %}

In this case, the

在这种情况下,

  $( "#project_bundle_dutertebundle_voters_city").autocomplete({

part is actually the default id of city field provided by Symfony2 when rendering the form.The JQuery autocomplete now is working , but the problem is , I cannot save the selected option, the invalid_message validation I created inside FormType.php is triggered as well as the JQuery script when submit button is clicked

part实际上是Symfony2在渲染表单时提供的城市字段的默认id。现在JQuery自动完成工作正常,但问题是,我无法保存所选的选项,我在FormType.php中创建的invalid_message验证以及单击“提交”按钮时的JQuery脚本

Selected: Basista (Pangasinan Province) aka undefined

选定:Basista(Pangasinan省)又名未定义

which tells that the Id of the selected value is undefined

这表明所选值的Id未定义

$( "#project_bundle_dutertebundle_voters_city").autocomplete({
            source: "{{ path('city_autocomplete') }}",
            minLength: 2,
            select: function( event, ui ) {
            log( ui.item ?
                "Selected: " + ui.item.value + " aka " + ui.item.id ://this throw undefined
                "Nothing selected, input was " + this.value );
            }
        });

3 个解决方案

#1


6  

First you have to start creating a route and action that returns json data. JQuery's autocomplete remote gives you a $_GET variabele with the index 'term' and wants to receive JSON back. Here is an example that uses an Entity with the name City and a property $name

首先,您必须开始创建返回json数据的路由和操作。 JQuery的自动完成远程为您提供带有索引'term'的$ _GET变量,并希望重新接收JSON。下面是一个使用名称为City且属性为$ name的Entity的示例

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
 * City controller.
 *
 * @Route("/city")
 */
class CityController extends Controller
{
    /**
     * @Route("/autocomplete", name="city_autocomplete")
     */
    public function autocompleteAction(Request $request)
    {
        $names = array();
        $term = trim(strip_tags($request->get('term')));

        $em = $this->getDoctrine()->getManager();

        $entities = $em->getRepository('AppBundle:City')->createQueryBuilder('c')
           ->where('c.name LIKE :name')
           ->setParameter('name', '%'.$term.'%')
           ->getQuery()
           ->getResult();

        foreach ($entities as $entity)
        {
            $names[] = $entity->getName();
        }

        $response = new JsonResponse();
        $response->setData($names);

        return $response;
    }
}

Secondary you can make a twig view just like the source from jQuery's autocomplete. The only difference is the source variable in the autocomplete() function . There you have to specify te twig's path() function with your route key eg city_autocomplete.

您可以在辅助视图中创建一个twig视图,就像来自jQuery自动完成的源代码一样。唯一的区别是autocomplete()函数中的源变量。在那里你必须用你的路线键指定te twig的path()函数,例如city_autocomplete。

(This view needs another route and another (normal) action.)

(此视图需要另一个路径和另一个(正常)操作。)

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery UI Autocomplete - Remote datasource</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
  <script src="//code.jquery.com/jquery-1.10.2.js"></script>
  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
  <link rel="stylesheet" href="/resources/demos/style.css">
  <style>
  .ui-autocomplete-loading {
    background: white url("images/ui-anim_basic_16x16.gif") right center no-repeat;
  }
  </style>
  <script>
  $(function() {
    function log( message ) {
      $( "<div>" ).text( message ).prependTo( "#log" );
      $( "#log" ).scrollTop( 0 );
    }

    $( "#birds" ).autocomplete({
      source: "{{ path('city_autocomplete') }}",
      minLength: 2,
      select: function( event, ui ) {
        log( ui.item ?
          "Selected: " + ui.item.value + " aka " + ui.item.id :
          "Nothing selected, input was " + this.value );
      }
    });
  });
  </script>
</head>
<body>

<div class="ui-widget">
  <label for="birds">Birds: </label>
  <input id="birds">
</div>

<div class="ui-widget" style="margin-top:2em; font-family:Arial">
  Result:
  <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div>
</div>


</body>
</html>

And finaly you can slightly change this view and use your own form.

最后,您可以稍微更改此视图并使用您自己的表单。

#2


2  

Finally, after digging deeper in to my Symfony code, I finally found the solution.Using the code provided by user Frankbeen, I add some 'tweak' inorder for the JQuery to finally work.The culprit is in the controller.

最后,在深入研究我的Symfony代码后,我终于找到了解决方案。使用用户Frankbeen提供的代码,我添加了一些'调整'以便JQuery最终工作。罪魁祸首在控制器中。

$names[] = $entity->getName()."({$entity->getProvince()})";

The city entity is related to province entity in one to many relationship.Since the city entity has thousands of name(records) chances are some values will have same name,so appending related province is useful to avoid confusions in users

城市实体在一对多关系中与省实体相关。由于城市实体有数千个名称(记录),因此某些值具有相同的名称,因此附加相关省有助于避免用户混淆

San Francisco(Russia Province), San Francisco(Chinese Province),San Francisco(Portugal Province)

旧金山(俄罗斯省),旧金山(中国省),旧金山(葡萄牙省)

在Symfony2 Entity字段中添加JQuery自动完成

Now since names where now 'different' in the names already saved in database, the invalid_message validation will trigger the errors.My solution is to 'clean' the submitted data by removing the appended provinces before comparing the user submitted values to the values in the database. Inside the DataTransformer, I added some code

现在,由于名称现在已经保存在数据库中的名称“不同”,invalid_message验证将触发错误。我的解决方案是通过删除附加的省份来“清理”提交的数据,然后再将用户提交的值与数据库。在DataTransformer中,我添加了一些代码

public function reverseTransform($cityNameConcat)
{
    $cityName = preg_replace("/\([^)]+\)/", "", $cityNameConcat);
    if (!$cityName) {
        return;
    }

    $city = $this->entityManager
        ->getRepository('DuterteBundle:City')->findOneBy(array('name' => $cityName));

    if (null === $city) {
        throw new TransformationFailedException(sprintf('There is no "%s" exists',
            $cityName
        ));
    }

    return $city;
}

Now the JQuery is finally working and saving to database is also successful.And lastly,

现在JQuery终于工作了,保存到数据库也很成功。最后,

$( "#project_bundle_dutertebundle_voters_city").autocomplete({
        source: "{{ path('city_autocomplete') }}",
        minLength: 2,
        select: function( event, ui ) {
        log( ui.item ?
            "Selected: " + ui.item.value + " aka " + ui.item.id ://this throw undefined
            "Nothing selected, input was " + this.value );
        }
    });

Is changed to

改为

<script>
    $(function() {
        function log( message ) {
            $( "<div>" ).text( message ).prependTo( "#log" );
            $( "#log" ).scrollTop( 0 );
        }

        $( "#project_bundle_dutertebundle_voters_city").autocomplete({
            source: "{{ path('city_autocomplete') }}",
            minLength: 2,
            select: function( event, ui ) {
            log( ui.item ?
                "Selected: " + ui.item.value + " aka " + ui.item.label:
                "Nothing selected, input was " + this.value );
                $("#project_bundle_dutertebundle_voters_city").val(ui.item.label);
                $("#project_bundle_dutertebundle_voters_city").val(ui.item.value);
                return false;
            },
            change: function( event, ui ) {
                $( "#project_bundle_dutertebundle_voters_city" ).val( ui.item? ui.item.value : 0 );
} 
        });
    });
</script>

Now the 'Undefined' error is gone.

现在'未定义'错误消失了。

#3


0  

Here a solution to add a field regarding the response given by the Symfony Controller. In success, add fields you want by returning a object. Then, in the select, you can access it by ui.item.fields

这是一个解决方案,用于添加有关Symfony控制器给出的响应的字段。成功通过返回对象添加所需的字段。然后,在select中,您可以通过ui.item.fields访问它

$('#mySelector').autocomplete({

        source : function(requete, reponse) {

            lettre = {
                lettre: $('#lettre').val()
            };

            $.ajax({
                url: Routing.generate('chargementSource'),
                dataType: 'json',
                data : lettre,
                success: function (donnee) {
                    
                    reponse(
                            $.map(donnee, function (objet) {
                                return { 
                                  value: '' + objet.nom + ' ' + objet.prenom +', '+ objet.adresse +' '+ objet.codepostal +', '+ objet.ville + '', 
                                 id: objet.id 
                               }
                            })
                    );

                }

            });
        },
        select: function (event, ui) {

            $('#myId').val(ui.item.id);
            //alert(ui.item.id);
            //$('#myId').val(ui.elem.value);

            return false;

        }

    });

#1


6  

First you have to start creating a route and action that returns json data. JQuery's autocomplete remote gives you a $_GET variabele with the index 'term' and wants to receive JSON back. Here is an example that uses an Entity with the name City and a property $name

首先,您必须开始创建返回json数据的路由和操作。 JQuery的自动完成远程为您提供带有索引'term'的$ _GET变量,并希望重新接收JSON。下面是一个使用名称为City且属性为$ name的Entity的示例

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
 * City controller.
 *
 * @Route("/city")
 */
class CityController extends Controller
{
    /**
     * @Route("/autocomplete", name="city_autocomplete")
     */
    public function autocompleteAction(Request $request)
    {
        $names = array();
        $term = trim(strip_tags($request->get('term')));

        $em = $this->getDoctrine()->getManager();

        $entities = $em->getRepository('AppBundle:City')->createQueryBuilder('c')
           ->where('c.name LIKE :name')
           ->setParameter('name', '%'.$term.'%')
           ->getQuery()
           ->getResult();

        foreach ($entities as $entity)
        {
            $names[] = $entity->getName();
        }

        $response = new JsonResponse();
        $response->setData($names);

        return $response;
    }
}

Secondary you can make a twig view just like the source from jQuery's autocomplete. The only difference is the source variable in the autocomplete() function . There you have to specify te twig's path() function with your route key eg city_autocomplete.

您可以在辅助视图中创建一个twig视图,就像来自jQuery自动完成的源代码一样。唯一的区别是autocomplete()函数中的源变量。在那里你必须用你的路线键指定te twig的path()函数,例如city_autocomplete。

(This view needs another route and another (normal) action.)

(此视图需要另一个路径和另一个(正常)操作。)

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery UI Autocomplete - Remote datasource</title>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
  <script src="//code.jquery.com/jquery-1.10.2.js"></script>
  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
  <link rel="stylesheet" href="/resources/demos/style.css">
  <style>
  .ui-autocomplete-loading {
    background: white url("images/ui-anim_basic_16x16.gif") right center no-repeat;
  }
  </style>
  <script>
  $(function() {
    function log( message ) {
      $( "<div>" ).text( message ).prependTo( "#log" );
      $( "#log" ).scrollTop( 0 );
    }

    $( "#birds" ).autocomplete({
      source: "{{ path('city_autocomplete') }}",
      minLength: 2,
      select: function( event, ui ) {
        log( ui.item ?
          "Selected: " + ui.item.value + " aka " + ui.item.id :
          "Nothing selected, input was " + this.value );
      }
    });
  });
  </script>
</head>
<body>

<div class="ui-widget">
  <label for="birds">Birds: </label>
  <input id="birds">
</div>

<div class="ui-widget" style="margin-top:2em; font-family:Arial">
  Result:
  <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div>
</div>


</body>
</html>

And finaly you can slightly change this view and use your own form.

最后,您可以稍微更改此视图并使用您自己的表单。

#2


2  

Finally, after digging deeper in to my Symfony code, I finally found the solution.Using the code provided by user Frankbeen, I add some 'tweak' inorder for the JQuery to finally work.The culprit is in the controller.

最后,在深入研究我的Symfony代码后,我终于找到了解决方案。使用用户Frankbeen提供的代码,我添加了一些'调整'以便JQuery最终工作。罪魁祸首在控制器中。

$names[] = $entity->getName()."({$entity->getProvince()})";

The city entity is related to province entity in one to many relationship.Since the city entity has thousands of name(records) chances are some values will have same name,so appending related province is useful to avoid confusions in users

城市实体在一对多关系中与省实体相关。由于城市实体有数千个名称(记录),因此某些值具有相同的名称,因此附加相关省有助于避免用户混淆

San Francisco(Russia Province), San Francisco(Chinese Province),San Francisco(Portugal Province)

旧金山(俄罗斯省),旧金山(中国省),旧金山(葡萄牙省)

在Symfony2 Entity字段中添加JQuery自动完成

Now since names where now 'different' in the names already saved in database, the invalid_message validation will trigger the errors.My solution is to 'clean' the submitted data by removing the appended provinces before comparing the user submitted values to the values in the database. Inside the DataTransformer, I added some code

现在,由于名称现在已经保存在数据库中的名称“不同”,invalid_message验证将触发错误。我的解决方案是通过删除附加的省份来“清理”提交的数据,然后再将用户提交的值与数据库。在DataTransformer中,我添加了一些代码

public function reverseTransform($cityNameConcat)
{
    $cityName = preg_replace("/\([^)]+\)/", "", $cityNameConcat);
    if (!$cityName) {
        return;
    }

    $city = $this->entityManager
        ->getRepository('DuterteBundle:City')->findOneBy(array('name' => $cityName));

    if (null === $city) {
        throw new TransformationFailedException(sprintf('There is no "%s" exists',
            $cityName
        ));
    }

    return $city;
}

Now the JQuery is finally working and saving to database is also successful.And lastly,

现在JQuery终于工作了,保存到数据库也很成功。最后,

$( "#project_bundle_dutertebundle_voters_city").autocomplete({
        source: "{{ path('city_autocomplete') }}",
        minLength: 2,
        select: function( event, ui ) {
        log( ui.item ?
            "Selected: " + ui.item.value + " aka " + ui.item.id ://this throw undefined
            "Nothing selected, input was " + this.value );
        }
    });

Is changed to

改为

<script>
    $(function() {
        function log( message ) {
            $( "<div>" ).text( message ).prependTo( "#log" );
            $( "#log" ).scrollTop( 0 );
        }

        $( "#project_bundle_dutertebundle_voters_city").autocomplete({
            source: "{{ path('city_autocomplete') }}",
            minLength: 2,
            select: function( event, ui ) {
            log( ui.item ?
                "Selected: " + ui.item.value + " aka " + ui.item.label:
                "Nothing selected, input was " + this.value );
                $("#project_bundle_dutertebundle_voters_city").val(ui.item.label);
                $("#project_bundle_dutertebundle_voters_city").val(ui.item.value);
                return false;
            },
            change: function( event, ui ) {
                $( "#project_bundle_dutertebundle_voters_city" ).val( ui.item? ui.item.value : 0 );
} 
        });
    });
</script>

Now the 'Undefined' error is gone.

现在'未定义'错误消失了。

#3


0  

Here a solution to add a field regarding the response given by the Symfony Controller. In success, add fields you want by returning a object. Then, in the select, you can access it by ui.item.fields

这是一个解决方案,用于添加有关Symfony控制器给出的响应的字段。成功通过返回对象添加所需的字段。然后,在select中,您可以通过ui.item.fields访问它

$('#mySelector').autocomplete({

        source : function(requete, reponse) {

            lettre = {
                lettre: $('#lettre').val()
            };

            $.ajax({
                url: Routing.generate('chargementSource'),
                dataType: 'json',
                data : lettre,
                success: function (donnee) {
                    
                    reponse(
                            $.map(donnee, function (objet) {
                                return { 
                                  value: '' + objet.nom + ' ' + objet.prenom +', '+ objet.adresse +' '+ objet.codepostal +', '+ objet.ville + '', 
                                 id: objet.id 
                               }
                            })
                    );

                }

            });
        },
        select: function (event, ui) {

            $('#myId').val(ui.item.id);
            //alert(ui.item.id);
            //$('#myId').val(ui.elem.value);

            return false;

        }

    });