12

Situation is:

Admin logs in to system and he changes product somehow.

For example:

Sets qty to 10 Adds 2 images Changes description

Is there any way to track these changes? Well I mean exactly track WHAT and HOW admin changed?

I use Magento CE 1.6

Jevgeni Smirnov
  • 3,787
  • 5
  • 33
  • 50

3 Answers3

22

You can find the unaltered data through an object's getOrigData() method. A good time to get the object is through it's save_before event, so create an observer for the catalog_product_save_before event. The observer might contain the following:

public function onCatalogProductSaveBefore($observer)
{
  $product = $observer->getProduct();
  if ($product->hasDataChanges()) {
    $newValues = array_diff_assoc($product->getData(), $product->getOrigData());
    $oldValues = array_diff_assoc($product->getOrigData(), $product->getData());
    $added     = array_diff_key($product->getData(), $product->getOrigData());
    $unset     = array_diff_key($product->getOrigData(), $product->getData());
  }
}

Note that $newValues will include all of $added and existing attributes that have had their value changed. Ditto for $oldValues and $unset.

clockworkgeek
  • 37,650
  • 9
  • 89
  • 127
  • catalog_product_save_before getOrigData() are the way to go with. TY ;) – Jevgeni Smirnov Nov 21 '11 at 10:14
  • I think instead of using array_diff it should be array_diff_assoc – Pawel Dubiel Jul 10 '14 at 01:10
  • I think you could be right. Using `array_diff` would have worked in some cases though. – clockworkgeek Jul 10 '14 at 09:45
  • 1
    This is throwing en exception for me "Array to string conversion". It is because the getData() array contains "stock_item" which is an object. This causes an error at array_diff_assoc/array_diff function because the parameters to these functions should be arrays – Manaf P M Dec 03 '14 at 05:46
  • Would it work for group price delete ? or tier price delete ? – Anurag Prashant Jul 22 '15 at 10:46
  • @RedDevil It won't work because prices have their own class, `Mage_Catalog_Model_Product_Type_Price`. The price class is not a descendant of `Varien_Object` so it does not have the old prices for you. – clockworkgeek Jul 22 '15 at 14:15
5

Yes you can find unaltered data through getOrigData() as pointed out by clockworkgeek but Magento has a build in function to compare and check if the data has been changed. The method is called dataHasChangedFor() and you have to pass the property that you want to check. It would look somthing like this.

$isChanged = $productData->dataHasChangedFor('description'); 
if ($isChanged) { 
// do somthing 
}

here the method dataHasChanged() will return boolean value based on whether the data was altered.

Varun Desai
  • 51
  • 1
  • 4
2

just in case some people still coming up to this one: @clockworkgeek's answer is not correct, as soon as you turn on developer mode and displaying errors (php>=5.4) you will get exceptions and warnings:

Array to string conversion in...

as solution for recursive comparison:

public function onCatalogProductSaveBefore($observer)
{
  $product = $observer->getProduct();
  if ($product->hasDataChanges()) {
    $newValues = $this->_compareArrayAssocRecursive($product->getData(), $product->getOrigData());
    $oldValues = $this->_compareArrayAssocRecursive($product->getOrigData(), $product->getData());
  }
}

protected function _compareArrayAssocRecursive($array1, $array2) 
{
    $diff = array();
    foreach ($array1 as $key => $value) {
        if (is_array($value)) {
            if (!isset($array2[$key]) || !is_array($array2[$key])) {
                $diff[$key] = $value;
            } else {
                $newDiff = $this->_compareArrayAssocRecursive($value, $array2[$key]);
                if (!empty($newDiff)) {
                    $diff[$key] = $newDiff;
                }
            }
        } elseif (!array_key_exists($key,$array2) || $array2[$key] !== $value) {
            $diff[$key] = $value;
        }
    }
    return $diff;
}

Hope that helps Personally i would recommend to put that function into a helper class and make it public :)

UPDATE: according to problems with numeric values was float and decimal the better solution would be:

/**
 * return diff according to product changes 
 * 
 * @param Mage_Catalog_Model_Product $product
 * @return array
 */
protected function _compareArrayAssocRecursive($product)
{
    $diff = array();
    $attributes = $product->getTypeInstance(true)->getEditableAttributes($product);

    foreach ($attributes as $key => $value) {
        if ($product->dataHasChangedFor($key)) {
            $diff[$key] = $product->getData($key);
        }
    }
    return $diff;
}