DigiComp.Sequence/Classes/Service/SequenceGenerator.php

138 lines
3.8 KiB
PHP
Raw Permalink Normal View History

2014-04-07 15:25:16 +02:00
<?php
declare(strict_types=1);
2014-04-07 15:25:16 +02:00
namespace DigiComp\Sequence\Service;
2021-09-22 11:34:34 +02:00
use DigiComp\Sequence\Domain\Model\SequenceEntry;
use DigiComp\Sequence\Service\Exception\InvalidSourceException;
2021-09-21 10:39:03 +02:00
use Doctrine\DBAL\Driver\Exception as DoctrineDBALDriverException;
2021-09-20 18:12:51 +02:00
use Doctrine\DBAL\Exception as DoctrineDBALException;
use Doctrine\ORM\EntityManagerInterface;
2017-03-13 16:59:04 +01:00
use Neos\Flow\Annotations as Flow;
use Neos\Utility\TypeHandling;
use Psr\Log\LoggerInterface;
2014-04-07 15:25:16 +02:00
/**
2021-09-22 11:34:34 +02:00
* A sequence number generator working for transactional databases.
2014-04-07 15:25:16 +02:00
*
2016-06-24 19:40:43 +02:00
* Thoughts: We could make the step-range configurable, and if > 1 we could return new keys immediately for this
* request, as we "reserved" the space between.
2014-04-16 10:24:05 +02:00
*
2014-04-07 15:25:16 +02:00
* @Flow\Scope("singleton")
*/
2016-06-24 19:40:43 +02:00
class SequenceGenerator
{
/**
* @Flow\Inject
* @var EntityManagerInterface
2016-06-24 19:40:43 +02:00
*/
protected $entityManager;
2014-04-07 15:25:16 +02:00
2016-06-24 19:40:43 +02:00
/**
* @Flow\Inject
* @var LoggerInterface
2016-06-24 19:40:43 +02:00
*/
2021-09-17 11:22:44 +02:00
protected $logger;
2014-04-07 15:25:16 +02:00
2016-06-24 19:40:43 +02:00
/**
2021-09-22 11:34:34 +02:00
* @param string|object $source
2016-06-24 19:40:43 +02:00
* @return int
2021-09-21 10:39:03 +02:00
* @throws DoctrineDBALDriverException
2021-09-20 18:12:51 +02:00
* @throws DoctrineDBALException
2021-09-22 11:34:34 +02:00
* @throws InvalidSourceException
2016-06-24 19:40:43 +02:00
*/
2021-09-22 11:34:34 +02:00
public function getNextNumberFor($source): int
2016-06-24 19:40:43 +02:00
{
2021-09-22 11:34:34 +02:00
$type = $this->inferTypeFromSource($source);
$number = $this->getLastNumberFor($type);
2014-04-07 15:25:16 +02:00
2021-09-22 11:34:34 +02:00
// TODO: Check for maximal tries, or similar?
// TODO: Let increment be configurable per type?
2016-06-24 19:40:43 +02:00
do {
2021-09-22 11:34:34 +02:00
$number++;
} while (!$this->insertFor($type, $number));
2017-03-13 16:59:04 +01:00
2021-09-22 11:34:34 +02:00
return $number;
2016-06-24 19:40:43 +02:00
}
2014-04-07 15:25:16 +02:00
2017-03-13 16:59:04 +01:00
/**
* @param string $type
2021-09-22 11:34:34 +02:00
* @param int $number
2017-03-13 16:59:04 +01:00
* @return bool
*/
2021-09-22 11:34:34 +02:00
protected function insertFor(string $type, int $number): bool
2016-06-24 19:40:43 +02:00
{
try {
2021-09-22 11:34:34 +02:00
$this->entityManager->getConnection()->insert(
$this->entityManager->getClassMetadata(SequenceEntry::class)->getTableName(),
2022-04-20 17:36:31 +02:00
['type' => $type, 'number' => $number]
2016-06-24 19:40:43 +02:00
);
2021-09-22 11:34:34 +02:00
2016-06-24 19:40:43 +02:00
return true;
2021-09-22 11:34:34 +02:00
} catch (\PDOException $exception) {
} catch (DoctrineDBALException $exception) {
if (!$exception->getPrevious() instanceof \PDOException) {
$this->logger->critical('Exception occurred: ' . $exception->getMessage());
2016-06-24 19:40:43 +02:00
}
2021-09-22 11:34:34 +02:00
} catch (\Exception $exception) {
$this->logger->critical('Exception occurred: ' . $exception->getMessage());
2016-06-24 19:40:43 +02:00
}
2017-03-13 16:59:04 +01:00
2016-06-24 19:40:43 +02:00
return false;
}
2014-04-07 15:25:16 +02:00
2017-03-13 16:59:04 +01:00
/**
2021-09-22 11:34:34 +02:00
* @param string|object $source
* @param int $number
2017-03-13 16:59:04 +01:00
* @return bool
2021-09-22 11:34:34 +02:00
* @throws DoctrineDBALDriverException
* @throws DoctrineDBALException
* @throws InvalidSourceException
2017-03-13 16:59:04 +01:00
*/
2021-09-22 11:34:34 +02:00
public function setLastNumberFor($source, int $number): bool
2016-06-24 19:40:43 +02:00
{
2021-09-22 11:34:34 +02:00
$type = $this->inferTypeFromSource($source);
2017-03-13 16:59:04 +01:00
2021-09-22 11:34:34 +02:00
if ($this->getLastNumberFor($type) >= $number) {
return false;
}
return $this->insertFor($type, $number);
2016-06-24 19:40:43 +02:00
}
2014-04-07 15:25:16 +02:00
2016-06-24 19:40:43 +02:00
/**
2021-09-22 11:34:34 +02:00
* @param string|object $source
2021-09-21 10:39:03 +02:00
* @throws DoctrineDBALDriverException
2021-09-20 18:12:51 +02:00
* @throws DoctrineDBALException
2021-09-22 11:34:34 +02:00
* @throws InvalidSourceException
2016-06-24 19:40:43 +02:00
*/
2021-09-22 11:34:34 +02:00
public function getLastNumberFor($source): int
2016-06-24 19:40:43 +02:00
{
2021-09-16 15:30:22 +02:00
return (int)$this->entityManager->getConnection()->executeQuery(
'SELECT MAX(number) FROM '
2021-09-22 11:34:34 +02:00
. $this->entityManager->getClassMetadata(SequenceEntry::class)->getTableName()
2021-09-16 15:00:42 +02:00
. ' WHERE type = :type',
2021-09-22 11:34:34 +02:00
['type' => $this->inferTypeFromSource($source)]
2021-09-21 10:39:03 +02:00
)->fetchOne();
2016-06-24 19:40:43 +02:00
}
/**
2021-09-22 11:34:34 +02:00
* @param string|object $source
* @return string
2021-09-22 11:34:34 +02:00
* @throws InvalidSourceException
*/
2021-09-22 11:34:34 +02:00
protected function inferTypeFromSource($source): string
2017-03-13 16:59:04 +01:00
{
2021-09-22 11:34:34 +02:00
if (\is_string($source)) {
return $source;
}
2021-09-22 11:34:34 +02:00
if (\is_object($source)) {
return TypeHandling::getTypeForValue($source);
}
2017-03-13 16:59:04 +01:00
2021-09-22 11:34:34 +02:00
throw new InvalidSourceException('Could not infer type from source.', 1632216173);
}
2016-06-24 19:40:43 +02:00
}