50 votos

La comprobación de duplicados de las llaves de la Doctrina 2

Hay una manera fácil de comprobar por duplicado de las llaves de la Doctrina 2 antes de hacer un flush?

20voto

Peter Johnson Puntos 1156

Puedo usar esta estrategia para comprobar las restricciones unique después de flush(), puede no ser lo que quieres, pero podría ayudar a alguien más.


Cuando usted llame a flush(), si un único restringir falla, un PDOException es lanzado con el código de 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;
}

Si usted necesita para obtener el nombre de la falla de la columna:

Crear tabla de índices con el prefijo de los nombres de, por ejemplo,. 'unique_'

 * @Entity
 * @Table(name="table_name",
 *      uniqueConstraints={
 *          @UniqueConstraint(name="unique_name",columns={"name"}),
 *          @UniqueConstraint(name="unique_email",columns={"email"})
 *      })

NO especifica las columnas como único en el @definición de Columna

Esto parece reemplazar el nombre de índice con una muestra aleatoria de una...

 **ie.** Do not have 'unique=true' in your @Column definition

Después de regenerar su mesa (puede que tenga que caer y reconstrucción), usted debería ser capaz de extraer el nombre de la columna desde el mensaje de excepción.

// ...
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;

No es perfecto, pero funciona...

4voto

Vašek Purchart Puntos 21

Me he encontrado con este problema hace algún tiempo, demasiado. El principal problema no está en la base de datos de excepciones específicas, pero el hecho de que, cuando un PDOException se produce el EntityManager es cerrado. Eso significa que usted no puede estar seguro de lo que va a suceder con los datos que quería de color. Pero probablemente sería que no se guardan en la base de datos, porque creo que esto se hace dentro de una transacción.

Así que cuando yo estaba thinkg acerca de este problema que se me ocurrió con esta solución, pero yo no tengo tiempo para escribir todavía.

  1. Se puede hacer uso de detectores de eventos, especialmente en la onFlush evento. Este evento se invoca antes de que los datos se envían a la base de datos (después de los cambios, se calculan - así que ya sabes las entidades que han cambiado).
  2. En este caso el oyente tendría que buscar todos los cambió entidades para sus llaves (para primaria sería buscar en la clase de metadatos para @Id).
  3. Entonces usted tendría que usar un método de búsqueda con los criterios de sus claves. Si usted quiere encontrar un resultado, usted tiene la oportunidad de lanzar su propia excepción, que no se cerrará el EntityManager y que son capaces de atrapar en su modelo y hacer algunas correcciones a los datos antes de intentar la descarga de nuevo.

El problema con esta solución sería que podría generar una gran cantidad de consultas a la base de datos, por lo que se requeriría de un buen montón de optimalization. Si desea utilizar tal cosa sólo en pocos lugares me recomiendan hacer la comprobación en el lugar donde el duplicado pueda surgir. Así, por ejemplo, cuando desea crear una entidad y guardarlo:

$user = new User('login');
$presentUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('login' => 'login'));
if (count($presentUsers)>0) {
    // this login is alreday taken (throw exception)
}

2voto

Aris Puntos 1407

En Symfony 2, que en realidad se produce una \Excepción, no una \PDOException

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

}

$e->getMessage() echos algo como la siguiente:

Se produjo una excepción durante la ejecución de 'INSERT INTO (...) los VALORES (?, ?, ?, ?)' con params [...]:

SQLSTATE[23000]: la Integridad de la violación de la restricción: 1062 entrada Duplicada '...' clave 'PRINCIPAL'

0voto

Peter M. Elias Puntos 96

Me gustaría añadir a esta específicamente con respecto a PDOExceptions--

El 23000 código de error es la manta de código para una familia de Restricción de Integridad de las Violaciones que MySQL se puede devolver.

Por lo tanto, el manejo de la 23000 código de error no es suficientemente específica para algunos casos de uso.

Por ejemplo, usted desea, puede reaccionar de manera diferente a un registro duplicado violación de una falta de infracción de clave externa.

Aquí está un ejemplo de cómo lidiar con este:

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

Me doy cuenta de que esto no es tan detallada como la cuestión era preguntar pero me parece que es muy útil en muchos casos y no es Doctrine2 específicos.

0voto

Abhilash V Puntos 357

La manera más fácil debería ser este :

$product    = $entityManager->getRepository("\Api\Product\Entity\Product")->findBy(array('productName' => $data['product_name']));
if(!empty($product)){
 // duplicate
}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X