AttributeBehavior.php 5.0 KB

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