忽略了ManyToOne实体上的Symfony2 / Doctrine UniqueEntity

时间:2015-01-02 15:21:23

标签: symfony doctrine-orm unique-constraint

我有"角色"与"项目"相关联。 我不在乎角色名称是否重复,但我想确定的是,对于每个项目,角色名称都不能重复。

以下是我认为应该有效的方法:

<?php
// src/AppBundle/Entity/Role.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\RoleRepository")
 * @ORM\Table(name="roles")
 * @UniqueEntity(fields={"name","project"}, message="Duplicated role for this project")
 */
class Role
{
/**
 * @ORM\Column(type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * @ORM\Column(type="string", length=100)
 */
protected $name;

/**
 * @ORM\Column(type="text")
 */
protected $description;

...
other fields
...

/**
 * @ORM\ManyToOne(targetEntity="Project")
 */
protected $project;
}

根据文档here,这正是我所需要的:

  

此必填选项是此字段的字段(或字段列表)   实体应该是唯一的。例如,如果您同时指定了电子邮件   并在一个UniqueEntity约束中命名字段,然后它会   强制组合值在唯一的位置(例如,两个用户可以   有相同的电子邮件,只要他们也没有相同的名称。

简单地忽略约束(我的意思是,如果我尝试为同一个项目设置相同的角色名称,它会存储重复的角色名称和projectID)。

我错过了什么?

编辑:在我使用&#34; php app / console doctrine:schema:update --force&#34;更新数据库之后我试图直接用SQL生成错误,但没有抛出任何异常。现在,我不知道这个&#34; UniqueEntity&#34;验证是在DB级别或它的Doctrine验证器上完成的。

EDIT2:我试图只有一个字段(&#34;名称&#34;)并且验证工作正常(当然只在该字段上)。 我还尝试对字段进行验证&#34; name&#34;和&#34;描述&#34;它的工作原理!!!所以基本上它不会验证要验证的字段是否是指向另一个表的ID。

无论如何,这是控制器:

/**
 * @Route("/role/create/{projectID}", name="role_create")
 */
public function createRoleAction(Request $request, $projectID)
{
    $prj = $this->getDoctrine()->getRepository('AppBundle:Project')->findOneById($projectID);

    $role = new Role();
    $form = $this->createForm(new RoleFormType(), $role);

    $form->handleRequest($request);

    if ($form->isValid())
        {
        $em = $this->getDoctrine()->getManager();
        $role->setProject($prj);
        $em->persist($role);
        $em->flush();
        return $this->redirect($this->generateUrl('hr_manage', array('projectID' => $projectID)));
        }

return $this->render('Role/createForm.html.twig', array('projectID' => $projectID, 'form' => $form->createView(),));
}

未执行验证,并且实体持久保存在DB上,使用&#34;项目&#34;列指向正确的项目。以下是2个相关领域的快照: enter image description here

这是RoleFormType(相关字段的摘录):

<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RoleFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    // add your custom field
    $builder->add('name', 'text')
            ->add('description', 'text')
            ...lots of other fields, but "project" is not present as it's passed automatically from the controller
            ->add('save', 'submit', array('label' => 'Create'));

}

public function getName()
{
    return 'role';
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array('data_class' => 'AppBundle\Entity\Role',));
}

}

1 个答案:

答案 0 :(得分:3)

问题是您实际上并未验证实体以检查唯一约束违规。当您调用$form->isValid()时,它会调用Role实体的验证程序,因为您将其作为表单的数据类传递。但是,由于project在此之后才设置,因此project字段不会进行验证。

当您致电$em->persist($role);$em->flush();时,这只是告诉Doctrine将实体插入数据库。这2次调用自行执行验证,因此将插入重复的

尝试在创建表单之前设置项目:

$role = new Role();
$role->setProject($prj);

$form = $this->createForm(new RoleFormType(), $role);

现在将在实体上设置项目,因此当调用$form->isValid()时,Symfony验证器将检查唯一性。

如果这不起作用,您还希望将project类型作为hidden字段添加到表单中,以便将其传回,但是我不认为这是必要的。

我要说的另一件事是你肯定想在你的数据库本身添加唯一约束 - 这种方式即使你试图插入一个副本,数据库也会向你抛出异常而不允许它,无论如何你的代码。