1 we could return new keys immediately for this * request, as we "reserved" the space between. * * @Flow\Scope("singleton") */ class SequenceGenerator { /** * @Flow\Inject * @var EntityManagerInterface */ protected $entityManager; /** * @Flow\Inject * @var LoggerInterface */ protected $logger; /** * @param string|object $type * @return int * @throws Exception * @throws DoctrineDBALDriverException * @throws DoctrineDBALException */ public function getNextNumberFor($type): int { $type = $this->inferTypeFromSource($type); $count = $this->getLastNumberFor($type); // TODO: Check for maximal tries, or similar // TODO: Let increment be configurable per type do { $count++; } while (!$this->validateFreeNumber($count, $type)); return $count; } /** * @param int $count * @param string $type * @return bool */ protected function validateFreeNumber(int $count, string $type): bool { $em = $this->entityManager; try { $em->getConnection()->insert( $em->getClassMetadata(Insert::class)->getTableName(), ['number' => $count, 'type' => $type] ); return true; } catch (\PDOException $e) { return false; } catch (DoctrineDBALException $e) { if (!$e->getPrevious() instanceof \PDOException) { $this->logger->critical('Exception occurred: ' . $e->getMessage()); } } catch (\Exception $e) { $this->logger->critical('Exception occurred: ' . $e->getMessage()); } return false; } /** * @param int $to * @param string|object $type * * @return bool * @throws Exception */ public function advanceTo(int $to, $type): bool { $type = $this->inferTypeFromSource($type); return $this->validateFreeNumber($to, $type); } /** * @param string|object $type * @return int * @throws Exception * @throws DoctrineDBALDriverException * @throws DoctrineDBALException */ public function getLastNumberFor($type): int { return (int)$this->entityManager->getConnection()->executeQuery( 'SELECT MAX(number) FROM ' . $this->entityManager->getClassMetadata(Insert::class)->getTableName() . ' WHERE type = :type', ['type' => $this->inferTypeFromSource($type)] )->fetchOne(); } /** * @param string|object $stringOrObject * @return string * @throws Exception */ protected function inferTypeFromSource($stringOrObject): string { if (\is_object($stringOrObject)) { $stringOrObject = TypeHandling::getTypeForValue($stringOrObject); } if (!$stringOrObject) { throw new Exception('No Type given'); } return $stringOrObject; } }