Yii2 自带事件的用法

技巧库 · forecho · 于 7年前 发布 · 17428 次阅读

Model 层

保存之前的事件示例

public function beforeSave($insert)
{
    if (parent::beforeSave($insert)) {
        // 插入新数据判断订单号是否存在
        if (!Order::findModel(['trade_no' => $this->order_trade_no])) {
            throw new Exception("订单号不存在");
        }
        return true;
    } else {
        return false;
    }
}

保存之后的事件示例

public function afterSave($insert, $changedAttributes)
{
    parent::afterSave($insert, $changedAttributes);
    if ($insert) {
        // 插入新数据之后修改订单状态
        Order::updateAll(['shipping_status' => Order::SHIPPING_STATUS1, 'shipping_at' => time()], ['trade_no' => $this->order_trade_no]);
    }
}

删除之后的事件示例

public function afterDelete()
{
    parent::afterDelete();
}

Model 事件怎么保证数据事务呢?

添加一下代码在 Model 中:

public function transactions()
{
    return [
        self::SCENARIO_DEFAULT => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE
        // self::SCENARIO_DEFAULT => self::OP_INSERT
    ];
}

Controller 层

每次请求之前操作示例

/**
 * @param \yii\base\Action $action
 * @return bool
 * @throws \yii\web\BadRequestHttpException
 */
public function beforeAction($action)
{
    if (parent::beforeAction($action)) {
        $this->request = Yii::$app->request;
        Yii::info($this->request->absoluteUrl, '请求地址');
        Yii::info($this->request->rawBody, '请求数据');
        return true;
    } else {
        return false;
    }
}

每次请求之后操作示例

/**
 * @param \yii\base\Action $action
 * @param mixed $result
 * @return array|mixed
 * @throws BusinessException
 */
public function afterAction($action, $result)
{
    Yii::info(\yii\helpers\Json::encode($result), '请求返回结果');
    return $result;
}

删除之前的操作

public function beforeDelete()
{
    if (Business::find()->where(['louma_id' => $this->id])->count('id')) {
        Yii::$app->session->setFlash('error', '请先删除此楼宇的商户');
        return false;
    }
    return parent::beforeDelete();
}

待续……

本文由 forecho 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。


如果这篇文章对您有帮助,不妨微信小额赞助我一下,让我有动力继续写出高质量的教程。

本帖已被设为精华帖!
共收到 19 条回复 事务 事件 Yii2 事件 Yii2高级用法
Kittyfamous#17年前 0 个赞

forecho真是yii2专家。

zxx1988328#27年前 0 个赞

期待后续

fecommerce#37年前 0 个赞

在楼主的神武文档下面,补充一下自己写一个event的方法步骤:

1.Yii2 使用Event -1 ,如何使用事件:

http://www.fancyecommerce.com/2016/04/29/yii2-使用event-1-,如何使用事件/

2.Yii2 使用Event -2 ,如何使用事件:

http://www.fancyecommerce.com/2016/04/29/yii2-使用event-2-,如何使用事件/

forecho#47年前 0 个赞

@zxx1988328 #2楼 已更新

yidashi#57年前 0 个赞

这个不叫事件吧。

fecommerce#67年前 0 个赞

@yidashi #5楼 和Event 是不一样,这种before after 通过重写,大致实现的结果 和 事件 的作用差不多。

ilvsx#77年前 1 个赞

这个应该是装饰器模式,作用是可以动态的添加和修改功能(其实只是看起来比较像装饰器模式,无视无视……)

事件的话,也就是观察者模式,触发一个事件后通知所有观察这个事件的观察者,观察者可以是多个。比如当保存一个 Model 的时候,触发 saving 事件,这个时候就可以编写一个保存日志和一个更新缓存的观察者,以后只要保存 Model ,就可以把日志记录下来,同时更新缓存

xjdata#87年前 0 个赞

@ilvsx #7楼

请教. 能否将你描述的, Model 的时候,触发 saving 事件,这个时候就可以编写一个保存日志和一个更新缓存的观察者,以后只要保存 Model ,就可以把日志记录下来,同时更新缓存 给一个实际的例子呢? 非常需要,但是很菜.... 谢谢了.

