Symfony2和Selectize.js:在实体字段类型中保留新项的最清晰方法



在Symfony2中,我有BandType,其中我添加了实体Tag

->add('tags', 'entity', [
'label' => 'Tags',
'class' => 'DbBundle:Tag',
'property' => 'title',
'multiple'  =>  true,
])

这会生成多选元素,在这里我可以从数据库中选择现有标签(条令)。但是我需要添加新的标签dynamicaly,这些标签还不存在。

在客户端,我使用jQuery插件Selectize.js,它允许我向选择框添加新标签。但在提交表单后,新的标记不会被保存

所以我的问题是-从选择框(实体字段类型)中持久化新项目的最清晰方法是什么

为实体使用数据转换器。在reverseTransform方法中,如果没有找到新添加的band,只需在那里创建它,而不是抛出TransformationFailedException。

一种可能的解决方案是使用FormEvents。以下是示例代码:

namespace AppBundleForm;
use AppBundleEntityTag;
use DoctrineCommonPersistenceObjectManager;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentFormFormEvents;
use SymfonyComponentFormFormEvent;
use SymfonyComponentOptionsResolverOptionsResolver;
class PostType extends AbstractType
{
/**
* @var ObjectManager
*/
private $manager;
/**
* Constructor
*
* @param ObjectManager $manager
*/
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('content')
->add('tags')
;
$builder->get('tags')->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
$choiceList = $event->getForm()->getConfig()->getAttribute('choice_list');
$array = is_null($event->getData()) ? [] : $event->getData();
$choices = $choiceList->getChoicesForValues($array);
if (count($choices) !== count($array)) {
$values = $choiceList->getValuesForChoices($choices);
$diff = array_merge(array_diff($values, $array), array_diff($array, $values));
foreach ($diff as $value) {
$new = new Tag($value);
$this->manager->persist($new);
$this->manager->flush();
$values[] = $new->getId();
}
$event->setData($values);
}
}
);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundleEntityPost'
));
}
}

如另一个答案中所述,您需要为实体使用数据转换器,如果找不到用户要求的实体,则返回一个新实体。

有很多方法可以做到这一点。这是一种方法,从一个恰好使用selectize.js的应用程序简化而来,但这些概念适用于前端的任何UI。

class SubjectTransformer implements DataTransformerInterface
{
protected $em;
public function __construct($em)
{
$this->em = $em;
}
//public function transform($val) { ... }
public function reverseTransform($str)
{
$repo = $this->em->getRepository('AppBundle:Subject');
$subject = $repo->findOneByName($str);
if($subject)
return $subject;
//Didn't find it, so it must be new 
$subject = new Subject;
$subject->setName($str);
$this->em->persist($subject);
return $subject;
}
}

具体地,该DataTransformer用于CollectionType字段的entry_type

  • 在其构造函数中使用实体管理器
  • reverseTransform中,使用EM从数据库中检索值
  • 如果找不到,则创建一个新实体,并将其持久化
  • 显式不会刷新实体,以防表单处理器/控制器在实际提交新实体之前想要对其执行额外验证

其他可能的变化包括不调用em->persist;呼叫CCD_ 9;或者(可能是理想的)传递服务来管理搜索/创建,而不是直接使用实体管理器。(这样的服务可能会实现近乎重复的检测、糟糕的语言过滤,只给某些用户创建新标签的能力,等等。)

最新更新