178 lines
4.5 KiB
PHP
178 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\LiveComponents\Polling;
|
|
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
|
|
|
/**
|
|
* Registry for tracking PollableClosure instances in templates.
|
|
*
|
|
* Maintains a mapping of PollIds to their pollable closure registrations
|
|
* for automatic JavaScript generation and endpoint registration.
|
|
*/
|
|
final class PollableClosureRegistry
|
|
{
|
|
/** @var array<string, PollableClosureRegistration> */
|
|
private array $registrations = [];
|
|
|
|
/**
|
|
* Register a pollable closure.
|
|
*
|
|
* @param string $templateKey Template variable key (e.g., 'notifications')
|
|
* @param PollableClosure $closure The pollable closure
|
|
* @param ComponentId|null $componentId Optional component ID for scoping
|
|
* @return PollableClosureRegistration
|
|
*/
|
|
public function register(
|
|
string $templateKey,
|
|
PollableClosure $closure,
|
|
?ComponentId $componentId = null
|
|
): PollableClosureRegistration {
|
|
$pollId = $this->generatePollId($templateKey, $componentId);
|
|
|
|
$registration = new PollableClosureRegistration(
|
|
pollId: $pollId,
|
|
closure: $closure,
|
|
templateKey: $templateKey,
|
|
componentId: $componentId
|
|
);
|
|
|
|
$this->registrations[$pollId->value] = $registration;
|
|
|
|
return $registration;
|
|
}
|
|
|
|
/**
|
|
* Get a registration by poll ID.
|
|
*/
|
|
public function get(PollId $pollId): ?PollableClosureRegistration
|
|
{
|
|
return $this->registrations[$pollId->value] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Check if a poll ID exists.
|
|
*/
|
|
public function has(PollId $pollId): bool
|
|
{
|
|
return isset($this->registrations[$pollId->value]);
|
|
}
|
|
|
|
/**
|
|
* Get all registered closures.
|
|
*
|
|
* @return array<PollableClosureRegistration>
|
|
*/
|
|
public function getAll(): array
|
|
{
|
|
return array_values($this->registrations);
|
|
}
|
|
|
|
/**
|
|
* Get all enabled registrations.
|
|
*
|
|
* @return array<PollableClosureRegistration>
|
|
*/
|
|
public function getEnabled(): array
|
|
{
|
|
return array_values(array_filter(
|
|
$this->registrations,
|
|
fn(PollableClosureRegistration $reg) => $reg->shouldPoll()
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Get registrations for specific component.
|
|
*
|
|
* @return array<PollableClosureRegistration>
|
|
*/
|
|
public function getForComponent(ComponentId $componentId): array
|
|
{
|
|
return array_values(array_filter(
|
|
$this->registrations,
|
|
fn(PollableClosureRegistration $reg) => $reg->isForComponent($componentId)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Execute a closure by poll ID.
|
|
*
|
|
* @throws \RuntimeException if poll ID not found or closure throws
|
|
*/
|
|
public function execute(PollId $pollId): mixed
|
|
{
|
|
$registration = $this->get($pollId);
|
|
|
|
if ($registration === null) {
|
|
throw new \RuntimeException("Poll ID '{$pollId}' not found");
|
|
}
|
|
|
|
try {
|
|
return $registration->execute();
|
|
} catch (\Throwable $e) {
|
|
throw new \RuntimeException(
|
|
"Failed to execute pollable closure '{$pollId}': {$e->getMessage()}",
|
|
previous: $e
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregister a closure.
|
|
*/
|
|
public function unregister(PollId $pollId): void
|
|
{
|
|
unset($this->registrations[$pollId->value]);
|
|
}
|
|
|
|
/**
|
|
* Clear all registered closures.
|
|
*/
|
|
public function clear(): void
|
|
{
|
|
$this->registrations = [];
|
|
}
|
|
|
|
/**
|
|
* Get count of registered closures.
|
|
*/
|
|
public function count(): int
|
|
{
|
|
return count($this->registrations);
|
|
}
|
|
|
|
/**
|
|
* Generate unique poll ID.
|
|
*/
|
|
private function generatePollId(string $templateKey, ?ComponentId $componentId): PollId
|
|
{
|
|
if ($componentId !== null) {
|
|
return PollId::forClosure($templateKey, $componentId->toString());
|
|
}
|
|
|
|
return PollId::forClosure($templateKey);
|
|
}
|
|
|
|
/**
|
|
* Get polling metadata for JavaScript generation.
|
|
*
|
|
* @return array<string, array{template_key: string, interval: int, event: ?string, endpoint: string, component_id: ?string}>
|
|
*/
|
|
public function getPollingMetadata(): array
|
|
{
|
|
$metadata = [];
|
|
|
|
foreach ($this->registrations as $pollIdString => $registration) {
|
|
if (!$registration->shouldPoll()) {
|
|
continue;
|
|
}
|
|
|
|
$metadata[$pollIdString] = $registration->getMetadata();
|
|
}
|
|
|
|
return $metadata;
|
|
}
|
|
}
|