ilvsx#97年前 1 个赞
<?php

abstract class Model
{

  private $event;

  public function __construct(Event $event)
  {
    $this->event = $event;
  }

  public function save()
  {
    $this->event->notify('saving', $this);
  }

}

class User extends Model
{
}

class Blog extends Model
{
}

class Event
{
  private $events;

  public function addObserver($name, IObserver $observer){
    $this->events[$name][] = $observer;
  }

  public function notify($name, $model){
    echo get_class($model) . PHP_EOL;
    foreach ($this->events[$name] as $observer) {
      $observer->doSomething($model);
    }
  }
}

interface IObserver{
  public function doSomething(Model $model);
}

class LogObserver implements IObserver{
  public function doSomething(Model $model){
    echo 'Log...' . PHP_EOL;
  }
}

class CacheObserver implements IObserver{
  public function doSomething(Model $model){
    echo 'Cache...' . PHP_EOL;
  }
}


$event = new Event();
$event->addObserver('saving', new LogObserver());
$event->addObserver('saving', new CacheObserver());

$user = new User($event);
$user->save();
$blog = new Blog($event);
$blog->save();

执行结果:

User
Log...
Cache...
Blog
Log...
Cache...

@xjdata [#8楼](#comment8) 这是可以满足那段描述的一个实现。 别看那么长…… 其实就是 Model 里的 save() 方法触发 saving 事件,然后 Event 里依次通知(执行)注册(添加)好的观察者LogObserverCacheObserver

xjdata#107年前 0 个赞

@ilvsx #9楼 大概看明白了. 我还需要在实际中多使用下,才能理解你的这些代码.

主要还是设计模式. 这个观察者模式,在我接触的应用中很是需要, 比如很多时候应用需要记录很多操作的 每一步详细日志. 以便之后确认责任.

之前我也是使用了yii2的 事件. 但是因为自己很菜, 所以总是感觉 实现的没有那么干净利索. 多谢.

xjdata#117年前 0 个赞

@ilvsx #9楼 请问, 你在代码里实现了一个 抽象类, user blog 都继承了. 但是我在实际使用中 model基本上都是继承自yii2的ar的. 我需要怎样才能将你的观察者加入到自己的代码中呢? 谢谢.

ilvsx#127年前 0 个赞

我翻了下文档…… 发现楼主这个确实是 Yii 的事件的正确使用姿势…… 好尴尬啊…… 没看文档没看源码就直接回答了…… 只是看起来有点像装饰器模式…… 怎么办…… 好尴尬……

http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#beforeSave()-detail
这里的 beforeSaveafterSave 其实就是触发的 EVENT_BEFORE_INSERTEVENT_AFTER_INSERT 事件。


@xjdata [[#11楼](#comment11)](#comment11) yii2 的 ActiveRecord 已经提前预制好了 ⑨ 个事件,如下:

http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#EVENT_AFTER_DELETE-detail

你只需要添加对应的处理就可以了:
http://www.getyii.com/doc-2.0/guide/concept-events.html#

看这个文档的 类级别的事件处理器 部分,那个例子应该就是你想要的了……

xjdata#137年前 0 个赞

@ilvsx #12楼 晕,,,, 有啥尴尬的, 倒是楼主非常不错啊. 不论如何非常感谢你.

forecho#146年前 0 个赞

补充删除之前事件

public function beforeDelete()
{
    if (parent::beforeDelete()) {
        // something code
        return true;
    } else {
        return false;
    }
}
fecommerce#156年前 1 个赞

有很多模式,是为了做产品用的。 譬如事件,可以非常方便做扩展,因为可以在不改动原来代码的前提下插入代码。 如果系统做了是给自己公司用的,事件没啥作用,直接去那个地方改代码就行了,多高效。

fecommerce#166年前 0 个赞

哎,玩php的转产品经理吧。:triumph:

forecho#176年前 0 个赞

@fecommerce #16楼 为啥这么说?

forecho#185年前 0 个赞
public function beforeValidate()
{
    if (parent::beforeValidate()) {
        return true;
    }
    return false;
}
添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册