You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
4.7KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\behaviors;
  8. use Yii;
  9. use Closure;
  10. use yii\base\Behavior;
  11. use yii\base\Event;
  12. use yii\db\ActiveRecord;
  13. /**
  14. * AttributeBehavior automatically assigns a specified value to one or multiple attributes of an ActiveRecord
  15. * object when certain events happen.
  16. *
  17. * To use AttributeBehavior, configure the [[attributes]] property which should specify the list of attributes
  18. * that need to be updated and the corresponding events that should trigger the update. Then configure the
  19. * [[value]] property with a PHP callable whose return value will be used to assign to the current attribute(s).
  20. * For example,
  21. *
  22. * ```php
  23. * use yii\behaviors\AttributeBehavior;
  24. *
  25. * public function behaviors()
  26. * {
  27. * return [
  28. * [
  29. * 'class' => AttributeBehavior::className(),
  30. * 'attributes' => [
  31. * ActiveRecord::EVENT_BEFORE_INSERT => 'attribute1',
  32. * ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
  33. * ],
  34. * 'value' => function ($event) {
  35. * return 'some value';
  36. * },
  37. * ],
  38. * ];
  39. * }
  40. * ```
  41. *
  42. * Because attribute values will be set automatically by this behavior, they are usually not user input and should therefore
  43. * not be validated, i.e. they should not appear in the [[\yii\base\Model::rules()|rules()]] method of the model.
  44. *
  45. * @author Luciano Baraglia <luciano.baraglia@gmail.com>
  46. * @author Qiang Xue <qiang.xue@gmail.com>
  47. * @since 2.0
  48. */
  49. class AttributeBehavior extends Behavior
  50. {
  51. /**
  52. * @var array list of attributes that are to be automatically filled with the value specified via [[value]].
  53. * The array keys are the ActiveRecord events upon which the attributes are to be updated,
  54. * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
  55. * a single attribute, or an array to represent a list of attributes. For example,
  56. *
  57. * ```php
  58. * [
  59. * ActiveRecord::EVENT_BEFORE_INSERT => ['attribute1', 'attribute2'],
  60. * ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
  61. * ]
  62. * ```
  63. */
  64. public $attributes = [];
  65. /**
  66. * @var mixed the value that will be assigned to the current attributes. This can be an anonymous function,
  67. * callable in array format (e.g. `[$this, 'methodName']`), an [[Expression]] object representing a DB expression
  68. * (e.g. `new Expression('NOW()')`), scalar, string or an arbitrary value. If the former, the return value of the
  69. * function will be assigned to the attributes.
  70. * The signature of the function should be as follows,
  71. *
  72. * ```php
  73. * function ($event)
  74. * {
  75. * // return value will be assigned to the attribute
  76. * }
  77. * ```
  78. */
  79. public $value;
  80. /**
  81. * @var boolean whether to skip this behavior when the `$owner` has not been
  82. * modified
  83. * @since 2.0.8
  84. */
  85. public $skipUpdateOnClean = true;
  86. /**
  87. * @inheritdoc
  88. */
  89. public function events()
  90. {
  91. return array_fill_keys(
  92. array_keys($this->attributes),
  93. 'evaluateAttributes'
  94. );
  95. }
  96. /**
  97. * Evaluates the attribute value and assigns it to the current attributes.
  98. * @param Event $event
  99. */
  100. public function evaluateAttributes($event)
  101. {
  102. if ($this->skipUpdateOnClean
  103. && $event->name == ActiveRecord::EVENT_BEFORE_UPDATE
  104. && empty($this->owner->dirtyAttributes)
  105. ) {
  106. return;
  107. }
  108. if (!empty($this->attributes[$event->name])) {
  109. $attributes = (array) $this->attributes[$event->name];
  110. $value = $this->getValue($event);
  111. foreach ($attributes as $attribute) {
  112. // ignore attribute names which are not string (e.g. when set by TimestampBehavior::updatedAtAttribute)
  113. if (is_string($attribute)) {
  114. $this->owner->$attribute = $value;
  115. }
  116. }
  117. }
  118. }
  119. /**
  120. * Returns the value for the current attributes.
  121. * This method is called by [[evaluateAttributes()]]. Its return value will be assigned
  122. * to the attributes corresponding to the triggering event.
  123. * @param Event $event the event that triggers the current attribute updating.
  124. * @return mixed the attribute value
  125. */
  126. protected function getValue($event)
  127. {
  128. if ($this->value instanceof Closure || is_array($this->value) && is_callable($this->value)) {
  129. return call_user_func($this->value, $event);
  130. }
  131. return $this->value;
  132. }
  133. }