Doctrine 使用原则2检查重复密钥
在进行刷新之前,有没有一种简单的方法可以用Doctrine 2检查重复的密钥?我以前也遇到过这个问题。主要问题不是特定于数据库的异常,而是当引发PDOException时EntityManager关闭的事实。这意味着您无法确定要刷新的数据会发生什么情况。但它可能不会保存在数据库中,因为我认为这是在事务中完成的 所以当我思考这个问题的时候,我想出了这个解决方案,但我还没有时间去写它Doctrine 使用原则2检查重复密钥,doctrine,doctrine-orm,Doctrine,Doctrine Orm,在进行刷新之前,有没有一种简单的方法可以用Doctrine 2检查重复的密钥?我以前也遇到过这个问题。主要问题不是特定于数据库的异常,而是当引发PDOException时EntityManager关闭的事实。这意味着您无法确定要刷新的数据会发生什么情况。但它可能不会保存在数据库中,因为我认为这是在事务中完成的 所以当我思考这个问题的时候,我想出了这个解决方案,但我还没有时间去写它 可以使用,特别是onFlush事件来完成。此事件在数据发送到数据库之前调用(在计算变更集之后-因此您已经知道哪些实体
$user = new User('login');
$presentUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('login' => 'login'));
if (count($presentUsers)>0) {
// this login is already taken (throw exception)
}
我使用此策略检查flush()之后的唯一约束,可能不是您想要的,但可能会帮助其他人
调用flush()时,如果唯一约束失败,将抛出PDOException,代码为23000
try {
// ...
$em->flush();
}
catch( \PDOException $e )
{
if( $e->getCode() === '23000' )
{
echo $e->getMessage();
// Will output an SQLSTATE[23000] message, similar to:
// Integrity constraint violation: 1062 Duplicate entry 'x'
// ... for key 'UNIQ_BB4A8E30E7927C74'
}
else throw $e;
}
如果需要获取失败列的名称: 创建带有前缀名称的表索引,例如“unique”
* @Entity
* @Table(name="table_name",
* uniqueConstraints={
* @UniqueConstraint(name="unique_name",columns={"name"}),
* @UniqueConstraint(name="unique_email",columns={"email"})
* })
不要在@Column定义中将列指定为唯一的 这似乎用随机名称覆盖索引名称
**ie.** Do not have 'unique=true' in your @Column definition
重新生成表后(可能需要删除并重新生成),您应该能够从异常消息中提取列名
// ...
if( $e->getCode() === '23000' )
{
if( \preg_match( "%key 'unique_(?P<key>.+)'%", $e->getMessage(), $match ) )
{
echo 'Unique constraint failed for key "' . $match[ 'key' ] . '"';
}
else throw $e;
}
else throw $e;
/。。。
如果($e->getCode()=='23000')
{
if(\preg_match(“%key”unique_(?P.+)“%”,$e->getMessage(),$match))
{
回显键“.”的唯一约束失败。$match['key'].'”;
}
否则抛出$e;
}
否则抛出$e;
虽然不完美,但它可以工作…我想补充一点,特别是关于PDO例外-- 23000错误代码是MySQL可以返回的一系列完整性约束冲突的总括代码 因此,对于某些用例,处理23000错误代码不够具体 例如,您可能希望对重复记录违规做出与丢失外键违规不同的反应 下面是一个如何处理此问题的示例:
try {
$pdo -> executeDoomedToFailQuery();
} catch(\PDOException $e) {
// log the actual exception here
$code = PDOCode::get($e);
// Decide what to do next based on meaningful MySQL code
}
// ... The PDOCode::get function
public static function get(\PDOException $e) {
$message = $e -> getMessage();
$matches = array();
$code = preg_match('/ (\d\d\d\d) / ', $message, $matches);
return $code;
}
我意识到这并不像问题所问的那么详细,但我发现这在许多情况下非常有用,并且不是特定于教义的。最简单的方法应该是:
$product = $entityManager->getRepository("\Api\Product\Entity\Product")->findBy(array('productName' => $data['product_name']));
if(!empty($product)){
// duplicate
}
我用过这个,它似乎很管用。它返回特定的MySQL错误号-即,对于重复条目返回1062-准备好让您以您喜欢的方式处理
try
{
$em->flush();
}
catch(\PDOException $e)
{
$code = $e->errorInfo[1];
// Do stuff with error code
echo $code;
}
我用一些其他的场景测试了它,它也会返回其他代码,比如1146(表不存在)和1054(未知列)。如果您只想捕获重复的错误。你不应该只检查代码
$e->getCode() === '23000'
因为这将捕获其他错误,例如字段“user”不能为空。我的解决方案是检查错误消息,如果它包含文本“Duplicate entry”
try {
$em->flush();
} catch (\Doctrine\DBAL\DBALException $e) {
if (is_int(strpos($e->getPrevious()->getMessage(), 'Duplicate entry'))) {
$error = 'The name of the site must be a unique name!';
} else {
//....
}
}
在Symfony 2中,它实际上抛出了一个\异常,而不是一个\异常
try {
// ...
$em->flush();
}
catch( \Exception $e )
{
echo $e->getMessage();
echo $e->getCode(); //shows '0'
### handle ###
}
$e->getMessage()的回声如下:
使用参数[…]执行“插入到(…)值(?,,,?)”时发生异常:
SQLSTATE[23000]:完整性约束冲突:1062键“PRIMARY”的重复条目“…”如果您使用的是Symfony2,则可以与
form->isValid()
一起使用,以在flush()之前捕获重复项
我在这里发布这个答案,但它似乎很有价值,因为许多条令用户也将使用Symfony2。需要明确的是:这使用了Symfony的validations类,该类在后台使用实体存储库进行检查(可配置,但默认为findBy
)
在实体上,可以添加注释:
使用Symfony\Bridge\Doctrine\Validator\Constraints\uniquentity;
/**
*@uniquentity(“电子邮件”)
*/
将您的实体分类{
然后在控制器中,将请求传递给表单后,您可以检查验证
$form->handleRequest($request);
如果(!$form->isValid())
{
如果($email_errors=$form['email']->getErrors())
{
foreach($email\u errors作为$error){
//与电子邮件相关的所有验证错误
}
}
…
我建议将这一点与Peter的答案结合起来,因为您的数据库模式也应该强制唯一性:
/**
*@uniquentity('email')
*@Orm\Entity()
*@Orm\Table(name=“Table\u name”,
*唯一约束={
*@UniqueConstraint(name=“unique_email”,columns={“email”})
* })
*/
您可以捕获以下内容:
我真的没有答案,但我想知道刷新前的检查与刷新和处理错误有什么不同(假设存在重复的密钥)。刷新时会引发特定于数据库的异常。此处提供的大多数解决方案都没有考虑到这样一个事实,即您无法事先检查重复项,因为这不是一个原子操作,因此,如果其他线程插入,您仍然可以有重复的值
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
// ...
try {
// ...
$em->flush();
}
catch (UniqueConstraintViolationException $e) {
// ....
}