SQLite 中的 Doctrine UniqueConstraintViolationException 不是 caught/thrown

Doctrine UniqueConstraintViolationException not caught/thrown in SQLite

我有一个包含图像数据的 SQLite table。一列是 position,它是一个文本字段,其中包含该图像应在网站上显示的位置。该字段具有唯一约束。这是 table 定义:

CREATE TABLE images (id INTEGER PRIMARY KEY, title TEXT UNIQUE NOT NULL, position TEXT UNIQUE
NOT NULL, caption TEXT, filename TEXT, album INTEGER NOT NULL, publicationDate TEXT, updated
TEXT, createdBy INTEGER
 NOT NULL, updatedBy INTEGER NOT NULL, FOREIGN KEY (createdBy) REFERENCES users(id), FOREIGN KEY
(album) REFERENCES albums(id), FOREIGN KEY (updatedBy) REFERENCES users(id));

并且,来自实体的相关位:

/**
* @ORM\Entity
* @ORM\Table(name="images",indexes={
*       @Index(name="publication_date", columns={"publicationDate"})},
*       uniqueConstraints={@UniqueConstraint(name="unique_position", columns={"position", "fileName"})}
*       )
*/
class Image
{
    /**
    * @ORM\Id
    * @ORM\Column(type="integer")
    * @ORM\GeneratedValue
    **/
    protected $id;

    /**
    * @ORM\Column(type="string", unique=true)
    */
    protected $position;

我可以成功写入 table(Doctrine persist,然后 flush),除非 position 列上存在唯一约束冲突。当存在唯一约束冲突时,flush() 静静地失败(不写入数据库)并且应用程序继续执行。这是相关代码 - 在设置和持久化实体之后:

        try {
            $this->entityManager->flush();
            $message['type'] = 'alert-info';
            $message['content'] = "$title added succesfully";
            return $message;
        } catch (UniqueConstraintViolationException $e) {
            $message['type'] = 'alert-danger';
            $message['content'] = "$title could not be added " . $e;
            return $this->create($message, $formVars);
        } catch (Exception $e) {
            $message['type'] = 'alert-danger';
            $message['content'] = "$title could not be added " . $e;
            return $this->create($message, $formVars);
        }

为什么我没有捕捉到异常?或者,它根本就没有被抛出?

发布我上面提供的提示和代码实现只是 运行 2 次以查看结果,这里是控制器:

<?php

namespace App\Controller;

use App\Entity\TestEntity;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class TestController extends AbstractController
{
    /**
     * @Route("/", name="test")
     */
    public function test(Request $request, ValidatorInterface $validator)
    {
        $newEntity = new TestEntity();
        $newEntity->setPosition('test');

        // In addition $form->isValid() does this for you and displays form error specified in entity
        if(($result = $validator->validate($newEntity))->count() === 0) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($newEntity);
            $em->flush();
            dump('Entity persisted');
        } else {
            dump($result); // <- array of violations
            dump('Sorry entity with this position exists');
        }

        die;
    }
}

这是您的实体:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * @ORM\Table(name="test_entity")
 * @ORM\Entity(repositoryClass="App\Repository\TestEntityRepository")
 * 
 * @UniqueEntity(
 *  fields={"position"},
 *  errorPath="position",
 *  message="Duplicate of position {{ value }}."
 * )
 */
class TestEntity
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    public function getId()
    {
        return $this->id;
    }

    /**
     * @ORM\Column(name="position", type="string", length=190, unique=true) // key length limit for Mysql
     */
    private $position;

    public function getPosition()
    {
        return $this->position;
    }

    public function setPosition($position)
    {
        $this->position = $position;
        return $this;
    }
}

出于好奇,我测试了你的第一个想法,它似乎对我有用

<?php

namespace App\Controller;

use App\Entity\TestEntity;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class TestController extends AbstractController
{
    /**
     * @Route("/", name="test")
     */
    public function test(Request $request, ValidatorInterface $validator)
    {
        $newEntity = new TestEntity();
        $newEntity->setPosition('test');

        try {
            $em = $this->getDoctrine()->getManager();
            $em->persist($newEntity);
            $em->flush();
        } catch(\Doctrine\DBAL\Exception\UniqueConstraintViolationException $e) {
            dump('First catch');
            dump($e);
        }
        catch(\Exception $e) {
            dump('Second catch');
            dump($e);
        }

        die;
    }
}

它导致了 'First catch' 的转储,但这可能与我使用的 mysql 而不是 SQlite 有关,这里还有对 Symfony Validation Doc 的引用以防万一你想看看
EDIT2
简单验证器 class

class SomeSortOfValidator
{
    public function exists($entity, $fieldName, $fieldValue)
    {
        // Lets suppose you have entity manager autowired.
        $record = $this->em->getRepository($entity)->findOneBy([
            $fieldName => $fieldValue
        ]);
        return $record ? true : false;
    }
}