我正在Symfony的文章编辑器上工作,内置标记功能:
class MainController extends Controller
{
public function indexAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
// $article = ...
$form = $this->createForm(new ArticleType(), $article);
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($article);
$em->flush();
return $this->redirect($this->generateUrl('acme_edit_success'));
}
return $this->render('AcmeBundle:Main:index.html.twig', array(
'form' => $form->createView()
));
}
}
标记表单注册为具有@Doctrine
参数的服务,因此我可以在类中使用实体管理器。标签表单嵌入在文章表单中。
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content')
->add('tags', 'collection', array(
'type' => 'acme_bundle_tagtype',
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false
))
->add('save', 'submit')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\Bundle\Entity\Article',
'cascade_validation' => true
));
}
public function getName()
{
return 'acme_bundle_articletype';
}
}
class TagType extends AbstractType
{
private $entityManager;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new TagTransformer($this->entityManager);
$builder->add(
$builder->create('name')
->addModelTransformer($transformer)
);
}
function __construct(\Doctrine\Bundle\DoctrineBundle\Registry $doctrine) {
$this->entityManager = $doctrine->getManager();
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\Bundle\Entity\Tag'
));
}
public function getName()
{
return 'acme_bundle_tagtype';
}
}
我创建了这个数据转换器来检查给定的标签是否已经存在,然后将标签对象转换为数据库中已存在的标签对象:
class TagTransformer implements DataTransformerInterface
{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function transform($tag)
{
if (null === $tag) {
return '';
}
return $tag;
}
public function reverseTransform($name)
{
if (!$name)
return null;
$tag = $this->om
->getRepository('AcmeBundle:Tag')
->findOneByName($name)
;
if (!$tag) {
$tag = new Tag();
$tag->setName($name);
}
return $tag;
}
}
当我尝试使用已存在的标记保存文章时,reverseTransform()
函数成功返回原始标记对象,但是 DBAL通过其__toString()
将对象转换回字符串方法,并且Doctrine仍会启动INSERT
查询而不是UPDATE
,因此我收到了下一个错误:
执行' INSERT INTO Tag(名称)时发生异常 价值观(?)'用params [{}]:
SQLSTATE [23000]:完整性约束违规:1062重复条目 '现有标签'关键字' UNIQ_0123456789ABCDE'
我该如何解决这个问题?当我输入已经在使用的标签名称时,我希望Symfony在文章标签关系中使用相同的标签。实体类出现在我之前关于how to avoid duplicate entries in a many-to-many relationship with Doctrine的问题中。
答案 0 :(得分:1)
你的变压器有一个错误。而不是检查name是否为null,您应该验证是否返回了标记:
if (!$tag) {
$tag = new Tag();
$tag->setName($name);
}
您也不需要保留标记,因为默认情况下,doctrine会级联持久化所有相关实体。
完整方法:
public function reverseTransform($name)
{
if (!$name) {
return null;
}
$tag = $this->om
->getRepository('AcmeBundle:Tag')
->findOneByName($name)
;
if (!$tag) {
$tag = new Tag();
$tag->setName($name);
}
return $tag;
}