7

I'm using the event catalog_product_save_after to trigger some action after a product has altered/saved. This event does not work when a massaction is used to change the product as this is done without calling save().

  • Is there another event that can be used to monitor product changes?
  • Is there a recommended way to get all product changes?
5

You can do this job via standard event/observers:

<global>
     <events>
        <catalog_product_save_before>
            <observers>
                <some_module_detect_product_changes>
                    <type>singleton</type>
                    <class>some_module/observer</class>
                    <method>detectProductChanges</method>
                </some_module_detect_product_changes>
            </observers>
        </catalog_product_save_before>
        <catalog_product_attribute_update_before>
            <observers>
                <some_module_detect_product_attribute_changes>
                    <type>singleton</type>
                    <class>some_module/observer</class>
                    <method>detectProductAttributeChanges</method>
                </some_module_detect_product_attribute_changes>
            </observers>
        </catalog_product_attribute_update_before>
</global>

And Observer.php:

class Some_Module_Model_Observer{
    public function detectProductAttributeChanges($observer)
    {
        $attributesData = $observer->getEvent()->getAttributesData();
        $productIds     = $observer->getEvent()->getProductIds();

        $user  = Mage::getSingleton('admin/session')->getUser();
        foreach ($productIds as $id) {
            $change             = Mage::getModel('some_module/changes');
            $change->product_id = $id;
            $change->new_values = print_r($attributesData, true);
            $change->user_id    = ($user) ? $user->getId() : NULL;
            $change->created    = now();
            $change->save();
        }
        return $this;
    }

    public function detectProductChanges($observer)
    {
        /**
         * @var $product Mage_Catalog_Model_Product
         * @var $user    Mage_Admin_Model_User
         */
        $product = $observer->getEvent()->getProduct();
        if ($product->hasDataChanges()) {

            try {
                $user       = Mage::getSingleton('admin/session')->getUser();
                $attributes = $this->getAttributes();
                $new        = array();
                $org        = array();
                $changes    = array();
                foreach ($attributes as $attribute) {
                    if (!is_array($product->getData($attribute))) {
                        $new[$attribute] = ($product->getData($attribute)) ? $product->getData($attribute) : null;
                        if (!is_array($product->getOrigData($attribute))) {
                            $org[$attribute] = ($product->getOrigData($attribute)) ? $product->getOrigData($attribute) : null;
                            if (($new[$attribute] != $org[$attribute])) {
                                $changes[$attribute] = array('new'=> $new[$attribute],
                                                             'old'=> $org[$attribute]);
                            }
                        }
                    }
                }

                $stokAttributes = array(
                    'qty',
                    'manage_stock',
                    'is_in_stock',
                    'min_qty',
                    'min_sale_qty',
                    'max_sale_qty',
                );
                if ($product->getStockItem()) {
                    foreach ($stokAttributes as $attribute=> $value) {
                        $new[$attribute] = ($product->getStockItem()->getData($attribute)) ? $product->getStockItem()->getData($attribute) : null;
                        $org[$attribute] = ($product->getStockItem()->getOrigData($attribute)) ? $product->getStockItem()->getOrigData($attribute) : null;
                        if ($new[$attribute] != $org[$attribute]) {
                            $changes[$attribute] = array('new'=> $new[$attribute],
                                                         'old'=> $org[$attribute]);
                        }
                    }
                }


                $newValues = array_intersect_key($new, $changes);
                if (count($newValues)) {
                    $oldValues = array_intersect_key($org, $changes);

                    $change             = Mage::getModel('some_module/changes');
                    $change->product_id = $product->entity_id;
                    $change->sku        = $product->sku;
                    $change->name       = $product->name;
                    $change->new_values = print_r($newValues, true);
                    $change->old_values = print_r($oldValues, true);
                    $change->user_id    = ($user) ? $user->getId() : NULL;
                    $change->created    = now();
                    $change->save();
                }
            } catch (Exception $e) {
                Mage::log($e->getTraceAsString(), null, 'product_changes_fault.log');
            }
        }
        return $this;
    }

}

Here is I used Changes model to save every changes to DB. And you may create admin grid to view tracked changes on dashboard.

  • This is a really good answer but there is a small problem: I need to get the products after they have been saved. The recorded changes could be used load the products in a cron job but the cron job only runs every few minutes. The changes could also be processed in controller_front_send_response_after (even without storing them to db) but I think this would be an ugly solution. – Wienczny Oct 8 '13 at 1:58
  • I can't imagine what do you really want. If you want to track product changes via cron job, you can also do this, but there is also you need additional table to store old values and new values, if you want to track a few attributes only you can take snapshot every particular period and compare one snapshot with new snapshot, through this way you can detect changes. But you want to track whole attributes it will be more complicated and expensive to store changes. – mageUz Oct 8 '13 at 8:12
  • And also simple way is to calculate hash code of the product based on attributes you want to track. Ex:. $hash=md5($product->getName().$product->getPrice().$product->getSomeValueYouWantToTrack()); if($product->getHashCode()!=$hash){//product has changes, do some logic}; $product->setHashCode($hash)->save() – mageUz Oct 8 '13 at 8:16
  • I don't want to track product changes but changed products. The changed products need to be synced to external service. – Wienczny Oct 8 '13 at 18:24
  • See my new last answer given above – mageUz Oct 14 '13 at 0:13
4

By observing catalog_product_attribute_update_before brought up by mageUz you can store the affected entity_ids referenced by the event as product_ids:

By Zend_Debug::dumping an $observer->getEvent()->getData() where I handled catalog_product_attribute_update_before I got:

array(4) {
  ["attributes_data"] => &array(1) {
    ["status"] => int(1)
  }
  ["product_ids"] => &array(2) {
    [0] => string(6) "159340"
    [1] => string(6) "159339"
  }
  ["store_id"] => &int(0)
  ["name"] => string(39) "catalog_product_attribute_update_before"
}

You could, for example, store those entity_ids in the Magento session singleton.

Then observe end_process_event_catalog_product_mass_action, which is one of Magento's dynamically built events. Handling that will tell you when product changes are complete allowing you to get the updated product state. Since it contains no data itself you'll need those entity_ids you just stored.

0

I already commented appropriate logic for your situation. But to get understand you I give further code:

class Some_Module_Model_Sync{
public function catalogSynchronizeChangedProducts()
{
    /**
     * suppose you have hash_code attribute for catalog products which is store
     * hash code of synchronizeable attribute values.
     */
    $collection = Mage::getResourceModel('catalog/product_collection');
    foreach ($collection as $product) {
        $hash = md5($product->getName() . $product->getPrice() . $product->getSomeValueYouWantToSyncronize());
        if ($product->getHashCode() != $hash) {
            //if you understand this stuff, product has some changes, because of
            //new hash code not equal to hash code when last synchronized time;
            //you should synchronize this product

            /*after synchronization */
            $product->setHashCode($hash)->save();
        }
    }
}
}

And your cron uses this logic to synchronization your products.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.