¿Diferencia entre catalog_product_save_after y catalog_product_save_commit_after?

8

¿Alguien puede explicar la diferencia entre estos eventos? Solo lo rápido y sucio por favor. Gracias.

Tengo un método Observador como este:

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

No está llegando al sendStatusMail()

Me estoy enganchando al evento:

        <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>

¿Debería estar usando: catalog_product_save_commit_after

OBJETIVO:

Enviar un correo electrónico después de que el producto esté deshabilitado.

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);
    }
}
este método
fuente
deberías usar evento <catalog_product_status_update>
Nickool
¿Es esa la razón por la que no está disparando? ¿Simplemente usando el evento incorrecto? @Nickool
thismethod

Respuestas:

14

El guardado ocurre en una transacción MySQL y el save_afterevento se activa antes de que se confirme la transacción, para que pueda realizar actualizaciones adicionales en la base de datos dentro de la misma transacción.

El save_commit_afterevento se desencadena después de que se haya confirmado la transacción, es decir, cuando los cambios se escribieron en la base de datos.

Además, en save_commit_after, la _hasDataChangespropiedad ya se ha restablecido false, por lo que su cheque no funcionaría. Por otro lado, si no hubiera cambios, ambos eventos ni siquiera se activarían , porque Mage_Core_Model_Abstract :: save () no hace nada si no hubo cambios en los datos:

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

Dicho esto, no veo por qué su código no debería funcionar.

Fabian Schmengler
fuente
Gracias por la respuesta. Cuando agrego un Mage :: log () en lugar de sendStatusMail () recibo el mensaje de registro correctamente. Pero no está enviando correos electrónicos. Me aseguré de que "Desactivar comunicaciones por correo electrónico" esté establecido en NO y que mi dirección de correo electrónico esté en mi dirección de correo electrónico personalizada> almacenar direcciones de correo electrónico. ¿Alguna otra idea de por qué no funciona? @fschmengler
thismethod
Sin conocer su método sendStatusMail, no. Eso es probablemente material para otra pregunta. ¿O funciona el mismo método si se llama desde un contexto diferente?
Fabian Schmengler
Actualicé mi pregunta original para mostrar el método sendStatusMail. Si no te importa ayudar más. Gracias.
thismethod
¿Alguna posibilidad de que pueda darme su opinión sobre mi método sendStatusMail ($ producto)?
thismethod
No puedo detectar ningún error allí, lo siento
Fabian Schmengler
0

vendor / magento / framework / Model / 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;
}

Echemos un vistazo a guardar la entidad del producto.

-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

Aquí está el código de la función commit:

/**
 * 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;
}

Echemos un vistazo a nuestro ejemplo más de cerca.

  1. $this->getConnection()->commit();poner valores en DB para nuestro primer nivel (es Stock). Si sucede algo malo aquí, se lanzará una excepción y todos los cambios se revertirán.

  2. Luego se va a procesar devoluciones de llamada. Como actualmente estamos en el 1er nivel, no se llamarán devoluciones de llamada. Y nos estamos mudando del evento catalog_product_after_save para confirmar los cambios del producto (nivel 0).

  3. $this->getConnection()->commit();poner valores en DB para nuestro nivel 0 (es el producto en sí). Si sucede algo malo aquí, también se generará una excepción y todos los cambios también se revertirán.

  4. Luego nos estamos moviendo a la ejecución de devoluciones de llamada. Ahora estamos en el nivel 0 y se ejecutarán devoluciones de llamada. Todo lo que sea malo en su interior call_user_func($callback);se pondrá al día y se registrará. No se revertirá nada si la devolución de llamada causa una excepción

zhartaunik
fuente