eca-1.0.x-dev/src/Entity/EcaStorage.php
src/Entity/EcaStorage.php
<?php
namespace Drupal\eca\Entity;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\State\StateInterface;
use Drupal\ai\Service\FunctionCalling\FunctionCallPluginManager;
use Drupal\field_widget_actions\PluginManager\FieldWidgetActionManager;
use Random\RandomException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Storage handler for ECA configurations.
*/
class EcaStorage extends ConfigEntityStorage {
/**
* The logger.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected LoggerChannelInterface $logger;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected EventDispatcherInterface $eventDispatcher;
/**
* The dynamic event subscriber.
*
* @var \Symfony\Component\EventDispatcher\EventSubscriberInterface
*/
protected EventSubscriberInterface $eventSubscriber;
/**
* The Drupal state.
*
* @var \Drupal\Core\State\StateInterface
*/
protected StateInterface $state;
/**
* The lock backend.
*
* @var \Drupal\Core\Lock\LockBackendInterface
*/
protected LockBackendInterface $lock;
/**
* The field widget action plugin manager.
*
* @var \Drupal\field_widget_actions\PluginManager\FieldWidgetActionManager|null
*/
protected FieldWidgetActionManager|null $fieldWidgetActionPluginManager;
/**
* The AI Function Call plugin manager.
*
* @var \Drupal\ai\Service\FunctionCalling\FunctionCallPluginManager|null
*/
protected FunctionCallPluginManager|null $aiFunctionCallPluginManager;
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type): EcaStorage {
/** @var static $instance */
$instance = parent::createInstance($container, $entity_type);
/** @var \Drupal\Core\Logger\LoggerChannelInterface $loggerChannel */
$loggerChannel = $container->get('logger.channel.eca');
$instance->setLogger($loggerChannel);
$instance->eventDispatcher = $container->get('event_dispatcher');
$instance->eventSubscriber = $container->get('eca.dynamic_subscriber');
$instance->lock = $container->get('lock');
$instance->state = $container->get('state');
$instance->fieldWidgetActionPluginManager = $container->get('plugin.manager.field_widget_actions', ContainerInterface::NULL_ON_INVALID_REFERENCE);
$instance->aiFunctionCallPluginManager = $container->get('plugin.manager.ai.function_calls', ContainerInterface::NULL_ON_INVALID_REFERENCE);
return $instance;
}
/**
* Rebuilds the state of subscribed events.
*/
public function rebuildSubscribedEvents(): void {
$lock_name = 'eca_rebuild_subscribed_events';
if (!$this->lock->acquire($lock_name)) {
try {
$sleep = random_int(1000, 50000);
}
catch (\Exception | RandomException) {
$sleep = 2500;
}
usleep($sleep);
$this->rebuildSubscribedEvents();
return;
}
$subscribedEvents = $this->doRebuildSubscribedEvents();
if ($this->state->get('eca.subscribed') !== $subscribedEvents) {
$this->state->set('eca.subscribed', $subscribedEvents);
$this->eventDispatcher->removeSubscriber($this->eventSubscriber);
$this->eventDispatcher->addSubscriber($this->eventSubscriber);
}
if (isset($subscribedEvents['eca_base.field_widget']) && $this->fieldWidgetActionPluginManager !== NULL) {
$this->fieldWidgetActionPluginManager->clearCachedDefinitions();
}
if (isset($subscribedEvents['eca_base.tool']) && $this->aiFunctionCallPluginManager !== NULL) {
$this->aiFunctionCallPluginManager->clearCachedDefinitions();
}
$this->lock->release($lock_name);
}
/**
* {@inheritdoc}
*/
protected function doPostSave(EntityInterface $entity, $update): void {
parent::doPostSave($entity, $update);
$this->rebuildSubscribedEvents();
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function delete(array $entities): void {
parent::delete($entities);
$this->rebuildSubscribedEvents();
}
/**
* Rebuilds the list of subscribed events based on the current configuration.
*
* @return array
* The list of ECA configuration IDs, grouped by their subscribed events.
*/
protected function doRebuildSubscribedEvents(): array {
$subscribed = [];
$entities = $this->loadMultiple();
// Sort the configurations by weight and label.
uasort($entities, [$this->entityType->getClass(), 'sort']);
/**
* @var \Drupal\eca\Entity\Eca $eca
*/
foreach ($entities as $eca) {
if (!$eca->status()) {
continue;
}
foreach ($eca->getUsedEvents() as $eca_event_id => $ecaEvent) {
$eca_id = $eca->id();
$plugin = $ecaEvent->getPlugin();
$name = $plugin->eventName();
$priority = $plugin->subscriberPriority();
$wildcard = $plugin->generateWildcard($eca_id, $ecaEvent);
$subscribed[$name][$priority][$eca_id][$eca_event_id] = $wildcard;
}
}
// Make sure that priorities are always distinct.
foreach ($subscribed as $prioritized) {
if (count($prioritized) > 1) {
throw new \LogicException("Priority for event subscription must be distinct.");
}
}
$this->logger->debug('Rebuilt subscribed events of ECA configuration.');
return $subscribed;
}
/**
* Set the logger.
*
* @param \Drupal\Core\Logger\LoggerChannelInterface $logger
* The logger.
*/
public function setLogger(LoggerChannelInterface $logger): void {
$this->logger = $logger;
}
}
