Разница между catalog_product_save_after и catalog_product_save_commit_after?

8

Может кто-нибудь объяснить разницу между этими событиями. Просто быстро и грязно, пожалуйста. Спасибо.

У меня есть метод Observer, например, так:

public function detectProductChanges($observer)
    {
        $product = $observer->getProduct();
        $old = $product->getOrigData();
        $new = $product->getData();
        if ($product->hasDataChanges() && $old['status'] == 1 && $new['status'] == 2) {
            $this->_sendStatusMail($product);
        }
    }

Это не добраться до sendStatusMail()

Я подключаюсь к событию:

        <events>
            <catalog_product_save_after>
                <observers>
                    <productchange>
                        <type>singleton</type>
                        <class>A_ProductNotification_Model_Observer</class>
                        <method>detectProductChanges</method>
                    </productchange>
                </observers>
            </catalog_product_save_after>
        </events>

Должен ли я использовать: catalog_product_save_commit_after

ЦЕЛЬ:

Получите электронное письмо после отключения продукта.

private function _sendStatusMail($product)
    {
        if (!Mage::getStoreConfig('trans_email/ident_custom3/email')) return false;
        $emailTemplate = Mage::getModel('core/email_template');
        $emailTemplate->loadDefault('elec_productnotification_tpl');
        $emailTemplate->setTemplateSubject('Product has been disabled');
        $emailTemplate->setSenderEmail($salesData['email']);
        $emailTemplateVariables['style_number']   = $product->getElecStyle();
        $emailTemplateVariables['frame_color']    = $product->getAttributeText('frame_color');
        $emailTemplateVariables['size']           = $product->getAttributeText('size');
        $emailTemplateVariables['elec_color'] = $product->getAttributeText('elec_color');
        $emailTemplateVariables['store_name']   = Mage::getModel('core/store')->load($product->getStoreId())->getName();
        $emailTemplateVariables['product_name'] = Mage::getModel('catalog/product')->load($product->getId())->getName();
        $emailTemplateVariables['product_sku']  = $product->getSku();
        $emailTemplateVariables['dates']        = date("F jS Y h:i:sA", strtotime('-7 hours'));
        // Get General email address (Admin->Configuration->General->Store Email Addresses)
        $emails = explode(',', Mage::getStoreConfig('trans_email/ident_custom3/email'));
        foreach ($emails as $email) $emailTemplate->send($email, $product->getStoreId(), $emailTemplateVariables);
    }
}
Этот способ
источник
Вы должны использовать событие <catalog_product_status_update>
Nickool
Это причина того, что это не стрельба? Просто используете неправильное событие? @Nickool
метод

Ответы:

14

Сохранение происходит в транзакции MySQL, и save_afterсобытие инициируется до фиксации транзакции, так что вы можете делать дополнительные обновления в базе данных в рамках той же транзакции.

save_commit_afterСобытие срабатывает после того, как сделка была совершена, то есть , когда изменения были записаны в базу данных.

Кроме того save_commit_after, _hasDataChangesсвойство уже сброшено false, поэтому проверка не будет работать. С другой стороны, если бы не было никаких изменений, оба события даже не были бы вызваны, потому что Mage_Core_Model_Abstract :: save () ничего не делает, если не было никаких изменений данных:

if (!$this->_hasModelChanged()) {
    return $this;
}

При этом я не понимаю, почему ваш код не должен работать.

Фабиан Шменглер
источник
Спасибо за ответ. Когда я добавляю Mage :: log () вместо sendStatusMail (), я получаю сообщение журнала правильно. Но это не отправка электронных писем. Я удостоверился, что для параметра «Отключить обмен сообщениями по электронной почте» установлено значение «НЕТ» и что мой адрес электронной почты находится в моем настраиваемом электронном адресе> адресов электронной почты магазина. Любые другие идеи, почему это не работает? @fschmengler
метод
Не зная вашего метода sendStatusMail, нет. Это, вероятно, материал для другого вопроса. Или один и тот же метод работает, если вызывается из другого контекста?
Фабиан Шменглер
Я обновил свой оригинальный вопрос, чтобы показать метод sendStatusMail. Если вы не против дальнейшей помощи. Спасибо.
метод
Есть ли шанс, что вы можете высказать свое мнение о моем методе sendStatusMail ($ product)?
метод
Я не могу обнаружить там никаких ошибок, извините
Фабиан Шменглер
0

продавец / Magento / рамки / модель / ResourceModel / Db / AbstractDb.php

public function save(\Magento\Framework\Model\AbstractModel $object)
{
    // ...

    $this->beginTransaction();

    try {
        // ...
        if ($object->isSaveAllowed()) {
            // ...
            $this->_beforeSave($object);
            // ...
            if ($this->isObjectNotNew($object)) {
                $this->updateObject($object);
            } else {
                $this->saveNewObject($object);
            }
            // ...
            $this->processAfterSaves($object);
        }
        $this->addCommitCallback([$object, 'afterCommitCallback'])->commit();
        // ...
    } catch (\Exception $e) {
        $this->rollBack();
        $object->setHasDataChanges(true);
        throw $e;
    }
    return $this;
}

Давайте посмотрим на сохранение сущности продукта.

-product_model save
|-product_resource save
|--begin transaction (0 lvl)
|---before product save events
|---creating new product or updating existing one
|---after product save events
|----one of event is saving another entity CatalogInventory Stock
|-----catalog_inventory_stock resource save
|------begin another transaction (1 lvl)
|-------before stock save events
|-------updating / creating stock item
|-------after product save events (here could be one more 
        dependable entity which could cause one more save
        operation and begin another transaction)
|------commit of 1st level !!! No callbacks executed
|--commit of 0 level ALL CALLBACKS ARE EXECUTED

Вот код функции коммита:

/**
 * Commit resource transaction
 *
 * @return $this
 * @api
 */
public function commit()
{
    $this->getConnection()->commit();
    /**
     * Process after commit callbacks
     */
    if ($this->getConnection()->getTransactionLevel() === 0) {
        $callbacks = CallbackPool::get(spl_object_hash($this->getConnection()));
        try {
            foreach ($callbacks as $callback) {
                call_user_func($callback);
            }
        } catch (\Exception $e) {
            $this->getLogger()->critical($e);
        }
    }
    return $this;
}

Давайте посмотрим на наш пример более подробно.

  1. $this->getConnection()->commit();положить значения в БД для нашего 1-го уровня (это запас). Если здесь произойдет что-то плохое, будет сгенерировано исключение, и все изменения будут отменены.

  2. Затем идет обработка обратных вызовов. Поскольку мы в настоящее время на 1-м уровне, никакие обратные вызовы не будут вызываться. И мы выходим из события catalog_product_after_save, чтобы зафиксировать изменения продукта (уровень 0).

  3. $this->getConnection()->commit();положить значения в БД для нашего уровня 0 (это сам продукт). Если что-то плохое случится, исключение также будет сгенерировано, и все изменения также будут отменены.

  4. Затем мы переходим к выполнению обратных вызовов. Теперь мы на нулевом уровне, и обратные вызовы будут выполняться. Все, что вы плохо внутри, call_user_func($callback);будет поймано и просто зарегистрировано. Ничто не будет отменено, если обратный вызов вызовет исключение

zhartaunik
источник