yii2

Форк
1
/
OptimisticLockBehavior.php 
171 строка · 6.1 Кб
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7

8
namespace yii\behaviors;
9

10
use Yii;
11
use yii\db\BaseActiveRecord;
12
use yii\base\InvalidCallException;
13
use yii\validators\NumberValidator;
14
use yii\helpers\ArrayHelper;
15

16
/**
17
 * OptimisticLockBehavior automatically upgrades a model's lock version using the column name
18
 * returned by [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]].
19
 *
20
 * Optimistic locking allows multiple users to access the same record for edits and avoids
21
 * potential conflicts. In case when a user attempts to save the record upon some staled data
22
 * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,
23
 * and the update or deletion is skipped.
24
 *
25
 * To use this behavior, first enable optimistic lock by following the steps listed in
26
 * [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]], remove the column name
27
 * holding the lock version from the [[\yii\base\Model::rules()|rules()]] method of your
28
 * ActiveRecord class, then add the following code to it:
29
 *
30
 * ```php
31
 * use yii\behaviors\OptimisticLockBehavior;
32
 *
33
 * public function behaviors()
34
 * {
35
 *     return [
36
 *         OptimisticLockBehavior::class,
37
 *     ];
38
 * }
39
 * ```
40
 *
41
 * By default, OptimisticLockBehavior will use [[\yii\web\Request::getBodyParam()|getBodyParam()]] to parse
42
 * the submitted value or set it to 0 on any fail. That means a request not holding the version attribute
43
 * may achieve a first successful update to entity, but starting from there any further try should fail
44
 * unless the request is holding the expected version number.
45
 *
46
 * Once attached, internal use of the model class should also fail to save the record if the version number
47
 * isn't held by [[\yii\web\Request::getBodyParam()|getBodyParam()]]. It may be useful to extend your model class,
48
 * enable optimistic lock in parent class by overriding [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]],
49
 * then attach the behavior to the child class so you can tie the parent model to internal use while linking the child model
50
 * holding this behavior to the controllers responsible of receiving end user inputs.
51
 * Alternatively, you can also configure the [[value]] property with a PHP callable to implement a different logic.
52
 *
53
 * OptimisticLockBehavior also provides a method named [[upgrade()]] that increases a model's
54
 * version by one, that may be useful when you need to mark an entity as stale among connected clients
55
 * and avoid any change to it until they load it again:
56
 *
57
 * ```php
58
 * $model->upgrade();
59
 * ```
60
 *
61
 * @author Salem Ouerdani <tunecino@gmail.com>
62
 * @since 2.0.16
63
 * @see \yii\db\BaseActiveRecord::optimisticLock() for details on how to enable optimistic lock.
64
 */
65
class OptimisticLockBehavior extends AttributeBehavior
66
{
67
    /**
68
     * {@inheritdoc}
69
     *
70
     * In case of `null` value it will be directly parsed from [[\yii\web\Request::getBodyParam()|getBodyParam()]] or set to 0.
71
     */
72
    public $value;
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public $skipUpdateOnClean = false;
77

78
    /**
79
     * @var string the attribute name holding the version value.
80
     */
81
    private $_lockAttribute;
82

83

84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function attach($owner)
88
    {
89
        parent::attach($owner);
90

91
        if (empty($this->attributes)) {
92
            $lock = $this->getLockAttribute();
93
            $this->attributes = array_fill_keys(array_keys($this->events()), $lock);
94
        }
95
    }
96

97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function events()
101
    {
102
        return Yii::$app->request instanceof \yii\web\Request ? [
103
            BaseActiveRecord::EVENT_BEFORE_INSERT => 'evaluateAttributes',
104
            BaseActiveRecord::EVENT_BEFORE_UPDATE => 'evaluateAttributes',
105
            BaseActiveRecord::EVENT_BEFORE_DELETE => 'evaluateAttributes',
106
        ] : [];
107
    }
108

109
    /**
110
     * Returns the column name to hold the version value as defined in [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]].
111
     * @return string the property name.
112
     * @throws InvalidCallException if [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]] is not properly configured.
113
     * @since 2.0.16
114
     */
115
    protected function getLockAttribute()
116
    {
117
        if ($this->_lockAttribute) {
118
            return $this->_lockAttribute;
119
        }
120

121
        /* @var $owner BaseActiveRecord */
122
        $owner = $this->owner;
123
        $lock = $owner->optimisticLock();
124
        if ($lock === null || $owner->hasAttribute($lock) === false) {
125
            throw new InvalidCallException("Unable to get the optimistic lock attribute. Probably 'optimisticLock()' method is misconfigured.");
126
        }
127
        $this->_lockAttribute = $lock;
128
        return $lock;
129
    }
130

131
    /**
132
     * {@inheritdoc}
133
     *
134
     * In case of `null`, value will be parsed from [[\yii\web\Request::getBodyParam()|getBodyParam()]] or set to 0.
135
     */
136
    protected function getValue($event)
137
    {
138
        if ($this->value === null) {
139
            $request = Yii::$app->getRequest();
140
            $lock = $this->getLockAttribute();
141
            $formName = $this->owner->formName();
142
            $formValue = $formName ? ArrayHelper::getValue($request->getBodyParams(), $formName . '.' . $lock) : null;
143
            $input = $formValue ?: $request->getBodyParam($lock);
144
            $isValid = $input && (new NumberValidator())->validate($input);
145
            return $isValid ? $input : 0;
146
        }
147

148
        return parent::getValue($event);
149
    }
150

151
    /**
152
     * Upgrades the version value by one and stores it to database.
153
     *
154
     * ```php
155
     * $model->upgrade();
156
     * ```
157
     * @throws InvalidCallException if owner is a new record.
158
     * @since 2.0.16
159
     */
160
    public function upgrade()
161
    {
162
        /* @var $owner BaseActiveRecord */
163
        $owner = $this->owner;
164
        if ($owner->getIsNewRecord()) {
165
            throw new InvalidCallException('Upgrading the model version is not possible on a new record.');
166
        }
167
        $lock = $this->getLockAttribute();
168
        $version = $owner->$lock ?: 0;
169
        $owner->updateAttributes([$lock => $version + 1]);
170
    }
171
}
172

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.