Symfony2 和 Selectize.js:在实体字段类型中保留新项目的最清晰方法?
Posted
技术标签:
【中文标题】Symfony2 和 Selectize.js:在实体字段类型中保留新项目的最清晰方法?【英文标题】:Symfony2 and Selectize.js: Clearest way to persist new items in entity field type? 【发布时间】:2015-07-01 23:20:43 【问题描述】:在 Symfony2 中,我有 BandType
,我在其中添加了实体 Tag
:
->add('tags', 'entity', [
'label' => 'Tags',
'class' => 'DbBundle:Tag',
'property' => 'title',
'multiple' => true,
])
这会生成多个选择元素,我可以在其中从数据库(Doctrine)中选择现有标签。但我需要动态添加新标签,目前尚不存在。
在客户端,我使用 jQuery 插件Selectize.js,它允许我向选择框添加新标签。但提交表单后,新的标签不会保存。
所以我的问题是 - 从选择框(实体字段类型)中保留新项目的最清晰方法是什么?
【问题讨论】:
【参考方案1】:为您的实体使用Data Transformer。而在 reverseTransform 方法中,如果没有找到新添加的带区,只需在那里创建它,而不是抛出 TransformationFailedException。
【讨论】:
您好,您能发布您的最终解决方案吗?谢谢【参考方案2】:一种可能的解决方案是使用FormEvents。这是示例代码:
namespace AppBundle\Form;
use AppBundle\Entity\Tag;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\OptionsResolver\OptionsResolver;
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' => 'AppBundle\Entity\Post'
));
【讨论】:
【参考方案3】:如另一个答案中所述,您需要为您的实体使用Data Transformer,如果找不到用户要求的实体,则返回一个新实体。
有很多方法可以做到这一点。这是一种方法,它从恰好使用 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
;打电话给em->flush
;或(可能理想情况下)传递服务来管理搜索/创建,而不是直接使用实体管理器。 (这样的服务可能会实现近似重复检测、不良语言过滤、只允许某些用户创建新标签等)
【讨论】:
您的答案与多对多关系无关。 @kuboslav 原始问题或您的赏金没有具体提及多对多关系,但是,此代码用于我的实际应用程序中的一个。 它没有明确写出来,但是是的,它是——而且你的解决方案也不适用于这种情况→每个乐队(实体)有多个标签(集合) 好吧,这里的困惑是,这不是一个集合的转换器。它是构成集合的子表单的转换器。以上是关于Symfony2 和 Selectize.js:在实体字段类型中保留新项目的最清晰方法?的主要内容,如果未能解决你的问题,请参考以下文章