<?php
declare(strict_types=1);
/*
* CoreShop
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - CoreShop Commercial License (CCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) CoreShop GmbH (https://www.coreshop.org)
* @license https://www.coreshop.org/license GPLv3 and CCL
*
*/
namespace CoreShop\Bundle\OptimisticEntityLockBundle\EventListener;
use CoreShop\Bundle\OptimisticEntityLockBundle\Exception\OptimisticLockException;
use CoreShop\Bundle\OptimisticEntityLockBundle\Manager\EntityLockManager;
use CoreShop\Bundle\OptimisticEntityLockBundle\Model\OptimisticLockedInterface;
use Doctrine\DBAL\Connection;
use Pimcore\Event\DataObjectEvents;
use Pimcore\Event\Model\DataObjectEvent;
use Pimcore\Model\DataObject\Concrete;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LockListener implements EventSubscriberInterface
{
public function __construct(
protected EntityLockManager $lockManager,
protected Connection $connection,
) {
}
public static function getSubscribedEvents(): array
{
return [
DataObjectEvents::PRE_ADD => 'preAddLock',
DataObjectEvents::PRE_UPDATE => 'checkLock',
DataObjectEvents::POST_UPDATE => 'postUpdateLock',
];
}
public function preAddLock(DataObjectEvent $dataObjectEvent): void
{
$object = $dataObjectEvent->getObject();
if (!$object instanceof OptimisticLockedInterface) {
return;
}
if (!$object instanceof Concrete) {
return;
}
/**
* @var Concrete $object
* @var OptimisticLockedInterface $object
*/
$object->setOptimisticLockVersion(1);
}
public function postUpdateLock(DataObjectEvent $dataObjectEvent): void
{
$object = $dataObjectEvent->getObject();
if (!$object instanceof OptimisticLockedInterface) {
return;
}
if (!$object instanceof Concrete) {
return;
}
/**
* @var Concrete $object
*/
$this->lockManager->updateLock($object);
}
public function checkLock(DataObjectEvent $dataObjectEvent): void
{
$object = $dataObjectEvent->getObject();
if (!$object instanceof OptimisticLockedInterface) {
return;
}
if (!$object instanceof Concrete) {
return;
}
$this->ensureVersionMatch($object);
/**
* @var Concrete $object
* @var OptimisticLockedInterface $object
*/
$object->setOptimisticLockVersion(($object->getOptimisticLockVersion() ?? 1) + 1);
}
private function ensureVersionMatch(Concrete $object): void
{
if (!$object instanceof OptimisticLockedInterface) {
return;
}
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->from('object_store_' . $object->getClassId())
->select('optimisticLockVersion')
->where('oo_id = :id')
->setMaxResults(1)
->setParameter('id', $object->getId())
;
$currentVersion = (int) $this->connection->fetchOne($queryBuilder->getSQL(), $queryBuilder->getParameters());
if ($currentVersion === $object->getOptimisticLockVersion()) {
return;
}
throw OptimisticLockException::lockFailedVersionMismatch(
$object,
$object->getOptimisticLockVersion(),
$currentVersion,
);
}
}