问题描述
我正在使用带有 PropelBundle 的 Symfony 2.1 表单,并且我正在尝试重构一个具有对象下拉列表(可供选择)的表单,而不是使用 jquery 自动完成字段(使用 AJAX).对于下拉列表,我在表单类型中使用了以下代码(对于下拉列表非常有效):
I'm using Symfony 2.1 forms with PropelBundle and I'm trying to refactor a form that had a drop-down list of objects (to select from) to instead use a jquery autocomplete field (working with AJAX). For the dropdown list I was using the following code (which worked perfectly for the drop-down) in my form type:
$builder->add('books', 'collection', array(
'type' => 'model',
'options' => array(
'class' => 'MyVendor\MyBundle\Model\Book',
'property' => 'title',
),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
));
为了提供一些上下文,假设我们正在创建一个新的Reader"对象,并且我们希望从可用的Book"对象列表中选择 Reader 最喜欢的书籍.使用收藏类型,以便可以在新的阅读器"表单中选择许多最喜欢的书籍".现在,我想更改上述内容以使用自动完成功能.为此,我尝试实现一个 Data Transformer 以便能够获得一本书来自简单文本字段的对象,可用于自动完成功能以传递图书 ID,如这个问题的答案.但是,我无法弄清楚如何使 Data Transformer 与集合类型和 Propel 类一起工作.我按照 Symfony Cookbook 中的说明创建了一个 BookToIdTransformer 类,并在ReaderType"文件中尝试了以下操作:
For the sake of giving a little context, let's say we are creating a new "Reader" object and that we would like to select the Reader's favorite books from a list of available "Book" objects. A collection type is used so that many "favorite books" can be selected in the new "Reader" form. Now, I would like to change the above to use autocomplete. For doing so, I tried to implement a Data Transformer to be able to get a Book object from a simple text field that could be used for the Autocomplete function to pass the Book ID as indicated in the answer to this Question. However, I was not able to figure out how to make the Data Transformer work with a collection type and Propel classes. I created a BookToIdTransformer class as indicated in the Symfony Cookbook and tried the following in the "ReaderType" file:
$transformer = new BookToIdTransformer();
$builder->add(
$builder->create('books', 'collection', array(
'type' => 'text',
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
))->addModelTransformer($transformer)
);
通过上述,我得到一个调用未定义的方法:getId"异常(显然转换器需要一个 PropelCollection of Books,而不是单个 Book 对象..).有谁知道该怎么做?或者让我知道是否有其他方法可以使用 Propel 在 Symfony 中实现自动完成并允许选择多个对象(例如书籍集合)?
With the above, I get a "Call to undefined method: getId" exception (apparently the Transformer expects a PropelCollection of Books, not a single Book object..). Does anyone know how to go about it? or let me know if there are other ways to implement the autocomplete in Symfony using Propel and allowing for selecting multiple objects (e.g. a collection of books)?
推荐答案
我最终采用的解决方案与我之前的答案略有不同.我最终在ReaderType"表单中使用了文本"字段类型而不是集合"字段类型,因为我最终使用了 Loopj Tokeninput jQuery 插件,它允许选择多个对象(例如收藏夹")以关联到表单中的主要对象(例如阅读器"对象).考虑到这一点,我创建了一个数据转换器",用于将传递的对象 ID(在文本字段中的逗号分隔列表中)转换为 Propel 对象集合.代码共享如下,包括一个示例ajax对象控制器.
The solution I ultimately went for is slightly different from my previous answer. I ended up using a "text" field type instead of a "collection" field type in my "ReaderType" form, since I ended up using the Loopj Tokeninput jQuery plugin which allows for selecting multiple objects (e.g. "Favorite Book") to associate to my main object (e.g. "Reader" object) in the form. Considering that, I created a "Data Transformer" for transforming the objects' ids passed (in a comma separated list in the text field) into a Propel Object Collection. The code is shared as follows, including a sample ajax object controller.
ReaderType"表单的关键部分如下所示:
The key part of the "ReaderType" form looks as follows:
$transformer = new BooksToIdsTransformer();
$builder->add(
$builder->create('books', 'text', array(
'required' => false,
))->addModelTransformer($transformer)
);
数据转换器"文件如下所示:
The "Data Transformer" file looks like this:
// src/MyVendor/MyBundle/Form/DataTransformer/BooksToIdsTransformer.php
namespace MyVendor\MyBundle\Form\DataTransformer;
use \PropelObjectCollection;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use MyVendor\MyBundle\Model\BookQuery;
class BooksToIdsTransformer implements DataTransformerInterface
{
public function transform($books)
{
if (null === $books) {
return "";
}
if (!$books instanceof PropelObjectCollection) {
throw new UnexpectedTypeException($books, '\PropelObjectCollection');
}
$idsArray = array();
foreach ($books as $book) {
$idsArray[] = $book->getId();
}
$ids = implode(",", $idsArray);
return $ids;
}
public function reverseTransform($ids)
{
$books = new PropelObjectCollection();
if ('' === $ids || null === $ids) {
return $books;
}
if (!is_string($ids)) {
throw new UnexpectedTypeException($ids, 'string');
}
$idsArray = explode(",", $ids);
$idsArray = array_filter ($idsArray, 'is_numeric');
foreach ($idsArray as $id) {
$books->append(BookQuery::create()->findOneById($id));
}
return $books;
}
}
返回一个json集合books"给Tokeninput自动完成函数的ajax控制器如下:
The ajax controller that returns a json collection of "books" to the Tokeninput autocomplete function is as follows:
namespace MyVendor\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use MyVendor\MyBundle\Model\BookQuery;
class ClassAjaxController extends Controller
{
public function bookAction(Request $request)
{
$value = $request->get('q');
$books = BookQuery::create()->findByName('%'.$value.'%');
$json = array();
foreach ($books as $book) {
$json[] = array(
'id' => $book->getId(),
'name' => $book->getName()
);
}
$response = new Response();
$response->setContent(json_encode($json));
return $response;
}
}
最后,routing.yml"文件中的路由器:
And finally, the router in the "routing.yml" file:
ajax_book:
pattern: /ajax_book
defaults: { _controller: MySiteBundle:ClassAjax:book }
这篇关于如何在 Symfony2 表单中为集合添加自动完成字段并使用 Propel?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!