<?php
declare(strict_types=1);
namespace App\Domain\Common\Lock;
use App\Domain\Common\Entity\Utilisateur;
use App\Domain\Common\Repository\UtilisateurRepository;
use App\Domain\Common\Traits\UserAwareTrait;
use App\Domain\Machine\Cache\MachineCacheManager;
use App\Infrastructure\Controller\Common\BaseController;
use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\Adapter\PdoAdapter;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class EditionConsultationLockHandler
{
protected const KEY_PREFIX_BY_RESOURCE = 'edition_consultation_resource';
protected const KEY_PREFIX_BY_USER = 'edition_consultation_user';
protected const KEY_PREFIX_BY_MACHINE = 'edition_consultation_machine';
protected const SESSION_KEY_FORCE_UNLOCK = 'forceUnlock';
use UserAwareTrait;
private PdoAdapter $cache;
private FlashBagInterface $bag;
private UtilisateurRepository $utilisateurRepository;
private MachineCacheManager $machineCacheManager;
private TranslatorInterface $translator;
private SessionInterface $session;
private LoggerInterface $logger;
public function __construct(
PdoAdapter $cache,
UtilisateurRepository $utilisateurRepository,
SessionInterface $session,
MachineCacheManager $machineCacheManager,
TranslatorInterface $translator,
LoggerInterface $logger
)
{
$this->cache = $cache;
$this->utilisateurRepository = $utilisateurRepository;
$this->session = $session;
$this->bag = $session->getFlashBag();
$this->machineCacheManager = $machineCacheManager;
$this->translator = $translator;
$this->logger = $logger;
}
public function acquireOrRefresh(int $consultationId): bool
{
$userId = $this->cache->get($this->getLockKeyByRessource($consultationId), [$this->getUser(), 'getId']);
if ($userId === $this->getUser()->getId()) {
// l'user en question possede le lock
$this->removeLock($consultationId);
// on le refresh
$this->cache->get($this->getLockKeyByRessource($consultationId), fn() => $userId);
$this->cache->get($this->getLockKeyByUser(), fn() => $consultationId);
if ($machine = $this->machineCacheManager->get()) {
$this->logger->info('Save lock engine', [
'consultation_id' => $consultationId,
'lock_engine_id' => $this->getLockKeyByMachine($consultationId),
'engine_id' => $machine->getId(),
'user_id' => $userId
]);
$this->cache->get($this->getLockKeyByMachine($consultationId), fn() => $machine->getId());
}
return true;
}
$userWhoLockId = $this->cache->get($this->getLockKeyByRessource($consultationId), fn() => $userId);
$userWhoLockEntity = $this->utilisateurRepository->find($userWhoLockId);
$this->bag->add(
BaseController::FLASH_ERROR,
$this->translator->trans('consultation.locked', ['%user%' => $userWhoLockEntity], 'flash')
);
return false;
}
public function releaseUserLock(): void
{
// TODO eventuellement stocker un test avant dans un cache plus rapide (session, redis ...)
// on relache le lock de l'user, s'il en a un
$consultationId = $this->cache->get($this->getLockKeyByUser(), fn() => null);
$this->removeLock($consultationId);
}
public function consultationHasMachineLock(int $consultationId): bool
{
return $this->cache->hasItem($this->getLockKeyByMachine($consultationId));
}
public function getMachineIdLock(int $consultationId): ?int
{
if ($this->consultationHasMachineLock($consultationId)) {
/** @var CacheItem $cacheItem */
$cacheItem = $this->cache->getItem($this->getLockKeyByMachine($consultationId));
return $cacheItem->get();
}
return null;
}
protected function removeLock(?int $consultationId = null)
{
if ($consultationId) {
$this->logger->info('Delete lock engine', [
'consultation_id' => $consultationId,
'lock_engine_id' => $this->getLockKeyByMachine($consultationId)
]);
$this->cache->delete($this->getLockKeyByRessource($consultationId));
$this->cache->delete($this->getLockKeyByMachine($consultationId));
}
$cacheConsultationId = $this->cache->get($this->getLockKeyByUser(), fn() => $consultationId);
if ($cacheConsultationId && $cacheConsultationId !== $consultationId) {
$this->cache->delete($this->getLockKeyByRessource($cacheConsultationId));
}
$this->cache->delete($this->getLockKeyByUser());
}
protected function getLockKeyByRessource(int $id): string
{
return self::KEY_PREFIX_BY_RESOURCE . '-' . $id;
}
protected function getLockKeyByUser(): string
{
return self::KEY_PREFIX_BY_USER . '-' . $this->getUser()->getId();
}
protected function getLockKeyByMachine($consultationId): string
{
return self::KEY_PREFIX_BY_MACHINE . '-' . $consultationId;
}
public function getUserLock(int $consultationId): ?Utilisateur
{
$userId = $this->cache->get($this->getLockKeyByRessource($consultationId), [$this->getUser(), 'getId']);
/** @var Utilisateur|null $user */
$user = $this->utilisateurRepository->find($userId);
return $user;
}
public function forceSessionUnlockMode(): void
{
$this->session->set(self::SESSION_KEY_FORCE_UNLOCK, true);
}
public function acquireSessionUnlockMode(): bool
{
if ($this->session->get(self::SESSION_KEY_FORCE_UNLOCK, false) === true) {
$this->session->set(self::SESSION_KEY_FORCE_UNLOCK, false);
return true;
}
return false;
}
}