Doctrine UniqueConstraintViolationException не перехватывается / генерируется в SQLite - PullRequest
0 голосов
/ 15 января 2020

У меня есть таблица 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;

Я могу успешно записать в таблицу (Doctrine сохранится, затем грипп sh) если не существует уникального нарушения ограничения для столбца position. При уникальном нарушении ограничения flu sh () молча завершается сбоем (без записи в БД), и приложение продолжает выполняться. Вот соответствующий код - после настройки и сохранения сущности:

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

Почему я не улавливаю исключение? Или это вообще не брошено?

1 Ответ

1 голос
/ 15 января 2020

Отправляя мой совет, приведенный выше, с реализацией кода, просто запустите его 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 Проверка c на случай, если вы хотите проверить это
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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...