Phalcon: транзакции и сложные модели

В моем проекте у меня есть таблица entities, в которой должны существовать все сущности (для поддержки сложных внешних ключей), поэтому мне нужно вставить дополнительную строку в специальную таблицу (в этот список сущностей), прежде чем вставлять строку в таблицу моей модели, и я хотел спросите, как лучше всего это сделать.

Итак, для следующего кода мне нужно вставить две строки: в таблицу entity, затем взять идентификатор только что вставленной строки, сохранить его в текущей модели и вставить в таблицу accounts:

$account = new Account();
$account->name = 'John';
$account->save(); // performs two inserts while creating model

Как я понял, я могу использовать метод beforeCreate() для вставки строки в таблицу entity и получения идентификатора для вновь созданной строки, что-то вроде этого:

class Account
{
    public function beforeSave()
    {
        $entity = new \Entity();
        $entity->type = get_class($this);
        $entity->save();
        $this->id = $entity->id;
    }
}

Но таким образом, если каким-либо образом строка счета не будет вставлена, строка в таблице entity будет существовать.

Затем я решил использовать транзакции, как показано в документах здесь http://docs.phalconphp.com/en/latest/reference/models.html#transactions

Но я не понимаю, если у меня есть небольшие транзакции для каждого метода model::create(), как это будет работать, когда мне понадобятся транзакции для сложных операций?

e.g.

// controller action context
use Phalcon\Mvc\Model\Transaction\Manager as TxManager,
    Phalcon\Mvc\Model\Transaction\Failed as TxFailed;

try {

    //Create a transaction manager
    $manager = new TxManager();

    // Request a transaction
    $transaction = $manager->get();

    $account = new Account();
    $account->setTransaction($transaction);
    $account->name = "WALL·E";
    $account->created_at = date("Y-m-d");
    if ($account->save() == false) { // sub-transaction inside account::create() method
        $transaction->rollback("Cannot save account");
    }

    $accountPart = new AccountPart();
    $accountPart->setTransaction($transaction);
    $accountPart->type = "head";
    if ($accountPart->save() == false) { // sub-transaction inside accountPart::create() method
        $transaction->rollback("Cannot save account part");
    }

    //Everything goes fine, let's commit the transaction
    $transaction->commit();

} catch(TxFailed $e) {
    echo "Failed, reason: ", $e->getMessage();
}

Трудно представить, как это будет работать в большом проекте. Вложенные транзакции не очень хороши для производительности базы данных.

Также я подумал о 3D-методе реализации, я добавил его код ниже, но он выглядит как хак, и я также не хочу его использовать:

public function create($data = null)
{   
    // Create abstract entity instance
    $entity = new \Entity();
    $entity->type = get_class($this);

    // Save abstract entity
    if (!$entity->save()) {
        return false;
    }

    // Save current entity
    $this->id = $entity->id;
    $result = parent::create($data);

    // Remove abstract entity if current row was not saved
    if (!$result) {
        $entity->delete();
    }

    return $result;
}

Каков наилучший и простой способ поддержки таких сложных объектов?


person avasin    schedule 29.01.2013    source источник


Ответы (1)


Самый простой способ реализовать транзакции — использовать 0.9.0:

class Account
{
    public function beforeCreate()
    {
        $entity = new \Entity();
        $entity->type = get_class($this);
        $this->entity = $entity;
    }

    public function initialize()
    {
        $this->belongsTo(array('entity_id', 'Entity', 'id'));
    }

}

С другой стороны, диспетчер транзакций создает изолированное соединение, которое позволяет запрашивать записи, измененные в текущем моментальном снимке транзакции, а также просматривать неизолированные записи.

Здесь есть объяснение различных сценариев транзакций в новых документах: http://docs.phalconphp.com/en/0.9.0/reference/models.html#transactions

person twistedxtra    schedule 29.01.2013
comment
Хм... есть ли шанс, например, откатить вставку строки Entity, если $account->save() не удастся? - person avasin; 30.01.2013
comment
если учетная запись каким-то образом не будет сохранена, строка Entity не будет создана, верно? И второй вопрос: можно ли использовать неявные транзакции с ручными транзакциями (на уровне действий контроллера) и с изолированными транзакциями? - person avasin; 30.01.2013