CakePHP中的数据并发管理 - 行为/控制器/模型?

时间:2014-11-30 05:12:10

标签: php mysql concurrency cakephp-2.3

为了管理并发性 - 确保保存到数据库的数据不是陈旧的或已经被其他用户编辑过 - 在我的CakePHP应用程序中,我在编辑函数中使用modified属性。下面是我控制器中的代码片段。

$this->MyModel->recursive = -1;
$event = $this->MyModel->findById($id);
$requestTimeStamp = new DateTime($this->request->data['MyModel']['modified']);
$dbTimeStamp = new DateTime($event['MyModel']['modified']);
if ($requestTimeStamp < $dbTimeStamp) {
    $response = array(
         'success' => false, 
         'id' => $id, 
         'message' => 'A concurrency error occurred while trying to save. Please try again');
    echo json_encode($response);
    exit;
} else {
   //... continue processing
}

此代码工作正常 - 但是当我尝试在我的应用程序中优化它时,我试图找出最适合放置它的位置。它最适合放在我的AppModel课程中,还是最好为同一个创建Behavior,还是最好留在控制器中?我认为理想的选择是考虑性能并最小化类加载开销以及数据库访问开销。

有没有人遇到/解决过这个问题?意见/建议表示赞赏。

1 个答案:

答案 0 :(得分:1)

所以我通过将并发检查作为AppModel->beforeSave()方法的一部分来解决这个问题。以下是其他人参考的代码

/*
 * Incorporated concurrency check in the beforeSave callback method to ensure that data is not stale before user saves.
 * The function checks if the model has a `modified` field, before it proceeds. If the model does not have such a method
 * then concurrency does not apply to this data structure. Upon proceeding, the method checks to see if the value of modified
 * data is the same in the database as well as the request that invokes this method. If they are not same then the save is 
 * aborted
 * This method requires the view or controller to pass a variable called data[ModelName][modified]. 
 * This variable must contain the value of the modified field when the record was read and it must be passed back as such. 
 * I usually set a hidden form field in my view like below - 
 * <input type="hidden" name="data[Model][modified]" value="<?php echo $model['modifed']; ?>" />
 */
    public function beforeSave($options = array()) {
        if ($this->hasField('modified') && isset($this->data[$this->name]['id']) && isset($this->data[$this->name]['modified'])) {
            CakeLog::debug('AppModel: beforeSave - inside concurrency check');
            CakeLog::debug($this->data);
            $this->recursive = -1;

            // run a select statement to ensure the modified date in the database has not changed. If it has changed then 
            // the below find query will return 0 rows
            $row = $this->find('first', array(
                'fields' => array(
                    'id', 'modified'
                ),
                'conditions' => array(
                    'id' => $this->data[$this->name]['id'],
                    'modified' => $this->data[$this->name]['modified']
                )
            ));

            // if no row is returned then error out and return - basically a concurrency error has occurred
            if (!$row) {
                CakeLog::error($this->name.':Concurrency error - [row-id:'.$this->data[$this->name]['id'].']');
                return false;
            }

            // if a row was retrned then there is no concurrency error, so proceed but change the modified date
            // to current timestamp to reflect accuracy
            $this->data[$this->name]['modified'] = date('Y-m-d H:i:s');
            return true;
        }
    }