在进行刷新之前,是否有一种简单的方法可以检查Doctrine 2的重复键?
答案 0 :(得分:46)
你可以这样抓住UniqueConstraintViolationException
:
{{1}}
答案 1 :(得分:19)
我使用此策略在 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;
}
如果您需要获取失败列的名称:
使用带前缀的名称创建表索引,例如。独特_'
* @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;
不完美,但有效......
答案 2 :(得分:7)
如果在插入之前运行SELECT查询不是您想要的,则只能运行flush()并捕获异常。
在Doctrine DBAL 2.3中,您可以通过查看PDO异常错误代码(MySQL为23000,Postgres为23050)安全地检测到唯一约束错误,该错误代码由Doctrine DBALException包装:
try {
$em->flush($user);
} catch (\Doctrine\DBAL\DBALException $e) {
if ($e->getPrevious() && 0 === strpos($e->getPrevious()->getCode(), '23')) {
throw new YourCustomException();
}
}
答案 3 :(得分:4)
我前段时间也遇到过这个问题。主要问题不是数据库特定的异常,而是当抛出PDOException时EntityManager关闭。这意味着您无法确定要刷新的数据会发生什么。但可能它不会保存在数据库中,因为我认为这是在事务中完成的。
所以当我考虑这个问题时,我提出了这个解决方案,但我没有时间实际编写它。
此解决方案的问题在于它可能会对数据库产生大量查询,因此需要进行大量优化。如果您只想在少数地方使用此类产品,我建议您检查可能出现重复的地方。因此,例如,您想要创建实体并保存它:
$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)
}
答案 4 :(得分:3)
如果你正在使用Symfony2,你可以使用UniqueEntity(…)和form->isValid()
来匹配flush()之前的重复项。
我在这里发布这个答案,但它似乎很有价值,因为很多的Doctrine用户也会使用Symfony2。需要明确的是:这使用了Symfony的验证类,该类在底层使用实体存储库进行检查(可配置但默认为findBy
)。
在您的实体上,您可以添加注释:
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @UniqueEntity("email")
*/
class YourEntity {
然后在您的控制器中,在将请求交给表单后,您可以检查您的验证。
$form->handleRequest($request);
if ( ! $form->isValid())
{
if ($email_errors = $form['email']->getErrors())
{
foreach($email_errors as $error) {
// all validation errors related to email
}
}
…
我建议将此与Peter的答案相结合,因为您的数据库架构也应该强制执行唯一性:
/**
* @UniqueEntity('email')
* @Orm\Entity()
* @Orm\Table(name="table_name",
* uniqueConstraints={
* @UniqueConstraint(name="unique_email",columns={"email"})
* })
*/
答案 5 :(得分:2)
如果您只想捕获重复的错误。您不应只检查代码编号
$e->getCode() === '23000'
因为这会捕获其他错误,例如字段'user'不能为空。我的解决方案是检查错误消息,如果它包含文本'重复条目'
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 {
//....
}
}
答案 6 :(得分:2)
在Symfony 2中,它实际上抛出了\ Exception,而不是\ PDOException
try {
// ...
$em->flush();
}
catch( \Exception $e )
{
echo $e->getMessage();
echo $e->getCode(); //shows '0'
### handle ###
}
$ e-&gt; getMessage()回声如下:
使用params [...]执行'INSERT INTO(...)VALUES(?,?,?,?)'时发生异常:
SQLSTATE [23000]:完整性约束违规:1062密钥'PRIMARY'重复输入'...'
答案 7 :(得分:0)
我想特别提到PDOExceptions -
23000错误代码是MySQL可以返回的一系列Integrity Constraint Violations的一揽子代码。
因此,处理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;
}
我意识到这不像问题那么详细,但我发现这在许多情况下非常有用,并且不是特定于Doctrine2。
答案 8 :(得分:0)
最简单的方法应该是:
$product = $entityManager->getRepository("\Api\Product\Entity\Product")->findBy(array('productName' => $data['product_name']));
if(!empty($product)){
// duplicate
}
答案 9 :(得分:0)
我用过这个似乎很有用。它返回特定的MySQL错误号 - 即重复条目的1062 - 准备好您处理自己喜欢的方式。
try
{
$em->flush();
}
catch(\PDOException $e)
{
$code = $e->errorInfo[1];
// Do stuff with error code
echo $code;
}
我在其他几个场景中对此进行了测试,它将返回其他代码,如1146(表不存在)和1054(未知列)。