Skip to content

Instantly share code, notes, and snippets.

@edannenberg
Last active December 17, 2015 08:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edannenberg/5577420 to your computer and use it in GitHub Desktop.
Save edannenberg/5577420 to your computer and use it in GitHub Desktop.
Fixes possible borked filtered navigation after upgrading older Magento versions.
<?php
/**
* This will replicate attribute values of configurable products to all it's child products.
*
* Rationale: After upgrading Magento 1.4 to 1.7 we found that our filtered navigation was
* missing quite alot of products. Turns out somewhere after 1.4 Varien changed the filtered
* navigation to only use the attributes of child products, ignoring the attributes of
* their configurable products.
*
* Drop this into the shell directory of your Magento installation and run with -h to see all options.
*
*/
require_once 'abstract.php';
/**
* Replicates attribute values of configurable products to all it's childs.
*
* @author Erik Dannenberg <erik.dannenberg@bbe-consulting.de>
*/
class BbeConsulting_Replicate_Attributes extends Mage_Shell_Abstract
{
/** var string */
protected $_defaultAttrCodes = 'color,gender,manufacturer';
/** var bool */
protected $_reindexAll = false;
/** var Varien_Db_Adapter_Interface */
protected $_adapter;
/** var string */
protected $_attributeCodes;
/** var string */
protected $_dateLimit;
/** var bool */
protected $_quietMode;
/** var int */
protected $_maxCount;
/** var int */
protected $_doneCount = 0;
/** var int */
protected $_updatedChildCount = 0;
public function __construct() {
parent::__construct();
register_shutdown_function(array($this, 'destruct'));
$this->_setMagentoIndexerMode(Mage_Index_Model_Process::MODE_MANUAL);
}
public function destruct() {
// rollback if script got aborted during a transaction
if ($this->_adapter->getTransactionLevel() === 1) {
$this->_adapter->rollback();
}
if ($this->_reindexAll && !$this->_quietMode) {
echo 'Reindexing everything, this may take a while..' . "\n";
}
$this->_setMagentoIndexerMode(Mage_Index_Model_Process::MODE_REAL_TIME, $this->_reindexAll);
}
public function run() {
$this->_showHelp();
if ($this->getArg('cronMode')) {
$this->_dateLimit = date('Y-m-d', strtotime('-1 day'));
}
if ($this->getArg('quiet')) {
$this->_quietMode = true;
}
if ($this->getArg('reindexAll')) {
$this->_reindexAll = true;
}
if ($this->getArg('attrCodes')) {
$this->_attributeCodes = $this->getArg('attrCodes');
} else {
$this->_attributeCodes = $this->_defaultAttrCodes;
}
$this->_replicateProductAttributes();
}
protected function _replicateProductAttributes() {
$startTime = time();
$sourceCol = Mage::getResourceModel('catalog/product_collection')
->addAttributeToFilter('type_id', array('eq' => 'configurable'));
if ($this->_dateLimit) {
$sourceCol->addAttributeToFilter('created_at', array('gteq' => $this->_dateLimit));
}
$this->_adapter = $sourceCol->getConnection();
$this->_maxCount = $sourceCol->count();
$this->_attributeCodes = explode(',', $this->_attributeCodes);
Mage::getSingleton('core/resource_iterator')->walk(
$sourceCol->getSelect(),
array(array($this, 'collectionCallback')),
array(),
$this->_adapter);
if (!$this->_quietMode) {
$totalTime = number_format(time()-$startTime);
$rate = number_format($this->_updatedChildCount / $totalTime);
echo 'Updated ' . $this->_updatedChildCount . ' Products in ' . $totalTime . ' seconds. (' . $rate .'p/s)' ."\n";
}
}
/**
* Callback for collection iterator
*
* @param array $args
*/
public function collectionCallback($args) {
$product = Mage::getModel('catalog/product');
$product->load($args['row']['entity_id']);
$productData = $product->getData();
$attributeValues = array();
foreach ($this->_attributeCodes as $attrCode) {
if (array_key_exists($attrCode, $productData)) {
$attributeValues[$attrCode] = $productData[$attrCode];
}
}
$children = Mage::getModel('catalog/product_type_configurable')->getChildrenIds($product->getId());
$storeId = 0;
if (!$this->_quietMode) {
++$this->_doneCount;
$percDone = (double)($this->_doneCount/$this->_maxCount);
$status = "\r " . number_format($percDone*100, 0) . "% $this->_doneCount/$this->_maxCount";
$status .= " - Updating " . count($children[0]) . " childs for: " . $product->getName() . " (ID: ". $product->getId() . ")";
$status .= "\033[K"; // clear end of line
if ($this->_doneCount == $this->_maxCount) {
$status .= "\n";
}
echo $status;
}
if (count($children[0]) > 0) {
Mage::dispatchEvent('catalog_product_attribute_update_before', array(
'attributes_data' => &$attributeValues,
'product_ids' => &$children[0],
'store_id' => &$storeId
));
Mage::getResourceSingleton('catalog/product_action')->updateAttributes($children[0], $attributeValues , $storeId);
$this->_updatedChildCount += count($children[0]);
}
$product->clearInstance(); // prevent memleaks
//Mage::log(memory_get_usage(true));
}
/**
* @param var Mage_Index_Model_Process::MODE_* $mode
*/
protected function _setMagentoIndexerMode($mode, $reindexAll=false) {
$processes = Mage::getSingleton('index/indexer')->getProcessesCollection();
if ($reindexAll) {
$processes->walk('reindexAll');
}
$processes->walk('setMode', array($mode));
$processes->walk('save');
}
/**
* Retrieve Usage Help Message
*/
public function usageHelp()
{
return <<<USAGE
Usage: php -f replicate-attribute-values.php
--attrCodes Comma separated list of attribute codes to replicate. default: color,gender,manufacturer
--reindexAll Reindex everything after script finished. true|false default: false
--cronMode Only replicate attributes of products that were created in the last 24 hours. true|false default: false
--quiet No shell output generated. true|false default: false
help This help
USAGE;
}
}
// signal handling, requires PCNTL feature in PHP, php -m | grep pcntl
// remove this block if you are on windows, it is not supported
// just be aware that the indexer state will be left on manual on script abortion/error
declare(ticks = 1000);
function sig_handler($signal) {
switch($signal) {
case SIGTERM:
case SIGKILL:
case SIGINT:
die("Aborting... \n");
}
}
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
// end of signal handling
$shell = new BbeConsulting_Replicate_Attributes();
$shell->run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment