代码之家  ›  专栏  ›  技术社区  ›  tom

使用条令2检查重复钥匙

  •  47
  • tom  · 技术社区  · 14 年前

    10 回复  |  直到 13 年前
        1
  •  51
  •   Darryl Hein IrishChieftain    7 年前

    你可以抓住 UniqueConstraintViolationException 像这样的:

    use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
    
    // ...
    
    try {
       // ...
       $em->flush();
    }
    catch (UniqueConstraintViolationException $e) {
        // ....
    }
    
        2
  •  20
  •   Peter Johnson    13 年前

    我使用此策略在 ,可能不是您想要的,但可能会帮助其他人。


    当你打电话 刷新() ,如果唯一约束失败,则 与代码一起抛出 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;
    

    不是完美的,但它是有效的。。。

        3
  •  8
  •   E Ciotti    5 年前

    如果您不希望在插入之前运行SELECT查询,则只能运行flush()并捕获异常。

    在Doctrine DBAL 2.3中,您可以通过查看Doctrine DBALException包装的PDO异常错误代码(对于MySQL为23000,对于Postgres为23050)来安全地检测唯一的约束错误:

            try {
                $em->flush($user);
            } catch (\Doctrine\DBAL\DBALException $e) {
                if ($e->getPrevious() &&  0 === strpos($e->getPrevious()->getCode(), '23')) {
                    throw new YourCustomException();
                }
            }
    
        4
  •  4
  •   oradwell    9 年前

    不久前我也遇到过这个问题。主要问题不是特定于数据库的异常,而是当抛出PDOException时,EntityManager将关闭。这意味着您无法确定要刷新的数据将发生什么。但可能不会保存在数据库中,因为我认为这是在事务中完成的。

    1. 可以用 event listeners ,尤其是onFlush事件。在将数据发送到数据库之前(在计算变更集之后——因此您已经知道更改了哪些实体)调用此事件。
    2. 在此事件侦听器中,您必须浏览所有已更改的实体以查找它们的键(对于primary,它将在类元数据中查找@Id)。
    3. 然后,您必须使用一个find方法来满足密钥的条件。 如果您能找到一个结果,您就有机会抛出自己的异常,它不会关闭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)
    }
    
        5
  •  3
  •   Mark Fox    10 年前

    UniqueEntity(…) 具有 form->isValid() 在flush()之前捕获重复项。

    我把这个答案贴在这里,但似乎很有价值,因为 原则的用户也将使用Symfony2。需要说明的是:它使用Symfony的validations类,该类在hood下使用实体存储库进行检查(是可配置的,但默认为 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"})
     * })
     */
    
        6
  •  2
  •   Patric Robert Gutersohn    10 年前

    如果你只想捕捉重复的错误。你不应该只检查密码

    $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 {
                            //....
                        }
                    }
    
        7
  •  2
  •   Aris    10 年前

    try {
        // ...
        $em->flush();
    }
    catch( \Exception $e )
    {
       echo $e->getMessage();
       echo  $e->getCode(); //shows '0'
       ### handle ###
    
    }
    

    $e->getMessage()回声如下:

    执行“INSERT INTO(…)VALUES(?时发生异常?, ?, ?, ?)' 带参数[…]:

    SQLSTATE[23000]:完整性约束冲突:键“PRIMARY”的1062重复项“…”

        8
  •  0
  •   Peter M. Elias    12 年前

    我想特别就PDOExceptions补充一点--

    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;
    }
    

    我意识到这并不像问题所问的那么详细,但我发现这在很多情况下非常有用,而且不是特定的教义。

        9
  •  0
  •   abhilashv    11 年前

    最简单的方法应该是:

    $product    = $entityManager->getRepository("\Api\Product\Entity\Product")->findBy(array('productName' => $data['product_name']));
    if(!empty($product)){
     // duplicate
    }
    
        10
  •  0
  •   Jeff S.    11 年前

    我用过这个,看起来很管用。它将返回特定的MySQL错误号(即,重复条目为1062),以便您可以随意处理。

    try
    {
        $em->flush();
    }
    catch(\PDOException $e)
    {
        $code = $e->errorInfo[1];
        // Do stuff with error code
        echo $code;
    }
    

    我用其他一些场景测试了这个,它也会返回其他代码,比如1146(表不存在)和1054(未知列)。