Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
119
src/Framework/Database/Events/DomainEventCollector.php
Normal file
119
src/Framework/Database/Events/DomainEventCollector.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
/**
|
||||
* External collector for domain events using WeakMap
|
||||
* Keeps entities clean as simple value objects with automatic memory management
|
||||
*/
|
||||
final class DomainEventCollector
|
||||
{
|
||||
/** @var \WeakMap<object, object[]> Events mapped to entities with automatic GC */
|
||||
private \WeakMap $eventsByEntity;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->eventsByEntity = new \WeakMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a domain event for an entity
|
||||
*/
|
||||
public function recordEvent(object $entity, object $event): void
|
||||
{
|
||||
if (! isset($this->eventsByEntity[$entity])) {
|
||||
$this->eventsByEntity[$entity] = [];
|
||||
}
|
||||
$this->eventsByEntity[$entity][] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all recorded domain events for an entity
|
||||
*
|
||||
* @return object[]
|
||||
*/
|
||||
public function getEventsForEntity(object $entity): array
|
||||
{
|
||||
return $this->eventsByEntity[$entity] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all recorded domain events for an entity
|
||||
* (Optional - WeakMap handles cleanup automatically)
|
||||
*/
|
||||
public function clearEventsForEntity(object $entity): void
|
||||
{
|
||||
unset($this->eventsByEntity[$entity]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all recorded domain events across all entities
|
||||
*
|
||||
* @return object[]
|
||||
*/
|
||||
public function getAllEvents(): array
|
||||
{
|
||||
$allEvents = [];
|
||||
foreach ($this->eventsByEntity as $events) {
|
||||
$allEvents = array_merge($allEvents, $events);
|
||||
}
|
||||
|
||||
return $allEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any domain events for an entity
|
||||
*/
|
||||
public function hasEventsForEntity(object $entity): bool
|
||||
{
|
||||
return isset($this->eventsByEntity[$entity]) && ! empty($this->eventsByEntity[$entity]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain events of a specific type for an entity
|
||||
*
|
||||
* @template T
|
||||
* @param class-string<T> $eventClass
|
||||
* @return T[]
|
||||
*/
|
||||
public function getEventsOfTypeForEntity(object $entity, string $eventClass): array
|
||||
{
|
||||
$events = $this->getEventsForEntity($entity);
|
||||
|
||||
return array_filter(
|
||||
$events,
|
||||
fn (object $event) => $event instanceof $eventClass
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of events for an entity
|
||||
*/
|
||||
public function getEventCountForEntity(object $entity): int
|
||||
{
|
||||
return count($this->getEventsForEntity($entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count of all events across all entities
|
||||
*/
|
||||
public function getTotalEventCount(): int
|
||||
{
|
||||
$total = 0;
|
||||
foreach ($this->eventsByEntity as $events) {
|
||||
$total += count($events);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of tracked entities
|
||||
*/
|
||||
public function getTrackedEntityCount(): int
|
||||
{
|
||||
return count($this->eventsByEntity);
|
||||
}
|
||||
}
|
||||
47
src/Framework/Database/Events/EntityCreatedEvent.php
Normal file
47
src/Framework/Database/Events/EntityCreatedEvent.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Event that is fired when an entity is created
|
||||
*/
|
||||
final readonly class EntityCreatedEvent
|
||||
{
|
||||
public Timestamp $timestamp;
|
||||
|
||||
public function __construct(
|
||||
public object $entity,
|
||||
public string $entityClass,
|
||||
public mixed $entityId,
|
||||
public array $data = [],
|
||||
?Timestamp $timestamp = null
|
||||
) {
|
||||
$this->timestamp = $timestamp ?? Timestamp::now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this event is for a specific entity class
|
||||
*/
|
||||
public function isEntityOfType(string $expectedClass): bool
|
||||
{
|
||||
return $this->entityClass === $expectedClass || $this->entity instanceof $expectedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event metadata
|
||||
*/
|
||||
public function getEventData(): array
|
||||
{
|
||||
return [
|
||||
'event_type' => 'entity_created',
|
||||
'entity_class' => $this->entityClass,
|
||||
'entity_id' => $this->entityId,
|
||||
'timestamp' => $this->timestamp->toFloat(),
|
||||
'data' => $this->data,
|
||||
];
|
||||
}
|
||||
}
|
||||
47
src/Framework/Database/Events/EntityDeletedEvent.php
Normal file
47
src/Framework/Database/Events/EntityDeletedEvent.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Event that is fired when an entity is deleted
|
||||
*/
|
||||
final readonly class EntityDeletedEvent
|
||||
{
|
||||
public Timestamp $timestamp;
|
||||
|
||||
public function __construct(
|
||||
public object $entity,
|
||||
public string $entityClass,
|
||||
public mixed $entityId,
|
||||
public array $deletedData = [],
|
||||
?Timestamp $timestamp = null
|
||||
) {
|
||||
$this->timestamp = $timestamp ?? Timestamp::now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this event is for a specific entity class
|
||||
*/
|
||||
public function isEntityOfType(string $expectedClass): bool
|
||||
{
|
||||
return $this->entityClass === $expectedClass || $this->entity instanceof $expectedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event metadata
|
||||
*/
|
||||
public function getEventData(): array
|
||||
{
|
||||
return [
|
||||
'event_type' => 'entity_deleted',
|
||||
'entity_class' => $this->entityClass,
|
||||
'entity_id' => $this->entityId,
|
||||
'timestamp' => $this->timestamp->toFloat(),
|
||||
'deleted_data' => $this->deletedData,
|
||||
];
|
||||
}
|
||||
}
|
||||
45
src/Framework/Database/Events/EntityDetachedEvent.php
Normal file
45
src/Framework/Database/Events/EntityDetachedEvent.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Event that is fired when an entity is detached from the entity manager
|
||||
*/
|
||||
final readonly class EntityDetachedEvent
|
||||
{
|
||||
public Timestamp $timestamp;
|
||||
|
||||
public function __construct(
|
||||
public object $entity,
|
||||
public string $entityClass,
|
||||
public mixed $entityId,
|
||||
?Timestamp $timestamp = null
|
||||
) {
|
||||
$this->timestamp = $timestamp ?? Timestamp::now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this event is for a specific entity class
|
||||
*/
|
||||
public function isEntityOfType(string $expectedClass): bool
|
||||
{
|
||||
return $this->entityClass === $expectedClass || $this->entity instanceof $expectedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event metadata
|
||||
*/
|
||||
public function getEventData(): array
|
||||
{
|
||||
return [
|
||||
'event_type' => 'entity_detached',
|
||||
'entity_class' => $this->entityClass,
|
||||
'entity_id' => $this->entityId,
|
||||
'timestamp' => $this->timestamp->toFloat(),
|
||||
];
|
||||
}
|
||||
}
|
||||
158
src/Framework/Database/Events/EntityEventManager.php
Normal file
158
src/Framework/Database/Events/EntityEventManager.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
use App\Framework\Core\Events\EventDispatcher;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\DateTime\Clock;
|
||||
|
||||
/**
|
||||
* Manages entity lifecycle events and coordinates with the core event system
|
||||
*/
|
||||
final class EntityEventManager
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EventDispatcher $eventDispatcher,
|
||||
private readonly DomainEventCollector $domainEventCollector,
|
||||
private readonly Clock $clock
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an entity lifecycle event
|
||||
*/
|
||||
public function dispatchLifecycleEvent(object $event): void
|
||||
{
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a domain event for later dispatch
|
||||
*/
|
||||
public function recordDomainEvent(object $entity, object $event): void
|
||||
{
|
||||
$this->domainEventCollector->recordEvent($entity, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch all domain events for an entity and clear them
|
||||
*/
|
||||
public function dispatchDomainEventsForEntity(object $entity): void
|
||||
{
|
||||
$events = $this->domainEventCollector->getEventsForEntity($entity);
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
|
||||
$this->domainEventCollector->clearEventsForEntity($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch all domain events across all entities and clear them
|
||||
*/
|
||||
public function dispatchAllDomainEvents(): void
|
||||
{
|
||||
$allEvents = $this->domainEventCollector->getAllEvents();
|
||||
|
||||
foreach ($allEvents as $event) {
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
}
|
||||
|
||||
// Events are automatically cleared when entities are garbage collected
|
||||
// But we can manually clear for immediate cleanup
|
||||
foreach ($this->domainEventCollector->getAllEvents() as $entity) {
|
||||
$this->domainEventCollector->clearEventsForEntity($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and dispatch an EntityCreatedEvent
|
||||
*/
|
||||
public function entityCreated(object $entity, string $entityClass, mixed $entityId, array $data = []): void
|
||||
{
|
||||
$timestamp = Timestamp::fromClock($this->clock);
|
||||
$event = new EntityCreatedEvent($entity, $entityClass, $entityId, $data, $timestamp);
|
||||
$this->dispatchLifecycleEvent($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and dispatch an EntityUpdatedEvent
|
||||
*/
|
||||
public function entityUpdated(
|
||||
object $entity,
|
||||
string $entityClass,
|
||||
mixed $entityId,
|
||||
array $changes = [],
|
||||
array $oldValues = [],
|
||||
array $newValues = []
|
||||
): void {
|
||||
$timestamp = Timestamp::fromClock($this->clock);
|
||||
$event = new EntityUpdatedEvent($entity, $entityClass, $entityId, $changes, $oldValues, $newValues, $timestamp);
|
||||
$this->dispatchLifecycleEvent($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and dispatch an EntityDeletedEvent
|
||||
*/
|
||||
public function entityDeleted(object $entity, string $entityClass, mixed $entityId, array $deletedData = []): void
|
||||
{
|
||||
$timestamp = Timestamp::fromClock($this->clock);
|
||||
$event = new EntityDeletedEvent($entity, $entityClass, $entityId, $deletedData, $timestamp);
|
||||
$this->dispatchLifecycleEvent($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and dispatch an EntityLoadedEvent
|
||||
*/
|
||||
public function entityLoaded(
|
||||
object $entity,
|
||||
string $entityClass,
|
||||
mixed $entityId,
|
||||
array $loadedData = [],
|
||||
bool $wasLazy = false
|
||||
): void {
|
||||
$timestamp = Timestamp::fromClock($this->clock);
|
||||
$event = new EntityLoadedEvent($entity, $entityClass, $entityId, $loadedData, $wasLazy, $timestamp);
|
||||
$this->dispatchLifecycleEvent($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and dispatch an EntityDetachedEvent
|
||||
*/
|
||||
public function entityDetached(object $entity, string $entityClass, mixed $entityId): void
|
||||
{
|
||||
$timestamp = Timestamp::fromClock($this->clock);
|
||||
$event = new EntityDetachedEvent($entity, $entityClass, $entityId, $timestamp);
|
||||
$this->dispatchLifecycleEvent($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain event collector for advanced usage
|
||||
*/
|
||||
public function getDomainEventCollector(): DomainEventCollector
|
||||
{
|
||||
return $this->domainEventCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core event dispatcher for advanced usage
|
||||
*/
|
||||
public function getEventDispatcher(): EventDispatcher
|
||||
{
|
||||
return $this->eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statistics about domain events
|
||||
*/
|
||||
public function getDomainEventStats(): array
|
||||
{
|
||||
return [
|
||||
'tracked_entities' => $this->domainEventCollector->getTrackedEntityCount(),
|
||||
'total_events' => $this->domainEventCollector->getTotalEventCount(),
|
||||
];
|
||||
}
|
||||
}
|
||||
49
src/Framework/Database/Events/EntityLoadedEvent.php
Normal file
49
src/Framework/Database/Events/EntityLoadedEvent.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Event that is fired when an entity is loaded from database
|
||||
*/
|
||||
final readonly class EntityLoadedEvent
|
||||
{
|
||||
public Timestamp $timestamp;
|
||||
|
||||
public function __construct(
|
||||
public object $entity,
|
||||
public string $entityClass,
|
||||
public mixed $entityId,
|
||||
public array $loadedData = [],
|
||||
public bool $wasLazy = false,
|
||||
?Timestamp $timestamp = null,
|
||||
) {
|
||||
$this->timestamp = $timestamp ?? Timestamp::now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this event is for a specific entity class
|
||||
*/
|
||||
public function isEntityOfType(string $expectedClass): bool
|
||||
{
|
||||
return $this->entityClass === $expectedClass || $this->entity instanceof $expectedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event metadata
|
||||
*/
|
||||
public function getEventData(): array
|
||||
{
|
||||
return [
|
||||
'event_type' => 'entity_loaded',
|
||||
'entity_class' => $this->entityClass,
|
||||
'entity_id' => $this->entityId,
|
||||
'timestamp' => $this->timestamp->toFloat(),
|
||||
'was_lazy' => $this->wasLazy,
|
||||
'loaded_data' => $this->loadedData,
|
||||
];
|
||||
}
|
||||
}
|
||||
74
src/Framework/Database/Events/EntityUpdatedEvent.php
Normal file
74
src/Framework/Database/Events/EntityUpdatedEvent.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Event that is fired when an entity is updated
|
||||
*/
|
||||
final readonly class EntityUpdatedEvent
|
||||
{
|
||||
public Timestamp $timestamp;
|
||||
|
||||
public function __construct(
|
||||
public object $entity,
|
||||
public string $entityClass,
|
||||
public mixed $entityId,
|
||||
public array $changes = [],
|
||||
public array $oldValues = [],
|
||||
public array $newValues = [],
|
||||
?Timestamp $timestamp = null
|
||||
) {
|
||||
$this->timestamp = $timestamp ?? Timestamp::now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this event is for a specific entity class
|
||||
*/
|
||||
public function isEntityOfType(string $expectedClass): bool
|
||||
{
|
||||
return $this->entityClass === $expectedClass || $this->entity instanceof $expectedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific field was changed
|
||||
*/
|
||||
public function hasFieldChanged(string $fieldName): bool
|
||||
{
|
||||
return in_array($fieldName, $this->changes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old value of a field
|
||||
*/
|
||||
public function getOldValue(string $fieldName): mixed
|
||||
{
|
||||
return $this->oldValues[$fieldName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new value of a field
|
||||
*/
|
||||
public function getNewValue(string $fieldName): mixed
|
||||
{
|
||||
return $this->newValues[$fieldName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event metadata
|
||||
*/
|
||||
public function getEventData(): array
|
||||
{
|
||||
return [
|
||||
'event_type' => 'entity_updated',
|
||||
'entity_class' => $this->entityClass,
|
||||
'entity_id' => $this->entityId,
|
||||
'timestamp' => $this->timestamp->toFloat(),
|
||||
'changes' => $this->changes,
|
||||
'changed_fields_count' => count($this->changes),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events\Examples;
|
||||
|
||||
use App\Framework\Database\EntityManager;
|
||||
|
||||
/**
|
||||
* Comprehensive Entity Event System usage examples
|
||||
*
|
||||
* This demonstrates how to use the entity lifecycle events
|
||||
* with the existing Core EventDispatcher system.
|
||||
*/
|
||||
final class EntityEventUsageExample
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManager $entityManager
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Basic lifecycle event handling
|
||||
*/
|
||||
public function basicLifecycleEventHandling(): void
|
||||
{
|
||||
// Create a new entity
|
||||
$user = new ExampleUser('john@example.com', 'John Doe');
|
||||
|
||||
// The EntityManager will automatically dispatch EntityCreatedEvent
|
||||
$this->entityManager->insert($user);
|
||||
|
||||
// Update the entity
|
||||
$user = $user->updateEmail('john.doe@example.com');
|
||||
|
||||
// The EntityManager will automatically dispatch EntityUpdatedEvent
|
||||
$this->entityManager->update($user);
|
||||
|
||||
// Delete the entity
|
||||
// The EntityManager will automatically dispatch EntityDeletedEvent
|
||||
$this->entityManager->delete($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Domain event recording and dispatching
|
||||
*/
|
||||
public function domainEventHandling(): void
|
||||
{
|
||||
$user = new ExampleUser('jane@example.com', 'Jane Smith');
|
||||
|
||||
// Record domain events (these are not automatically dispatched)
|
||||
$welcomeEvent = new UserWelcomeEmailEvent($user, 'jane@example.com');
|
||||
$this->entityManager->recordDomainEvent($user, $welcomeEvent);
|
||||
|
||||
$analyticsEvent = new UserRegistrationAnalyticsEvent($user, 'web', 'organic');
|
||||
$this->entityManager->recordDomainEvent($user, $analyticsEvent);
|
||||
|
||||
// Create the entity
|
||||
$this->entityManager->insert($user);
|
||||
|
||||
// Manually dispatch domain events for this entity
|
||||
$this->entityManager->dispatchDomainEventsForEntity($user);
|
||||
|
||||
// Or dispatch all domain events across all entities
|
||||
// $this->entityManager->dispatchAllDomainEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Event statistics and monitoring
|
||||
*/
|
||||
public function eventStatistics(): array
|
||||
{
|
||||
// Get domain event statistics
|
||||
$domainStats = $this->entityManager->getDomainEventStats();
|
||||
|
||||
return [
|
||||
'tracked_entities' => $domainStats['tracked_entities'],
|
||||
'pending_domain_events' => $domainStats['total_events'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Batch operations with events
|
||||
*/
|
||||
public function batchOperationsWithEvents(): void
|
||||
{
|
||||
$users = [
|
||||
new ExampleUser('user1@example.com', 'User One'),
|
||||
new ExampleUser('user2@example.com', 'User Two'),
|
||||
new ExampleUser('user3@example.com', 'User Three'),
|
||||
];
|
||||
|
||||
// Record domain events for each user
|
||||
foreach ($users as $user) {
|
||||
$event = new UserRegistrationAnalyticsEvent($user, 'batch_import', 'admin');
|
||||
$this->entityManager->recordDomainEvent($user, $event);
|
||||
}
|
||||
|
||||
// Save all users (will trigger EntityCreatedEvent for each)
|
||||
$this->entityManager->saveAll(...$users);
|
||||
|
||||
// Dispatch all domain events at once
|
||||
$this->entityManager->dispatchAllDomainEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Conditional event handling
|
||||
*/
|
||||
public function conditionalEventHandling(): void
|
||||
{
|
||||
$user = new ExampleUser('conditional@example.com', 'Conditional User');
|
||||
|
||||
// Record conditional domain event
|
||||
$premiumEvent = new UserPremiumUpgradeEvent($user, 'premium_plan');
|
||||
$this->entityManager->recordDomainEvent($user, $premiumEvent);
|
||||
|
||||
// Events will be dispatched based on conditions in handlers
|
||||
$this->entityManager->insert($user);
|
||||
$this->entityManager->dispatchDomainEventsForEntity($user);
|
||||
}
|
||||
}
|
||||
26
src/Framework/Database/Events/Examples/ExampleUser.php
Normal file
26
src/Framework/Database/Events/Examples/ExampleUser.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events\Examples;
|
||||
|
||||
/**
|
||||
* Example Entity for demonstration purposes
|
||||
*/
|
||||
final class ExampleUser
|
||||
{
|
||||
public readonly string $id;
|
||||
|
||||
public function __construct(
|
||||
public readonly string $email,
|
||||
public readonly string $name,
|
||||
?string $id = null
|
||||
) {
|
||||
$this->id = $id ?? uniqid('user_');
|
||||
}
|
||||
|
||||
public function updateEmail(string $newEmail): self
|
||||
{
|
||||
return new self($newEmail, $this->name, $this->id);
|
||||
}
|
||||
}
|
||||
191
src/Framework/Database/Events/Examples/UserEventHandlers.php
Normal file
191
src/Framework/Database/Events/Examples/UserEventHandlers.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events\Examples;
|
||||
|
||||
use App\Framework\Core\Events\OnEvent;
|
||||
use App\Framework\Database\Events\EntityCreatedEvent;
|
||||
use App\Framework\Database\Events\EntityDeletedEvent;
|
||||
use App\Framework\Database\Events\EntityDetachedEvent;
|
||||
use App\Framework\Database\Events\EntityLoadedEvent;
|
||||
use App\Framework\Database\Events\EntityUpdatedEvent;
|
||||
|
||||
/**
|
||||
* Example Event Handlers using Core EventDispatcher
|
||||
*/
|
||||
final class UserEventHandlers
|
||||
{
|
||||
/**
|
||||
* Handle entity creation for all entities
|
||||
*/
|
||||
#[OnEvent(priority: 100)]
|
||||
public function onEntityCreated(EntityCreatedEvent $event): void
|
||||
{
|
||||
error_log("Entity created: {$event->entityClass} with ID {$event->entityId}");
|
||||
|
||||
// Log to analytics service
|
||||
$this->logAnalyticsEvent('entity_created', [
|
||||
'entity_class' => $event->entityClass,
|
||||
'entity_id' => $event->entityId,
|
||||
'timestamp' => $event->timestamp,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user-specific entity creation
|
||||
*/
|
||||
#[OnEvent(priority: 90)]
|
||||
public function onUserCreated(EntityCreatedEvent $event): void
|
||||
{
|
||||
if (! $event->isEntityOfType(ExampleUser::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
error_log("New user registered: {$event->entity->email}");
|
||||
|
||||
// Send welcome email, create user profile, etc.
|
||||
$this->sendWelcomeEmail($event->entity);
|
||||
$this->createUserProfile($event->entity);
|
||||
$this->trackUserRegistration($event->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle entity updates with change tracking
|
||||
*/
|
||||
#[OnEvent(priority: 100)]
|
||||
public function onEntityUpdated(EntityUpdatedEvent $event): void
|
||||
{
|
||||
error_log("Entity updated: {$event->entityClass} with ID {$event->entityId}");
|
||||
|
||||
if ($event->isEntityOfType(ExampleUser::class)) {
|
||||
$this->handleUserUpdate($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle entity deletion with cleanup
|
||||
*/
|
||||
#[OnEvent(priority: 100)]
|
||||
public function onEntityDeleted(EntityDeletedEvent $event): void
|
||||
{
|
||||
error_log("Entity deleted: {$event->entityClass} with ID {$event->entityId}");
|
||||
|
||||
if ($event->isEntityOfType(ExampleUser::class)) {
|
||||
$this->cleanupUserData($event->entity);
|
||||
$this->notifyUserDeletion($event->entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle entity loading for performance monitoring
|
||||
*/
|
||||
#[OnEvent(priority: 50)]
|
||||
public function onEntityLoaded(EntityLoadedEvent $event): void
|
||||
{
|
||||
if ($event->wasLazy) {
|
||||
error_log("Lazy entity loaded: {$event->entityClass} with ID {$event->entityId}");
|
||||
}
|
||||
|
||||
// Track loading performance
|
||||
$this->trackEntityLoadTime($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle entity detachment for cleanup
|
||||
*/
|
||||
#[OnEvent(priority: 100)]
|
||||
public function onEntityDetached(EntityDetachedEvent $event): void
|
||||
{
|
||||
error_log("Entity detached: {$event->entityClass} with ID {$event->entityId}");
|
||||
|
||||
// Clean up any cached references
|
||||
$this->cleanupEntityReferences($event->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle domain events
|
||||
*/
|
||||
#[OnEvent(priority: 200)]
|
||||
public function onUserWelcomeEmail(UserWelcomeEmailEvent $event): void
|
||||
{
|
||||
error_log("Sending welcome email to: {$event->email}");
|
||||
|
||||
// Send actual email
|
||||
// $this->emailService->sendWelcomeEmail($event->user, $event->email);
|
||||
}
|
||||
|
||||
#[OnEvent(priority: 150)]
|
||||
public function onUserRegistrationAnalytics(UserRegistrationAnalyticsEvent $event): void
|
||||
{
|
||||
error_log("Tracking user registration: {$event->source} - {$event->channel}");
|
||||
|
||||
// Track in analytics system
|
||||
// $this->analyticsService->track('user_registered', [
|
||||
// 'user_id' => $event->user->id,
|
||||
// 'source' => $event->source,
|
||||
// 'channel' => $event->channel,
|
||||
// 'timestamp' => $event->timestamp,
|
||||
// ]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper methods (implementation would depend on your services)
|
||||
*/
|
||||
private function logAnalyticsEvent(string $eventType, array $data): void
|
||||
{
|
||||
// Implementation depends on your analytics service
|
||||
error_log("Analytics: {$eventType} - " . json_encode($data));
|
||||
}
|
||||
|
||||
private function sendWelcomeEmail(ExampleUser $user): void
|
||||
{
|
||||
error_log("Sending welcome email to: {$user->email}");
|
||||
}
|
||||
|
||||
private function createUserProfile(ExampleUser $user): void
|
||||
{
|
||||
error_log("Creating user profile for: {$user->name}");
|
||||
}
|
||||
|
||||
private function trackUserRegistration(ExampleUser $user): void
|
||||
{
|
||||
error_log("Tracking registration for user: {$user->id}");
|
||||
}
|
||||
|
||||
private function handleUserUpdate(EntityUpdatedEvent $event): void
|
||||
{
|
||||
error_log("User updated: {$event->entity->email}");
|
||||
|
||||
// Handle specific field changes
|
||||
if ($event->hasFieldChanged('email')) {
|
||||
$oldEmail = $event->getOldValue('email');
|
||||
$newEmail = $event->getNewValue('email');
|
||||
error_log("Email changed from {$oldEmail} to {$newEmail}");
|
||||
|
||||
// Send email confirmation, update related records, etc.
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanupUserData(ExampleUser $user): void
|
||||
{
|
||||
error_log("Cleaning up data for deleted user: {$user->id}");
|
||||
|
||||
// Delete user files, clear caches, notify related services
|
||||
}
|
||||
|
||||
private function notifyUserDeletion(ExampleUser $user): void
|
||||
{
|
||||
error_log("Notifying services about user deletion: {$user->id}");
|
||||
}
|
||||
|
||||
private function trackEntityLoadTime(EntityLoadedEvent $event): void
|
||||
{
|
||||
error_log("Entity load tracked: {$event->entityClass} loaded at {$event->timestamp}");
|
||||
}
|
||||
|
||||
private function cleanupEntityReferences(object $entity): void
|
||||
{
|
||||
error_log("Cleaning up references for detached entity: " . get_class($entity));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events\Examples;
|
||||
|
||||
/**
|
||||
* Example Domain Event - User Premium Upgrade
|
||||
*/
|
||||
final readonly class UserPremiumUpgradeEvent
|
||||
{
|
||||
public function __construct(
|
||||
public object $user,
|
||||
public string $planType,
|
||||
public ?float $timestamp = null
|
||||
) {
|
||||
$this->timestamp ??= microtime(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events\Examples;
|
||||
|
||||
/**
|
||||
* Example Domain Event - User Registration Analytics
|
||||
*/
|
||||
final readonly class UserRegistrationAnalyticsEvent
|
||||
{
|
||||
public function __construct(
|
||||
public object $user,
|
||||
public string $source,
|
||||
public string $channel,
|
||||
public ?float $timestamp = null
|
||||
) {
|
||||
$this->timestamp ??= microtime(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Database\Events\Examples;
|
||||
|
||||
/**
|
||||
* Example Domain Event - User Welcome Email
|
||||
*/
|
||||
final readonly class UserWelcomeEmailEvent
|
||||
{
|
||||
public function __construct(
|
||||
public object $user,
|
||||
public string $email,
|
||||
public ?float $timestamp = null
|
||||
) {
|
||||
$this->timestamp ??= microtime(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user