philippgrashoff/handlerforatk

该软件包的最新版本(5.0.0)没有可用的许可证信息。

5.0.0 2023-11-11 19:49 UTC

This package is auto-updated.

Last update: 2024-09-30 19:33:02 UTC


README

为什么要有这个仓库?

当模型保存时,我需要处理很多操作。我的代码通常是这样的

//after save implementation in a Tour Model
$this->onHook(
     Model::HOOK_AFTER_SAVE,
     function (self $tour) {
        CalendarCacheController::handleTourChange($tour); 
        StockController::handleTourChange($tour);
     }
);

我对这一点真的很不喜欢

  1. 模型本身需要知道在保存/删除时应该发生哪些额外的操作。
  2. 这意味着模型需要处理其他模型的控制流。我喜欢我的模型更“笨”,专注于属性管理、合理的辅助函数以及它们与其他模型的关系。
  3. 由于模型需要调用在插入/更新/删除之后应该发生的所有操作,这也意味着这些额外操作的代码需要位于同一仓库中。这可能会导致一个巨大的单体。我更愿意将更大的应用程序拆分成几个仓库。这强制实施良好的结构化和清晰的API。

这个简单的仓库就是为了改变这些缺点。它是 atk4/data 的一个实现。这个非常棒的框架没有实现MVC模式,但它有非常强大的模型。然而,这个仓库的逻辑可以简单地调整。它是一种单例(也可以通过DI完成)和钩子模式的组合。

这个仓库是如何工作的

模型经纪人有点像MQTT经纪人:模型可以“发布”事件,如after insertafter updateafter delete。其他类可以“订阅”这些事件并对它们作出反应。结果是

  • 模型本身不需要知道在after insert等事件之后执行哪些额外操作。
  • 模型本身不处理这些额外操作的流程控制。
  • 由于这是通过钩子实现的,因此可以从不在模型所在的仓库中添加额外的操作。

该仓库中有两个文件实现了逻辑

  • InvokeModelBrokerTrait:一个要添加到想要发布事件的Model的特质
  • ModelBroker:当注册的事件被调用时,会调用模型经纪人。其他类可以订阅经纪人,以便在这些事件发生时被调用。

如何使用它

  • 您只需要两个方法:InvokeModelBrokerTrait::publish()ModelBroker::subscribe()
  • 每个 Model 都可以在 Model::save() 中调用的任何事件(钩子位置)发布。为了这样做,必须将 InvokeModelBrokerTrait 添加到模型中。如果是这样,只需调用 publish() 方法并告诉该方法要发布哪个钩子位置,例如 $this->publish(Model::HOOK_AFTER_SAVE)
  • 任何其他类现在都可以订阅模型发布的任何事件。为此,它们只需调用 ModelBroker::subscribe() 并告诉该方法要订阅哪个事件以及事件发生时要做什么。
//A model that publishes an event
class SomeModel extends Model
{
    use InvokeModelBrokerTrait;
     
    protected function init(): void 
    {
        $this->publish(Model::HOOK_AFTER_SAVE); //in here we only want to publish the after save spot
    }
    //other init() code like adding fields
}

//this Class wants to act to the after save event
class SomeController 
{
    public static function registerModelBrokerHooks(): void {
        ModelBroker::getInstance()->subscribe(
            Model::HOOK_AFTER_SAVE,
            function (Model $entity, bool $isUpdate) { //the same parameters are available as on Model::HOOK_AFTER_SAVE hook spot
                //some logic that should be performed when the after save event takes place
            }
        );
    }
);

当然,这意味着所有额外操作都需要在模型保存执行之前注册。因此,例如,在 App::init() 中,需要添加一些这样的代码

SomeController::registerModelBrokerHooks();
SomeOtherController::registerModelBrokerHooks();
YetAnotherController::registerModelBrokerHooks();

模型和模型经纪人的耦合

模型经纪人在 Model::save() 的钩子位置被调用。这意味着所有额外操作都将与本身的 save() 在同一事务中。因此,如果其中一个额外操作失败,则整个 save() 将回滚。

多个模型发布相同的事件

如果有多个模型调用publish()发布相同的事件,任何订阅者都会从这些模型接收到这个事件。

class ModelA extends Model 
{
    use InvokeModelBrokerTrait;
    
    protected function init(): void 
    {
        $this->publish(Model::HOOK_AFTER_SAVE);
    }
}

class ModelB extends Model 
{
    use InvokeModelBrokerTrait;
    
    protected function init(): void 
    {
        $this->publish(Model::HOOK_AFTER_SAVE);
    }
}

//inside some other class. This subscription will receive the after save event from both ModelA and ModelB.
ModelBroker::getInstance()->subscribe(
    Model::HOOK_AFTER_SAVE,
    function (Model $entity, bool $isUpdate) {
        //only act on ModelA
        if($entity instanceof ModelA) {
               //do something
        }
    }
);

将来可能会增加在subscribe()中直接过滤事件的功能。

安装

使用此存储库的最简单方法是将其添加到您的composer.json文件中的'require'部分。

{
  "require": {
    "philippgrashoff/atkdatamodelbroker": "5.0.*"
  }
}

版本控制

此存储库的版本号与atk4\data的版本号相对应。因此,5.0.x版本与atk4\data 5.0.x版本兼容,依此类推。