【问题标题】:Doctrine UniqueConstraintViolationException not caught/thrown in SQLite在 SQLite 中未捕获/抛出 Doctrine UniqueConstraintViolationException
【发布时间】:2020-01-15 13:17:13
【问题描述】:

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

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;

除非position 列存在唯一约束违规,否则我可以成功写入表(Doctrine 持久化,然后刷新)。当存在唯一约束违规时,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);
        }

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

【问题讨论】:

  • 由于应用程序继续执行,所以根本不会抛出异常
  • 如果它抛出异常,您是否尝试过不尝试捕获?
  • @B0re 是的。我很确定不会抛出异常。 Rain的上述评论似乎同意
  • 老实说,我会采取不同的方法,并在“位置”字段上使用 @UniqueEntity symfony 约束,使用 ValidatorComponent 验证实体,然后根据结果刷新或创建捕获警报跨度>

标签: php sqlite exception doctrine-orm


【解决方案1】:

将上面提供的提示与代码实现一起发布,只需运行 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 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;
    }
}

【讨论】:

  • 您能告诉我有关如何构建验证器对象的信息吗?您的示例控制器有一个参数 - ValidatorInterface $validator
  • 这是由 symfony 自动装配的,它由 symfony/validator bundle 提供
  • 啊。我没有使用 Symfony - 我必须看看我是否可以创建一个实例并从我的容器中提供它。
  • 仅使用教义实现此类验证器的简单方法是创建带有实体、字段名和字段值的函数,我为您编辑了答案
猜你喜欢
  • 2023-02-06
  • 2020-06-26
  • 2020-11-16
  • 1970-01-01
  • 2015-10-05
  • 1970-01-01
  • 2012-12-09
  • 2013-08-12
  • 1970-01-01
相关资源
最近更新 更多