为什么 Codeigniter 4 交易失败,但在数据库中插入了一些数据?

Why Codeigniter 4 failed the transaction, but insert some data in DB?

请帮忙!我无法完成交易成功。

当我传入表单错误数据并发送表单时,我的事务开始,第一个 table 被跳过,因为验证失败,但事务继续工作并且一些数据插入第二和第三 table秒。为什么会这样?

我正在验证模型中的数据,这里是我的代码

namespace App\Models;

use CodeIgniter\Model;

class Users extends Model
{
    protected $DBGroup              = 'default';
    protected $table                = 'users';
    protected $primaryKey           = 'user_id';
    protected $useAutoIncrement     = true;
    protected $insertID             = 0;
    protected $returnType           = 'array';
    protected $useSoftDelete        = false;
    protected $protectFields        = true;

    protected $allowedFields        = [
        'full_name', 'phone_user', 'inn_user', 'hospital_code', 'check_priz', 'winner', 'banned',
    ];

    // Dates
    protected $useTimestamps        = true;
    protected $dateFormat           = 'datetime';
    protected $createdField         = 'created_at';
    protected $updatedField         = 'updated_at';

    protected $validationRules      = [
        'full_name'     => 'required|min_length[3]|max_length[255]',
        'phone_user'    => 'required|is_unique[users.phone_user]|min_length[11]|max_length[11]|regex_match[/^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$/]|numeric',
        'inn_user'      => 'required|numeric|is_unique[users.inn_user]|min_length[12]|max_length[12]',
        'hospital_code' => 'integer|max_length[5]'
    ];
    protected $validationMessages   = [];
    protected $skipValidation       = false;

}

这是我的交易功能


public function addNewUserAndCode($data)
    {
        $userModel = new UserModel;
        $CodeModel = new CodeModel;
        $CodeUserModel = new CodeUserModel;

        $db = \Config\Database::connect();
        $db->transBegin();

        //create user
        $id = $userModel->insert($data);

        //add code to user
        $promoCodeUserModel->insert([
            'user_id' => $id,
            'code' => $data['code']
        ]);

        //check used code
        $promoCodeModel->update($data['code'], ['used' => 1]);

        $db->transComplete();
        if ($db->transStatus() === FALSE) {
            $db->transRollback();
            log_message('debug', 'fail transaction');
            return $this->fail('Failed add user.', 400);
        } else {
            $db->transCommit();
            log_message('debug', 'success transaction');
        }
    }

对不起我的英语。 :) 非常感谢!)

你好 Kiril,欢迎来到 Whosebug

数据库上下文中事务的主要目的是在 DBMS 级别或以下级别发生意外情况时保持数据完整性,包括(但不限于)以下情况:

  • 入库期间停电
  • 硬件组件故障
  • 承载 DBMS 的操作系统崩溃或死机,或者 DBMS 本身已经崩溃

在您的案例中,“部分数据承诺”的原因是因为:

  1. 您使用insert()方法插入数据
  2. 该方法针对指定的 $validationRules
  3. 进行验证
  4. 验证过程在任何东西到达 DBMS 之前就失败了

所以在 DBMS 级别上绝对没有问题,因为那里什么都没有发生,为什么事务会失败?

insert()方法returnsFALSE如果由于某种原因插入失败,可以用这个方法解决

由于您是 运行 手动交易,您需要做的就是另外 CHECK IF $id EQUALS FALSE,所以条件变成:

if ($db->transStatus() === FALSE || $id === FALSE) {
    $db->transRollback();
    log_message('debug', 'fail transaction');
    return $this->fail('Failed add user.', 400);
} else {
    $db->transCommit();
    log_message('debug', 'success transaction');
}

另一个建议的解决方案是在控制器内部进行验证并检查验证过程是否失败,甚至不启动事务:

$validationRules = [
    'full_name'     => 'required|min_length[3]|max_length[255]',
    'phone_user'    => 'required|is_unique[users.phone_user]|min_length[11]|max_length[11]|regex_match[/^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$/]|numeric',
    'inn_user'      => 'required|numeric|is_unique[users.inn_user]|min_length[12]|max_length[12]',
    'hospital_code' => 'integer|max_length[5]'
];

$validator = \Config\Services::validator(); // Obtain an instance of the validator
$validator->setRules($validationRules); // Bind the validation rules to the validator
$isValid = $validator->withRequest($this->request)->run(); // Read the input from HTTP request and validate it against the rules, then check if they have a valid form

if ($isValid) {
    // Ok, you're good, you can now start the transaction here
    // Write your logic here ...
    if ($db->transStatus() === FALSE) {
        $db->transRollback(); // Restore the database to its original state, because a transaction should preserve data integrity whether it has failed or succeeded
        // Send HTTP 500 Internal Server Error response
    }
    else {
        // Everything went fine, you can peacefully commit
        $db->transCommit();
    }
}
else {
    // Malformed input, send HTTP 400 Bad Request response
}

有关验证的更多信息可在此处找到:http://codeigniter.com/user_guide/libraries/validation.html