Атрибут Version
Атрибут Version вешается на свойство сущности
и указывает Doctrine использовать это поле для
оптимистичной блокировки. При каждом обновлении
записи значение поля автоматически увеличивается.
Если два пользователя попытаются сохранить изменения
одновременно, Doctrine выбросит исключение
OptimisticLockException, предотвращая потерю данных.
Синтаксис
#[Version]
Пример
Давайте добавим поле версии в сущность Article:
<?php
namespace AppEntity;
use DoctrineORMMapping as ORM;
#[ORMEntity]
class Article
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn(type: 'integer')]
private ?int $id = null;
#[ORMColumn(type: 'string', length: 255)]
private string $title;
#[ORMVersion]
#[ORMColumn(type: 'integer')]
private int $version = 1;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getVersion(): int
{
return $this->version;
}
}
?>
Пример
Давайте посмотрим, как работает оптимистичная блокировка при обновлении:
<?php
namespace AppController;
use AppEntityArticle;
use DoctrineORMEntityManagerInterface;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAttributeRoute;
class ArticleController extends AbstractController
{
#[Route('/article/update/{id}', name: 'article_update')]
public function update(int $id, EntityManagerInterface $em): Response
{
$article = $em->getRepository(Article::class)->find($id);
if (!$article) {
return new Response('Article not found', 404);
}
$article->setTitle('updated title');
$em->flush();
return new Response('Version: ' . $article->getVersion());
}
}
?>
Результат выполнения кода для первой версии:
Version: 2
Пример
Давайте обработаем исключение при конфликте версий:
<?php
namespace AppController;
use AppEntityArticle;
use DoctrineORMEntityManagerInterface;
use DoctrineORMOptimisticLockException;
use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAttributeRoute;
class ArticleController extends AbstractController
{
#[Route('/article/edit/{id}', name: 'article_edit')]
public function edit(int $id, EntityManagerInterface $em): Response
{
$article = $em->getRepository(Article::class)->find($id);
if (!$article) {
return new Response('Article not found', 404);
}
try {
$article->setTitle('new title');
$em->flush();
return new Response('Saved successfully');
} catch (OptimisticLockException $e) {
return new Response('Conflict detected. Please reload.', 409);
}
}
}
?>
Результат выполнения кода при конфликте:
Conflict detected. Please reload.
Смотрите также
-
атрибут
Entity,
который помечает класс как сущность Doctrine -
атрибут
Column,
который задаёт параметры колонки в базе данных -
атрибут
Id,
который помечает свойство как первичный ключ -
атрибут
GeneratedValue,
который указывает стратегию генерации идентификатора