somnambulist / domain-events
Requires
- php: >=7
- ext-bcmath: *
- somnambulist/collection: ~2.1
- somnambulist/value-objects: ~1.3
Requires (Dev)
- doctrine/orm: 2.5.*
- illuminate/events: ~5.4
- nesbot/carbon: 1.22.*
- phpunit/phpunit: ~6.3
- symfony/event-dispatcher: ~3.3
- symfony/var-dumper: ~3.3
- symfony/yaml: ~3.3
Suggests
This package is auto-updated.
Last update: 2020-02-03 17:23:51 UTC
README
此存储库已被存档。请更新到组合包。
PHP 实体域事件
为普通PHP实体添加对域事件的支持。此包受Benjamin Eberlei的Gist和博客文章的启发
要求
- PHP 7+
- bcmath 扩展
安装
使用composer安装,或从github.com检出/拉取文件。
- composer install somnambulist/domain-events
- 对于Doctrine,将Doctrine\DomainEventPublisher注册到您的EntityManagers
- 对于Symfony,注册Symfony\DomainEventPublisher
- 对于Laravel,注册Laravel\DomainEventPublisher
- 实现可以引发事件并刷新/派发它们的实体/对象
- 实现您自己的事件发布者
引发事件
为了引发事件,决定哪些操作应该导致域事件。这些应该与域对象的状态变化相一致,并且事件应该从您的主体实体(聚合根)中产生。
例如:您可能希望在创建新的User实体或为用户添加角色时引发事件。
这确实需要对您通常与实体和Doctrine一起工作的方式做出一些改变,即您应该删除setter和可以为空的构造函数参数。相反,您需要通过特定方法来管理实体中的更改,例如
- completeOrder()
- updatePermissions()
- revokePermissions()
- publishStory()
在内部,更新实体状态后,调用:$this->raise(new NameOfEvent([]))
并将任何您希望传递给监听器的特定参数传递到事件中。这可能包括旧值和新值或整个实体引用,完全取决于您。
public function __construct($id, $name, $another, $createdAt) { $this->id = $id; $this->name = $name; $this->another = $another; $this->createdAt = $createdAt; $this->raise(new MyEntityCreatedEvent(['id' => $id, 'name' => $name, 'another' => $another])); }
通常最好不在构造函数中引发事件,而是使用命名构造函数来创建主要对象
private function __construct($id, $name, $another, $createdAt) { $this->id = $id; $this->name = $name; $this->another = $another; $this->createdAt = $createdAt; $this->raise(new MyEntityCreatedEvent(['id' => $id, 'name' => $name, 'another' => $another])); } public static function create($id, $name, $another) { $entity = new static($id, $name, $another, new DateTime()); $entity->raise(new MyEntityCreatedEvent(['id' => $id, 'name' => $name, 'another' => $another])); return $entity; }
触发域事件
根据您的框架/库选择,提供了一些集成。
Doctrine集成
此实现包括一个Doctrine订阅者,它将监听实现RaisesDomainEvent接口的实体,并确保调用releaseAndResetEvents()
。
-
注意:不需要使用
DomainEventPublisher
订阅者。您可以实现自己的事件分发器,使用完全不同的分发器(框架)并手动触发域事件,通过刷新更改,然后手动调用releaseAndResetEvents
并分发事件。如果您这样做,请注意,聚合根类和主标识符(如果使用)将不会自动设置。如果您打算使用它们,您需要更新代码来设置这些。
要使用包含的监听器,请将其添加到Doctrine配置中的事件订阅者列表中。这是针对每个实体管理器进行的。
- 注意:要使用依赖于Doctrine仓库的领域事件监听器,必须将加载这些订阅者推迟到Doctrine解决之后。
领域事件实例通过一个与Doctrine EventArgs 兼容的层进行代理,该层保留了原始事件和名称。
Symfony集成
包含了一个基本的Symfony EventDispatcher桥接器。您可以使用它与Symfony框架注册触发事件的对象,然后通过调用Symfony\DomainEventPublisher::dispatch()
在任何时候触发发布。所有注册的实体将收集、排序并通过主要的Symfony EventDispatcher分发领域事件。
领域事件通过一个与Symfony兼容的事件代理进行代理,该代理将事件名称自动转换为Symfony点名称(entity.created)。这保留了原始事件、名称和属性。
要监听事件,创建一个监听器并将其注册到主事件分发器,以匹配点表示法的事件。
Laravel集成
Laravel的事件分发器没有类型化事件,因此您不需要对事件进行任何特殊转换。提供了一个集成来注册类似于Symfony集成的触发事件的对象。
class SomeServiceClass { protected $publisher; public function __construct($publisher) { $this->publisher = $publisher; } public function doSomethingComplicated() { $object = new SomeDomainObject(); // or fetch from wherever $this->publisher->publishEventsFrom($object); $object->callSomeMethod(); $object->callSomeOtherMethod(); $object->doSomethingElse(); $this->publisher->dispatch(); } }
这将按顺序通过标准的Laravel事件分发器分发事件。
要监听事件,以通常方式在Laravel项目中绑定您的监听器:请确保使用Event::class
。
Symfony / Laravel自动分发
如果使用独立发布者,可以创建一个onTerminate中间件/内核监听器,在请求结束时触发所有领域事件。
请记住,您需要事先注册将触发事件的对象。
创建领域事件监听器
监听器可以有自己的依赖关系(构造函数未定义),并在onFlush工作单元事件之后被调用。监听器可以执行必要的任何后处理操作,甚至触发更多事件。
监听器应该添加以下命名的
- onNameOfTheEvent
- 方法(不带“event”后缀)
- 该方法将接收领域事件实例
- 领域事件将具有聚合的类和id
单元测试中的示例
class EventListener { public function onMyEntityCreated(MyEntityCreatedEvent $event) { printf( "New item created with id: %s, name: %s, another: %s", $event->getProperty('id'), $event->getProperty('name'), $event->getProperty('another') ); } }
单元测试显示了如何实现。
务必阅读之前提到的Benjamin Eberlei的文章,并查看他的断言库,以实现低依赖实体验证。