TP6的事件系统和模型事件

以前用过hook吧,钩子的作用和事件是一个性质的,你可以把事件当作一个闭包或者匿名函数来使用,就能得到自己想要的结果了。

闭包函数和匿名函数的作用域属于自身,通过反射可以bindTo当前的方法或者类,进而获取其作用域。

事件event方法就是将before_select事件类绑定一个闭包函数,当触发器调用before_select时也就执行了闭包函数,闭包内部使用return返回的时当前作用域内的结果。也就是执行before_select就是在执行闭包函数function(){},其返回值获得了调用event类的那个类的作用域,按照框架的设计,是获得了当前调用模型类的作用域。

如果上边我将的太生涩了,都是文字能力太差的原因,建议你再看一下闭包函数和其作用域,另外模型事件的定义是可以参照源代码,找到trigger('before_select)(一般都是用动态调用的方式,你要跑一下xdebug)你就找到你要的结果了。

另外,再啰嗦一下框架的Model加载,前后叙述省略一些和你提出的问题不大的过程,

首先,Model是通过App容器内make()实例后,经过Service的register()注册成ModelService被加载进框架的,查看app.php的initialize()最后的foreach就看到了。

        date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));

        // 初始化

        foreach ($this->initializers as $initializer) {

            $this->make($initializer)->init($this);

        }

        return $this;

    }

    protected $services = [

        PaginatorService::class,

        ValidateService::class,

        ModelService::class,

    ];

    public function init(App $app)

    {

        $file = $app->getRootPath() . 'vendor/services.php';

        $services = $this->services;

        if (is_file($file)) {

            $services = array_merge($services, include $file);

        }

        foreach ($services as $service) {

            if (class_exists($service)) {

                $app->register($service);

            }

        }

    }

其次,Model模型是在ModelService内执行boot()时被初始化的。

    public function boot()

    {

        Model::setDb($this->app->db);

        Model::setEvent($this->app->event);

        Model::setInvoker([$this->app, 'invoke']);

        Model::maker(function (Model $model) {

            $config = $this->app->config;

            $isAutoWriteTimestamp = $model->getAutoWriteTimestamp();

            if (is_null($isAutoWriteTimestamp)) {

                // 自动写入时间戳

                $model->isAutoWriteTimestamp($config->get('database.auto_timestamp', 'timestamp'));

            }

            $dateFormat = $model->getDateFormat();

            if (is_null($dateFormat)) {

                // 设置时间戳格式

                $model->setDateFormat($config->get('database.datetime_format', 'Y-m-d H:i:s'));

            }

        });

    }

你可以看到通过静态调用方法setEvent()将Event对象(也是整个框架底层结构的事件系统)加载进来,其中当然就包括模型事件,因为模型事件在整个模型系统中只是一个子集(看作一个属性)。

在整个框架内部的任意位置你随时都可以执行Db::event()或模型内部静态调用setEvent(),不过模型内部的静态调用setEvent()创建的任务是独立于框架系统事件任务的,但是框架系统事件可以随时调用它,具体内容自行看trait ModelEvent类这里不赘述了。

明白了以上的流程和逻辑关系之后,再回头看你自己对模型事件的自定义内容,事件系统本身就是通过绑定的方式进行关联。对于模型事件来说,也不例外

    public function event(string $event, callable $callback): void

    {

        $this->event[$event][] = $callback;

    }

这也就是在模型事件trigger()触发$event是,会执行一个$callback,如果你没有return就不存在任何返回值,只是执行了函数体,函数体也是你所有的预期操作和执行,而加上return只有一种情况,就是不执行当前事件之后的事件了,给一个false就可以。对于事件$event参与的执行,无法给你任何的反馈,你只是进行了一次hook而已。

另外附加一下trigger()的调用代码

    public function trigger(string $event, $params = null)

    {

        if (isset($this->event[$event])) {

            foreach ($this->event[$event] as $callback) {

                call_user_func_array($callback, [$this]);

            }

        }

    }

以上内容没有进行二次校对,错别字和语病请担待。

补充一句,看trigger()里的if判断,就是你不理解的return值,给了false后边的循环就不进行了。注意,这里用的是isset判断!!!只要定义过模型事件,isset就是true,模型事件内没有return和return任何除false以外的值一样可以执行,即使你return 0;都没关系。

$this->event[$event]就是将所有模型事件的类型存到数组中的索引中,而模型事件的闭包结果存在$this->event[$event][]中,后边的循环就是遍历整个数组,完成所有事件的执行。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1.JQuery 基础 改变web开发人员创造搞交互性界面的方式。设计者无需花费时间纠缠JS复杂的高级特性。 1....
    LaBaby_阅读 4,930评论 0 1
  • 1.JQuery 基础 改变web开发人员创造搞交互性界面的方式。设计者无需花费时间纠缠JS复杂的高级特性。 1....
    LaBaby_阅读 5,278评论 0 2
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 10,191评论 0 5
  • JavaScript语言精粹 前言 约定:=> 表示参考相关文章或书籍; JS是JavaScript的缩写。 本书...
    微笑的AK47阅读 3,690评论 0 3
  • Laravel框架一:原理机制篇 Laravel作为在国内国外都颇为流行的PHP框架,风格优雅,其拥有自己的一些特...
    Mr_Z_Heng阅读 9,242评论 0 13

友情链接更多精彩内容