使用Models时如何在PHP Active Record中创建事务?

I'm working on a project using PHP Active Record and am new to it and am really starting to love it. All of the objects are extensions of ActiveRecord's model

class User extends Model {

}

class Item extends Model {

}
.
.
.

Now my code will have something similar to this (The users table contains the count of the items that belong to the user and the items table contains, well the items).

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

Now I'm worried about the theoretical off chance that while the site is getting hammered something breaks or the DB is taken down for maintenance (Yes these things concern me). Since the statements are not in a transaction theoretically:

    $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

I want to be able to save the user and the item together, if one fails they roll back. I know how to do this in manual MySQL but that defeats the purpose of using an ORM.

That kind of code should do the trick, at the condition that your save() function implements a mysql_query. Up to you to encapsulate this in a nice interface, so that those ugly mysql_querys are also handled by your active record framework

    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");
    }

Maybe it would be useful for somebody who still using this library. There is transaction method in each of your models

public static function transaction($closure)

it is located in \ActiveRecord\Model namespace (it is the parent class of your project models)

You can use this public static method to run your transaction query using closure. example:

$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();
   });

Above transaction static method uses instance of connection from your model and if you have the same connection for each model you will not have any problems. for example I have only one connection for all of my models

\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
                )
            );
        });

If you have some internal validation etc you can throw \Exception or return false from your closure and transaction will be rollbacked

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

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

You can also get instance of connection and begin your transactions almost as usual

  $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: there is also some examples here http://www.phpactiverecord.org/docs/ActiveRecord/Model#methodtransaction