源码Illuminate\Database\Eloquent\Model中bootTraits方法可以自定义模型启动后做一些处理

  1. protected static function bootTraits()
  2. {
  3. $class = static::class;
  4. $booted = [];
  5. static::$traitInitializers[$class] = [];
  6. foreach (class_uses_recursive($class) as $trait) {
  7. $method = 'boot'.class_basename($trait);//关键语句
  8. if (method_exists($class, $method) && ! in_array($method, $booted)) {
  9. forward_static_call([$class, $method]);
  10. $booted[] = $method;
  11. }
  12. if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
  13. static::$traitInitializers[$class][] = $method;
  14. static::$traitInitializers[$class] = array_unique(
  15. static::$traitInitializers[$class]
  16. );
  17. }
  18. }
  19. }

实现步骤

一 注册模型启动任务

  1. trait FieldLogTrait
  2. {
  3. public static function bootFieldLogTrait()
  4. {
  5. static::saved(function ($model) {
  6. $_model = clone $model;
  7. $_model->relations = [];
  8. FieldLog::addEvent(function () use ($_model) {
  9. return $_model->changedRevisionableFields();
  10. });
  11. });
  12. static::deleted(function ($model) {
  13. $_model = clone $model;
  14. $_model->relations = [];
  15. FieldLog::addEvent(function () use ($_model) {
  16. return $_model->changedRevisionableFields();
  17. });
  18. });
  19. }
  20. private function changedRevisionableFields()
  21. {
  22. $changes_to_record = [];
  23. $dirtyData = $this->getDirty();
  24. $originalData = $this->original;
  25. $updatedData = $this->attributes;
  26. $table = $this->getTable();
  27. $row_id = $this->getKey();
  28. foreach ($dirtyData as $key => $value) {
  29. if (!isset($originalData[$key]) || $originalData[$key] != $updatedData[$key]) {
  30. $old_value = array_get($originalData, $key);
  31. $changes_to_record[] = [
  32. 'table' => $table,
  33. 'row_id' => $row_id,
  34. 'key' => $key,
  35. 'old_value' => !empty($old_value) ? $old_value : '',
  36. 'new_value' => $updatedData[$key],
  37. ];
  38. }
  39. }
  40. return $changes_to_record;
  41. }

二 字段变动收集

  1. <?php
  2. namespace App\Foundation\FieldLog;
  3. use App\Foundation\RequestID\RequestID;
  4. use App\Foundation\RequestID\ResID;
  5. use Carbon\Carbon;
  6. use Illuminate\Support\Facades\App;
  7. use Illuminate\Support\Facades\DB;
  8. use Illuminate\Support\Facades\Log;
  9. use function MongoDB\select_server;
  10. class FieldLogHandler
  11. {
  12. /**
  13. * 是否保存
  14. * @var bool
  15. */
  16. public $canStore = true;
  17. /**
  18. * 保存请求之后执行的动作
  19. * @var array
  20. */
  21. public $events = [];
  22. /**
  23. * 保存修改记录
  24. */
  25. public function store()
  26. {
  27. if ($this->canStore) {
  28. try {
  29. if (!empty($this->events)) {
  30. $data = [];
  31. //执行并合并所有结果
  32. foreach ($this->events as $event) {
  33. $results = call_user_func($event);
  34. if (!empty($results)) {
  35. foreach ($results as $result) {
  36. $data[] = $result;
  37. }
  38. }
  39. }
  40. //保存结果
  41. if (!empty($data)) {
  42. if (count($data) > 20) {
  43. //防止insert过长
  44. $lists = array_chunk($data, 20);
  45. foreach ($lists as $list) {
  46. $this->insert($list);
  47. }
  48. } else {
  49. $this->insert($data);
  50. }
  51. }
  52. }
  53. } catch (\Exception $exception) {
  54. }
  55. }
  56. }
  57. /**
  58. * 业务完成之后执行的回调
  59. * @param callable $fn
  60. */
  61. public function addEvent(callable $fn)
  62. {
  63. $this->events[] = $fn;
  64. }
  65. /**
  66. * 跳过持久化
  67. */
  68. public function skip()
  69. {
  70. $this->canStore = false;
  71. }
  72. public function insert($data)
  73. {
  74. if (!empty($data)) {
  75. $time = Carbon::now()->toDateTimeString();
  76. $requestId = ResID::get();
  77. foreach ($data as $key => $item) {
  78. $data[$key]['request_id'] = $requestId;
  79. $data[$key]['created_at'] = $time;
  80. $data[$key] = array_merge($data[$key], $this->extra);
  81. }
  82. event(new FieldChangeEvent($data));
  83. }
  84. }
  85. }

三 持久化

创建事件监听器处理FieldChangeEvent事件即可