我正在使用带有PropelBundle的Symfony 2.1表单,我正在尝试重构一个包含对象下拉列表(从中选择)的表单,而不是使用jquery自动完成字段(使用AJAX)。对于下拉列表,我在表单类型中使用了以下代码(对于下拉列表非常有效):
$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 to be able to get a Book object from a simple text field以传递书籍ID,如the answer to this Question中所示。但是,我无法弄清楚如何使Data Transformer与集合类型和Propel类一起工作。我创建了一个BookToIdTransformer类,如Symfony Cookbook中所示,并在“ReaderType”文件中尝试了以下内容:
$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”异常(显然,Transformer期望一个PropelCollection of Books,而不是一个Book对象......)。有谁知道如何去做?或者让我知道是否有其他方法可以使用Propel在Symfony中实现自动完成并允许选择多个对象(例如书籍集合)?
答案 0 :(得分:14)
我最终采用的解决方案与我之前的回答略有不同。我最终在我的“ReaderType”表单中使用了“text”字段类型而不是“collection”字段类型,因为我最终使用Loopj Tokeninput jQuery plugin来允许选择多个对象(例如“Favorite Book”)到在表单中与我的主对象(例如“Reader”对象)相关联。考虑到这一点,我创建了一个“数据转换器”,用于将传递的对象ID(在文本字段中以逗号分隔的列表中)转换为Propel对象集合。代码共享如下,包括示例ajax对象控制器。
“ReaderType”表单的关键部分如下所示:
$transformer = new BooksToIdsTransformer();
$builder->add(
$builder->create('books', 'text', array(
'required' => false,
))->addModelTransformer($transformer)
);
“Data Transformer”文件如下所示:
// 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集合返回给Tokeninput自动完成功能的ajax控制器如下:
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”文件中:
ajax_book:
pattern: /ajax_book
defaults: { _controller: MySiteBundle:ClassAjax:book }