【问题标题】:How to create transactions in PHP Active Record when using Models?使用模型时如何在 PHP Active Record 中创建事务?
【发布时间】:2014-07-10 14:36:31
【问题描述】:

我正在开发一个使用 PHP Active Record 的项目,并且对它很陌生,我真的开始喜欢它了。所有对象都是 ActiveRecord 模型的扩展

class User extends Model {

}

class Item extends Model {

}
.
.
.

现在我的代码将具有与此类似的内容(users 表包含属于用户的项目的计数,并且 items 表包含项目)。

    $user = User::find_by_id($id);
    $user->num_items++;
    $item = new Item();
    //Do stuff with the item
    $user->save();
    $item->save();

现在我担心理论上的可能性,即当网站受到重击时,某些东西会损坏或数据库被拆除以进行维护(是的,这些事情与我有关)。由于理论上这些语句不在事务中:

    $user->save();
    //User table is updated successfully
    //----- MySQL goes Down Here -------/
    $item->save(); //This won't run, now the user's table may say he has 4 items but he only has 3 since the last one didn't get inserted

我希望能够将用户和项目一起保存,如果其中一个失败,它们会回滚。我知道如何在手动 MySQL 中执行此操作,但这违背了使用 ORM 的目的。

【问题讨论】:

    标签: php mysql activerecord transactions


    【解决方案1】:

    也许它对仍在使用这个库的人有用。 您的每个模型中都有事务方法

    public static function transaction($closure)
    

    它位于 \ActiveRecord\Model 命名空间中(它是您项目模型的父类)

    您可以使用此公共静态方法通过闭包运行您的事务查询。 示例:

    $user = User::find_by_id($id);
    $item = new Item();
    
    $entity::transaction(function() use ($user, $item) {
          //Do stuff with the items...
           $user->name = 'somename';
           $user->save();
           $item->quantity = 0;
           $item->save();
       });
    

    上述事务静态方法使用模型中的连接实例,如果每个模型都有相同的连接,则不会有任何问题。例如,我的所有模型只有一个连接

    \ActiveRecord\Config::initialize(function ($cfg) use ($hostname, $username, $password, $database, $port) {
                $cfg->set_model_directory('/src/Entity');
                $cfg->set_connections(
                    array(
                        'development' => 'mysql://' . $username . ':' . $password . '@' . $hostname . '/' . $database
                    )
                );
            });
    

    如果你有一些内部验证等,你可以抛出 \Exception 或从你的闭包中返回 false 并且事务将被回滚

      YourModel::transaction(function()
          {
            YourModel::create(array("name" => "blah"));
            throw new Exception("rollback!");
          });
    
          YourModel::transaction(function()
          {
            YourModel::create(array("name" => "blah"));
            return false; # rollback!
          });
    

    您还可以获得连接实例并几乎像往常一样开始交易

      $user = User::find_by_id($id);
       $item = new Item();
       $connection = ConnectionManager::get_connection();
       $connection->transaction();
       try{
           //Do stuff with the items...
           $user->name = 'somename';
           $user->save();
           $item->quantity = 0;
           $item->save();
           $connection->commit();
       }
       catch (\Exception $exception){
           throw $exception;
           $connection->rollback();
       }
    

    ps:这里也有一些例子http://www.phpactiverecord.org/docs/ActiveRecord/Model#methodtransaction

    【讨论】:

    • 你说“仍在使用这个库”。它出什么问题了?你推荐什么替代品?
    • 不确定我能推荐你什么。这是圣战主题(搜索“活动记录与数据映射器模式”)。我正在使用使用数据映射器模式的教义2(phpactiverecord 使用活动记录模式)。 Doctrine2 很重,但对我们来说没关系。
    【解决方案2】:

    这种代码应该可以解决问题,条件是您的 save() 函数实现了 mysql_query。 由您将其封装在一个漂亮的接口中,以便那些丑陋的 mysql_query 也由您的活动记录框架处理

        mysql_query("SET AUTOCOMMIT=0");
        mysql_query("START TRANSACTION");
    
        $a1=$user->save();
        $a2=$item->save();
    
        if ($a1 and $a2) {
            mysql_query("COMMIT");
        } else {        
            mysql_query("ROLLBACK");
        }
    

    【讨论】:

    • 虽然这似乎是一个 hack,但在一个非常广泛使用的 ORM 中没有这样的功能吗?我还认为旧的 MySQL 连接器已被删除(或至少在最新的 PHP 中已弃用)。我认为 ActiveRecord 在内部使用 MySQLI 或 PDO(或者至少我希望 :))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    • 1970-01-01
    • 2013-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多