Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

Unique Constraint - DBAL Exception e ORM Exception

Olá, mais uma vez estou por aqui hehe

Eu estou fazendo alguns projetos para fixação do conteúdo. Finalizei a primeira carreira de PHP, ja iniciei a segunda, além de que ja coloquei na lista os cursos de Symfony e o plano de estudos do professor Vinícius.

Bem, em um dos casos que estou fazendo de estudo, simulei algo que preciso fazer no meu dia a dia: dar insert em uma tabela com unique constraint. Eu espero que ocorra erro por conta dessa constraint, pois não quero verificar se o dado ja existe ou não no banco antes de inserir, apenas quero realizar a inserção, se ele existir, que me ocorra o erro (a constraint é sobre uma coluna de hash indexada).

A minha situação pode ser exemplificada assim:

foreach($clientes as $cliente){
    try{
        $this->entityManager->persist($cliente);
        $this->entityManager->flush();
    }catch(Exception $e){
        //salva o erro no banco
    }
}

Quando ocorre a primeira exception por conta da unique constraint ela tem a segundo mensagem e stacktrace (respectivamente):

An exception occurred while executing 'INSERT...' SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '(...)' for key 'column_name_unique'
dbal\lib\Doctrine\DBAL\DBALException.php(182): Doctrine\DBAL\Driver\AbstractMySQLDriver->convertException('An exception oc...', Object(Doctrine\DBAL\Driver\PDO\Exception))

Todavia, após ocorrer essa DBAL Exception, não é mais possível rodar a aplicação, visto que é da uma mensagem na tela dizendo algo como: The EntityManager is closed.

Fiz algumas pesquisas na internet e na documentação do Doctrine e modifiquei meu código, para que a cada tentativa de salvar a entidade eu verifique se a EntityManager está fechada. Caso ela esteja, então ela é recriada.

//Código da function que reabre a EntityManager
private function reopenEntityManager()
{
        if (!$this->entityManager->isOpen()) {
            $this->entityManager = $this->entityManager->create(
                $this->entityManager->getConnection(),
                $this->entityManager->getConfiguration()
            );
        }
}
//Código para persistir a entidade
foreach($clientes as $cliente){
    try{
        $this->reopenEntityManager();
        $this->entityManager->persist($cliente);
        $this->entityManager->flush();
    }catch(Exception $e){
        //salva o erro no banco
    }
}

Assim, o erro que aparecia de a EntityManager estar fechada não ocorre mais e o foreach dá continuidade.

Todavia, quando a unique constraint é novamente violada, a exception salva não é a mesma que a relatava anteriormente, mas essa:

Undefined index: 000000005e09d9dc0000000049c307c7

Mesmo os registros que deveriam ser inseridos após o primeiro erro de unique constraint, não o são. Todas as tentativas passam a ter essa mensagem de exception, variando apenas o número do index, obviamente.

Além disso, a exception que deveria ocorrer era a mesma que ocorreu na primeira, quando fosse o caso de violação da unique constraint.

A impressão que eu tenho é a de que o Doctrine não está encontrando o objetos internos do objeto pai que estou salvando. Entretanto, ao executar os comandos de validação e para ver as informações, não aparece nenhum erro.

Observação.: Estou usando o Laravel com Doctrine. Mas coloquei essa dúvida aqui no tópico de doctrine pq acredito ser uma questão específica do doctrine, não do laravel. Não parece, ao menos, ter nenhuma relação com o Laravel.

1 resposta
solução!

Resolvi

Um dos meus mapeamentos internos estava sem um cascade={"persist"} no lado do @ManyToOne (isso já era um nó filho de um filho).

Aí, como uma nova instância do EntityManager não estava "vendo" essa entidade, ocorria que ele não encontrava a entidade, gerando o erro: Undefined index: 000000005e09d9dc0000000049c307c7

Como havia uma falha do mapeamento para persist em cascata nessa entidade, o novo EntityManager não conseguia chegar até ele, pois ele não estava "observando" o objeto.

Uma das ultimas linhas do stack de erro falava sobre a entidade que estava com problema e eu não tinha me atentado a isso.

Vou estudar um pouco mais sobre os mapeamentos em cascata para me atentar a que não ocorra mais esse tipo de situação.