Skip to content
This repository has been archived by the owner on Sep 20, 2019. It is now read-only.

Add event_class_map for event class aliases #178

Merged
merged 6 commits into from
Jul 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

All notable changes to `laravel-event-projector` will be documented in this file

## x.x.x - 2019-xx-xx
- Added `event_class_map` to alias your event classes which allows for refactoring after events have been fired

## 2.6.3 - 2019-06-14

- fix warnings in console commands
Expand Down
7 changes: 7 additions & 0 deletions config/event-projector.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
*/
'stored_event_job' => \Spatie\EventProjector\HandleStoredEventJob::class,

/*
* Similar to Relation::morphMap() you can define which alias responds to which
* event class. This allows you to change the namespace or classnames
* of your events but still handle older events correctly.
*/
'event_class_map' => [],

/*
* This class is responsible for serializing events. By default an event will be serialized
* and stored as json. You can customize the class name. A valid serializer
Expand Down
21 changes: 21 additions & 0 deletions docs/advanced-usage/using-aliases-for-stored-event-classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: Using aliases for stored event classes
weight: 8
---

By default we store the `Event`'s FQCN in the database when storing the events. This prevents you from changing the name or the namespace of your event classes.

To get around this you can define event class aliases in the `event-projector.php` config file:

```php
/*
* Similar to Relation::morphMap() you can define which alias responds to which
* event class. This allows you to change the namespace or classnames
* of your events but still handle older events correctly.
*/
'event_class_map' => [
'money_added' => MoneyAddedEvent::class,
],
```

With this configuration, instead of saving `\App\Events\MoneyAddedEvent` in the database, we just store `money_added`, now you can change the event classname and namespace. Just make sure to also change the mapping!
7 changes: 7 additions & 0 deletions docs/installation-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ return [
* it should extend \Spatie\EventProjector\HandleDomainEventJob.
*/
'stored_event_job' => \Spatie\EventProjector\HandleStoredEventJob::class,

/*
* Similar to Relation::morphMap() you can define which alias responds to which
* event class. This allows you to change the namespace or classnames
* of your events but still handle older events correctly.
*/
'event_class_map' => [],

/*
* This class is responsible for serializing events. By default an event will be serialized
Expand Down
24 changes: 23 additions & 1 deletion src/Models/StoredEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Exception;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Spatie\EventProjector\ShouldBeStored;
Expand All @@ -27,7 +28,7 @@ public static function createForEvent(ShouldBeStored $event, string $uuid = null
{
$storedEvent = new static();
$storedEvent->aggregate_uuid = $uuid;
$storedEvent->event_class = get_class($event);
$storedEvent->event_class = static::getEventClass(get_class($event));
$storedEvent->attributes['event_properties'] = app(EventSerializer::class)->serialize(clone $event);
$storedEvent->meta_data = [];
$storedEvent->created_at = Carbon::now();
Expand All @@ -37,6 +38,11 @@ public static function createForEvent(ShouldBeStored $event, string $uuid = null
return $storedEvent;
}

public function getEventClassAttribute(string $value): string
{
return static::getActualClassForEvent($value);
}

public function getEventAttribute(): ShouldBeStored
{
try {
Expand Down Expand Up @@ -100,4 +106,20 @@ public static function store(ShouldBeStored $event, string $uuid = null): void
{
static::storeMany([$event], $uuid);
}

protected static function getEventClass(string $class): string
{
$map = config('event-projector.event_class_map', []);

if (! empty($map) && in_array($class, $map)) {
return array_search($class, $map, true);
}

return $class;
}

protected static function getActualClassForEvent(string $class): string
{
return Arr::get(config('event-projector.event_class_map', []), $class, $class);
}
}
21 changes: 21 additions & 0 deletions tests/EventSubscriberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ public function it_will_log_events_that_implement_ShouldBeStored()
$this->assertEquals($this->account->id, $storedEvent->event->account->id);
}

/** @test * */
public function it_will_log_events_that_implement_ShouldBeStored_with_a_map()
{
$this->setConfig('event-projector.event_class_map', [
'money_added' => MoneyAddedEvent::class,
]);

event(new MoneyAddedEvent($this->account, 1234));

$this->assertCount(1, StoredEvent::get());

$storedEvent = StoredEvent::first();

$this->assertEquals(MoneyAddedEvent::class, $storedEvent->event_class);
$this->assertEquals('money_added', $storedEvent->getAttributes()['event_class']);

$this->assertInstanceOf(MoneyAddedEvent::class, $storedEvent->event);
$this->assertEquals(1234, $storedEvent->event->amount);
$this->assertEquals($this->account->id, $storedEvent->event->account->id);
}

/** @test */
public function it_will_not_store_events_without_the_ShouldBeStored_interface()
{
Expand Down
13 changes: 13 additions & 0 deletions tests/Models/StoredEventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ public function it_will_throw_a_human_readable_exception_when_the_event_couldnt_
StoredEvent::first()->event;
}

/** @test * */
public function it_will_store_the_alias_when_a_classname_is_found_in_the_event_class_map()
{
$this->setConfig('event-projector.event_class_map', [
'money_added' => MoneyAddedEvent::class,
]);

$this->fireEvents();

$this->assertEquals(MoneyAddedEvent::class, StoredEvent::first()->event_class);
$this->assertEquals('money_added', StoredEvent::first()->getAttributes()['event_class']);
}

public function fireEvents(int $number = 1, string $className = MoneyAddedEvent::class)
{
foreach (range(1, $number) as $i) {
Expand Down