Doctrine 2和Symfony 2中的唯一约束条件

34

我想在我的Doctrine 2实体中创建一个唯一约束,使得nametest在列级别上是唯一的。意味着:

  • 对象1

    • 名称:name1
    • 测试:test
  • 对象2

    • 名称:name2
    • 测试:test <---- 重复了

这应该会触发一个错误,因为test被重复了。

我尝试使用唯一约束(Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity),尝试了

 * @UniqueEntity("name")
 * @UniqueEntity("test")
并且
 * @UniqueEntity({"name", "test"})

当我同时复制name和test时,两者似乎只会触发错误。例如:

  • obj1

    • name: name1
    • test: test
  • obj2

    • name: name2
    • test: test

应该如何正确设置?还是我可能犯了某些错误?

也许我应该包含Doctrine注释,例如:

@Table(name="ecommerce_products",uniqueConstraints={@UniqueConstraint(name="search_idx", columns={"name", "email"})})

但是这仍然不能处理我的Symfony表单验证,我想是吗?

更新

我的测试代码:

/**
 * @ORM\Entity
 * @ORM\Table(name="roles") 
 * @UniqueEntity("name")
 * @UniqueEntity("test")
 */
class Role {

    /**
     * @var integer
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    protected $id;

    /**
     * @var string
     * 
     * @ORM\Column(type="string", length=32, unique=true)
     * @Assert\MaxLength(32)
     * @Assert\Regex("/^[a-zA-Z0-9_]+$/")
     */
    protected $name;

}

$v = $this->get('validator');

$role = new Role();
$role->setName('jm');
$role->setTest('test');
$e = $v->validate($role);
echo '=== 1 ===';
var_dump($e);
if (count($e) == 0)
    $em->persist($role);            

$role2 = new Role();
$role2->setName('john');
$role2->setTest('test');
$e = $v->validate($role2);
echo '=== 2 ===';
var_dump($e);
if (count($e) == 0)
    $em->persist($role2);

$em->flush();

在第一次运行时(空表):

=== 1 ===object(Symfony\Component\Validator\ConstraintViolationList)#322 (1) {
  ["violations":protected]=>
  array(0) {
  }
}
=== 2 ===object(Symfony\Component\Validator\ConstraintViolationList)#289 (1) {
  ["violations":protected]=>
  array(0) {
  }
}

但是我在数据库层面收到了关于唯一约束的错误。那么我该如何让验证层正常工作呢?


在您的第二个示例中,没有任何字段重复。这使得您的问题有点不清楚。 - greg0ire
@gre0ire,test 重复了吗? - Jiew Meng
抱歉,但在此之前,您说了“两个”。那第二个例子有什么问题吗? - greg0ire
你应该在你的示例中加上类似“不会触发任何错误”的内容。 - greg0ire
@greg0ire,在第二个例子中,“test”是重复的。我期望会出现一个验证错误。 - Jiew Meng
$test字段在哪里?我在类中没有看到它。 - Elnur Abdurrakhimov
2个回答

56
在Table注释中,您还可以为多个columns设置索引。

/**
 * @ORM\Entity
 * @ORM\Table(name="ecommerce_products",uniqueConstraints={
 *     @ORM\UniqueConstraint(name="search_idx", columns={"name", "email"})})
 */

或使用YAML格式:

Namespace\Entity\EntityName:
    type: entity
    table: ecommerce_products
    uniqueConstraints:
        uniqueConstraint:
            columns: [name, email]

当字段是与另一个表相关联时怎么办?比如 private $author。你不能在唯一性约束中放置 author 或 author_id。我该怎么解决呢? - Hugo Nogueira
@hugomn 如果它与另一个实体相关联,您可以添加:“myEntity_id”。 对@-yvoyer的一个问题:如何添加自定义消息?(或者如何摆脱错误页面?) - timmz
@hugomn,我认为基于连接列定义约束仍然有效。例如,假设您已经定义了一个属性@JoinColumn(name="customer_id", referencedColumnName="id"),在约束中指定customer_id应该可以工作... 我猜想唯一约束使用的是mysql而不是php的定义。我的意思是唯一约束应该有mysql列的名称“customer_id”,而不是PHP属性“$customer”。 - yvoyer
@Timmz,不确定我是否理解您关于错误页面和自定义消息的问题?它似乎与当前问题无关。 - yvoyer
如果您想检查相关实体,请仍然使用 php 的名称,否则您会得到该错误:字段“country_id”未被 Doctrine 映射,因此无法验证其唯一性。使用以下内容:* @UniqueEntity({"country_id", "product_id"}), 并将其与字段一起使用:* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Country", inversedBy="products") * @ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=true, onDelete="set null") */ protected $country; - MortisQc
这绝对是最好的答案,因为唯一实体应该用于验证(表单等),而Doctrine约束在数据库级别上。两者应该结合使用。 - Romain Bruckert

52

以下逐个检查各个字段:

@UniqueEntity("name")
@UniqueEntity("test")

也就是说,第一个触发器会在出现重复的name值时触发,而第二个触发器则会在出现重复的test值时触发。

如果你想要在同时包含相同组合nametest时使验证失败,可以使用以下内容:

@UniqueEntity({"name", "test"})

对于你想要的内容,第一种方法应该可以工作-除非你在其他地方做错了什么。此外,请尝试清除缓存以确保它不是它的问题。

更新

我建议的是关于应用端验证部分的事情。如果你使用Doctrine生成数据库模式,你需要为每个列提供Doctrine级别的注释-当然,如果你想使它们独立于彼此而唯一:

@Column(type = "string", unique = true)
private $name;

@Column(type = "string", unique = true)
private $test;

这些方法相辅相成,而不是相互排斥。@UniqueEntity 确保重复内容甚至无法到达数据库层,而 @Column 则确保如果重复内容到达了数据库层,它也不会被通过。

1
奇怪的是,对于“UniqueEntity”,它不起作用!只有当两个字段重复时才会触发错误,请查看我的主要问题更新。 - Jiew Meng
如果我想使用@UniqueEntity,我应该把它放在我的代码哪里? - Shawn
1个赞也解释了@UniqueEntity和@Column(unique=true)之间的区别。 - ArchLinuxTux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接