Magento remove images of deleted products

Clean up not used Magento images

Magento unfortunately does not remove images of a product unfortunately if the product is removed. The following script checks all the images and checks if it’s still present in the database of Magento. If not, it deletes the image. Use the following code:

setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);

set_time_limit(0);
ini_set('memory_limit','1024M');

$media = Mage::getBaseDir('media').'/catalog/product';

echo "Query database for images …\n";
$query = "SELECT value FROM catalog_product_entity_media_gallery";
$data = Mage::getSingleton('core/resource')->getConnection('core_read')->fetchAll($query);

$dbData=array();
foreach($data as $item){
  $dbData[$item['value']]=$item['value'];
}

echo "Images found in database:".count($dbData)."\n";

echo "Search images in media directory …\n";
$images = findFiles($media, array('jpg'));
echo "Images found under directory ($media):".count($images['jpg'])."\n";

echo "Start removing images …\n";
$removedCount=0;
$skippedCount=0;
foreach($images['jpg'] as $image) {
  if(strpos($image,'cache')!==false)
  {
    //echo "Skip cached image : $image\n";
    continue;
  }

  $imageCleanup = str_replace($media,"",$image);
  if(isset($dbData[$imageCleanup]))
  {
    echo "Skip image is in database : $image\n";
    $skippedCount++;
    continue;
  }
  else
  {
    echo "Remove image : $image\n";
    //if($testrun==false) unlink($image);
    $removedCount++;
  }
}

echo "Done, removed $removedCount images and skipped $skippedCount images.\n";

function findFiles($directory, $extensions = array()) {
  function glob_recursive($directory, &$directories = array()) {
    foreach(glob($directory, GLOB_ONLYDIR | GLOB_NOSORT) as $folder) {
      $directories[] = $folder;
      glob_recursive("{$folder}/*", $directories);
    }
  }
  glob_recursive($directory, $directories);
  $files = array ();
  foreach($directories as $directory) {
  foreach($extensions as $extension) {
  foreach(glob("{$directory}/*.{$extension}") as $file) {
  $files[$extension][] = $file;
}
}
}
return $files;
}

?>

Clean up Magento space: could save a lot of GBs

In my case, I tried the script for a store with 40K images of which 10K were not used. This is a disk space reduction of a few Gigabytes.

Final word

This script is thanks to a comment on RapidCommerce. If you use the script, that’s on your own responsibility. Try it out on a staging server and make a backup in advance. Cheers!

Magento admin category tree shows no sub categories

Ever had that the category tree does not show specific subcategories that are present though?

Than follow these steps:

1. Make a backup for if anything goes wrong (running all the steps is on your own risk of course).

2. Run this query

SELECT c.entity_id, c.children_count as original_children_count, COUNT(c2.children_count) as `children_count`, c.level as original_level, (LENGTH(c.path)-LENGTH(REPLACE(c.path,'/',''))) as `level`
FROM catalog_category_entity c
LEFT JOIN catalog_category_entity c2 ON c2.path like CONCAT(c.path,'/%')
GROUP BY c.path

3. Check if there are still minus children_count categories with:

SELECT c.entity_id, c.children_count as original_children_count, COUNT(c2.children_count) as `children_count`, c.level as original_level, (LENGTH(c.path)-LENGTH(REPLACE(c.path,'/',''))) as `level`
FROM catalog_category_entity c
LEFT JOIN catalog_category_entity c2 ON c2.path like CONCAT(c.path,'/%')
GROUP BY c.path

If anything went right, it should show a + sign before the category in the category tree in the Magento admin again. Good luck

This error is likely caused by a category copy/duplicate plugin.

Thanks to: http://magento.stackexchange.com/questions/34730/not-showing-sub-categories-in-category-tree-in-manage-products-page

Fixing “Front controller reached 100 router match iterations”

Ever got the bug: “Front controller reached 100 router match iterations”? Solve it as follows:

1. You can get more information by going to Magento Core file app/code/core/Mage/Core/Controller/Varien/Front.php, find there lines:

while (!$request->isDispatched() && $i++<100) {
foreach ($this->_routers as $router) {
if ($router->match($this->getRequest())) {
break;
}
}
}

Replace with:

Mage::log('----Matching routers------------------------------');
Mage::log('Total ' . count($this->_routers) . ': ' . implode(', ', array_keys($this->_routers)));
while (!$request->isDispatched() && $i++<100) {
Mage::log('- Iteration ' . $i);
$requestData = array(
'path_info' => $request->getPathInfo(),
'module' => $request->getModuleName(),
'action' => $request->getActionName(),
'controller' => $request->getControllerName(),
'controller_module' => $request->getControllerModule(),
'route' => $request->getRouteName()
);

$st = '';
foreach ($requestData as $key => $val) {
$st .= "[{$key}={$val}]";
}
Mage::log('Request: ' . $st);
foreach ($this->_routers as $name => $router) {
if ($router->match($this->getRequest())) {
Mage::log('Matched by "' . $name . '" router, class ' . get_class($router));
break;
}
}
}

Possible solution solving “Front controller reached 100 router” bug

What happened in my case? I opened the var/log/system.log file and there was downtime between 5 and 6. So there was a blankage between 5 and 6. The last log holds on 05:10, than the site went out:

2014-12-03T05:09:59+00:00 DEBUG (7): ----Matching routers------------------------------
2014-12-03T05:09:59+00:00 DEBUG (7): Total 7: admin, standard, cms, blog, default
2014-12-03T05:09:59+00:00 DEBUG (7): - Iteration 1
2014-12-03T05:09:59+00:00 DEBUG (7): Request: [path_info=/admin/][module=][action=][controller=][controller_module=][route=]
2014-12-03T05:10:02+00:00 DEBUG (7): Matched by "admin" router, class Mage_Core_Controller_Varien_Router_Admin
2014-12-03T05:10:02+00:00 DEBUG (7): - Iteration 2
2014-12-03T05:10:02+00:00 DEBUG (7): Request: [path_info=/admin/][module=admin][action=login][controller=index][controller_module=Mage_Adminhtml][route=adminhtml]
2014-12-03T05:10:02+00:00 DEBUG (7): Matched by "admin" router, class Mage_Core_Controller_Varien_Router_Admin

So search for the last entry before the outage. In my case there was an admin route problem. I solved it by changing the admin url in app/etc/local.xml (so far, so good). I’ll keep you updated if the outage will come again.

Thanks to: http://stackoverflow.com/questions/6262129/magento-front-controller-reached-100-router-match-iterations-error

Magento 1.8 add to url via url query

Would like to add a product via an url like (as described in this Mage article):



";
?>

This doesn’t work in Magento 1.8 unfortunately, because of the new form_key system. You can provide an hidden form_key field. Unfortunately this didn’t work in my case. Best way is to omit the form_key security for add to cart querystrings. This can be solved with this rewrite plugin:

https://github.com/deivisonarthur/Inovarti_FixAddToCartMage18/

Good luck!

Magento Product Flat Data – reindex error

When you get the error:

Product Flat Data index process unknown error:
exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`sprayfoa_store`.<result 2 when explaining filename '#sql-66c_ab044'>, CONSTRAINT `FK_CAT_PRD_FLAT_1_ENTT_ID_CAT_PRD_ENTT_ENTT_ID` FOREIGN KEY (`entity_id`) REFERENCES `catalog_product_entity`)' in ...
Stack trace:

Try to truncate all the category_flat_product_x tables in phpmyadmin and then reindex again.

Magento not finding or showing right product images

Problem: the wrong image is shown compared to the image you inserted. Possible errors:

  • Check per product all the store view images. Maybe it crosses with your desired image.
  • Rebuild the image cache
  • Check permissions on the directory
  • Check whether there is an image with the same name already in the media file. Also check whether the problem is due to case insensitivity
  • Reindex the catalog product structure. This makes sure that the right product image is linked and shown

UPDATE: Magento showing image in frontend, but not in backend

Solution: I probably found the solution now. The image is shown in the frontend, but not in the backend. Probably this is a solution: http://www.blog.magepsycho.com/how-to-fix-the-issue-product-images-missing-in-backend-but-not-in-frontend/ . It is a script that checks for ‘damages’ in the table, which is the reason why an image doesn’t show.

UPDATE2: Multistore shows wrong/different images per store

How to get back the images and reset all the images

An extension on the above update is my own customization on the script. It gets back images that do show up in the frontend but not in the backend or when wrong/different images are shown among the various storeviews.

update_missing_files.php

 * @website     http://www.magepsycho.com edited by http://www.devproblems.com
 * @category    Export / Import
 */
$mageFilename = 'app/Mage.php';
require_once $mageFilename;
Mage::setIsDeveloperMode(true);
ini_set('display_errors', 1);
umask(0);
Mage::app('admin');
Mage::register('isSecureArea', 1);
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);

set_time_limit(0);
ini_set('memory_limit','1024M');

/***************** UTILITY FUNCTIONS ********************/
function _log($message, $file = 'update_missing_images.log'){
    Mage::log($message, null, $file);
}

function _getIndex($field) {
    global $fields;
    $result = array_search($field, $fields);
    if($result === false){
        $result = -1;
    }
    return $result;
}

function _getConnection($type = 'core_read'){
    return Mage::getSingleton('core/resource')->getConnection($type);
}

function _getTableName($tableName){
    return Mage::getSingleton('core/resource')->getTableName($tableName);
}

function _getAttributeId($attribute_code = 'price'){
    $connection = _getConnection('core_read');
    $sql = "SELECT attribute_id
                FROM " . _getTableName('eav_attribute') . "
            WHERE
                entity_type_id = ?
                AND attribute_code = ?";
    $entity_type_id = _getEntityTypeId();
    return $connection->fetchOne($sql, array($entity_type_id, $attribute_code));
}

function _getEntityTypeId($entity_type_code = 'catalog_product'){
    $connection = _getConnection('core_read');
    $sql        = "SELECT entity_type_id FROM " . _getTableName('eav_entity_type') . " WHERE entity_type_code = ?";
    return $connection->fetchOne($sql, array($entity_type_code));
}

function _getIdFromSku($sku){
    $connection = _getConnection('core_read');
    $sql        = "SELECT entity_id FROM " . _getTableName('catalog_product_entity') . " WHERE sku = ?";
    return $connection->fetchOne($sql, array($sku));
}

function _checkIfSkuExists($sku){
    $connection = _getConnection('core_read');
    $sql        = "SELECT COUNT(*) AS count_no FROM " . _getTableName('catalog_product_entity') . " WHERE sku = ?";
    $count      = $connection->fetchOne($sql, array($sku));
    if($count > 0){
        return true;
    }else{
        return false;
    }
}

function _checkIfRowExists($productId, $attributeId, $value){
    $tableName  = _getTableName('catalog_product_entity_media_gallery');
    $connection = _getConnection('core_read');
    $sql        = "SELECT COUNT(*) AS count_no FROM " . _getTableName($tableName) . " WHERE entity_id = ? AND attribute_id = ?  AND value = ?";
    $count      = $connection->fetchOne($sql, array($productId, $attributeId, $value));
    if($count > 0){
        return true;
    }else{
        return false;
    }
}

function _insertRow($productId, $attributeId, $value){
    $connection             = _getConnection('core_write');
    $tableName              = _getTableName('catalog_product_entity_media_gallery');

    $sql = "INSERT INTO " . $tableName . " (attribute_id, entity_id, value) VALUES (?, ?, ?)";
    $connection->query($sql, array($attributeId, $productId, $value));
}

function _deleteWrongRows($productId, $smallImageId, $imageId, $thumbnailId){
    $connection             = _getConnection('core_write');
    $tableName              = _getTableName('catalog_product_entity_varchar');

    $sql = "DELETE FROM " . $tableName . " WHERE `entity_id` = ?
	AND store_id != ?
	AND (
	attribute_id = ?
	OR attribute_id = ?
	OR attribute_id = ?
	)";
	//printf($sql, $productId, 0, $smallImageId, $imageId, $thumbnailId);
    $connection->query($sql, array($productId, 0, $smallImageId, $imageId, $thumbnailId));
}

function _updateMissingImages($count, $productId, $data){
    $connection             = _getConnection('core_read');
    $smallImageId           = _getAttributeId('small_image');
    $imageId                = _getAttributeId('image');
    $thumbnailId            = _getAttributeId('thumbnail');
    $mediaGalleryId         = _getAttributeId('media_gallery');

    //getting small, base, thumbnail images from catalog_product_entity_varchar for a product
    $sql    = "SELECT * FROM " . _getTableName('catalog_product_entity_varchar') . " WHERE attribute_id IN (?, ?, ?) AND entity_id = ? AND `value` != 'no_selection'";
    $rows   = $connection->fetchAll($sql, array($imageId, $smallImageId, $thumbnailId, $productId));
    if(!empty($rows)){
        foreach($rows as $_image){
            //check if that images exist in catalog_product_entity_media_gallery table or not
            if(!_checkIfRowExists($productId, $mediaGalleryId, $_image['value'])){
                //insert that image in catalog_product_entity_media_gallery if it doesn't exist
                _insertRow($productId, $mediaGalleryId, $_image['value']);
                /* Output / Logs */
                $missingImageUpdates = $count . '> Updated for:: $productId=' . $productId . ', $image=' . $_image['value'];
                echo $missingImageUpdates.'
';
                _log($missingImageUpdates);
            }

            // delete wrong product selected images storeview rows
            if(_deleteWrongRows($productId, $smallImageId, $imageId, $thumbnailId)){
            	$ImageUpdates = $count . '> Deleted wrong rows for:: $productId=' . $productId . ', $image=' . $_image['value'];
                echo $ImageUpdates.'
';
                _log($ImageUpdates);
            }
        }
        $separator = str_repeat('=', 100);
        _log($separator);
        echo $separator . '
';
    }
}
/***************** UTILITY FUNCTIONS ********************/

$messages           = array();
$csv                = new Varien_File_Csv();
$data               = $csv->getData('update_missing_images.csv'); //path to csv
$fields             = array_shift($data);
#print_r($fields); print_r($data); exit;

$message = '


'; $count = 1; foreach($data as $_data){ $sku = isset($_data[_getIndex('sku')]) ? trim($_data[_getIndex('sku')]) : ''; if(_checkIfSkuExists($sku)){ try{ $productId = _getIdFromSku($sku); _updateMissingImages($count, $productId, $_data); $message .= $count . '> Success:: While Updating Images of Sku (' . $sku . '). '; }catch(Exception $e){ $message .= $count .'> Error:: While Upating Images of Sku (' . $sku . ') => '.$e->getMessage().' '; } }else{ $message .= $count .'> Error:: Product with Sku (' . $sku . ') does\'t exist. '; } $count++; } echo $message; //$process = Mage::getSingleton('index/indexer')->getProcessByCode('catalog_product_flat'); ?>

Make a file update_missing_images.csv like:

sku
SKU1234
SKU4312
ETC

USE THIS SCRIPT AT YOUR OWN RISK

Module error message: MediabrowserUtility is not defined

If you install a custom module in Magento it could lack the TinyMce Mediabrowser. You get the following message then:

error: error in [unknown object].fireEvent():

event name: open_browser_callback

error message: MediabrowserUtility is not defined

How to fix this?

Quick fix

In /app/design/adminhtml/default/default/layout/YOURMODULE.xml add:


            1
            
            
            
            
            
            
            
            js_cssprototype/windows/themes/default.css
            js_cssprototype/windows/themes/magento.css
            lib/prototype/windows/themes/magento.css
            
        

Or change to

Long solution (may not work)

In your /YOURMODULE/etc/config.xml you have:

 
  <cms_wysiwyg_config_prepare>

      <variable_observer>
        core/variable_observer
        prepareWysiwygPluginConfig

In Block/Adminhtml/YOURMODULE/Edit.php

protected function _prepareLayout() {
    parent::_prepareLayout();
    if (Mage::getSingleton('cms/wysiwyg_config')->isEnabled()) {
        $this->getLayout()->getBlock('head')->setCanLoadTinyMce(true);
        $this->getLayout()->getBlock('head')->setCanLoadExtJs(true);
    }
}

Then in the top of Block/Adminhtml/YOURMODULE/Edit/Tab/Form.php

 protected function _prepareForm() {
            $form = new Varien_Data_Form();
            $this->setForm($form);
            $wysiwygConfig = Mage::getSingleton('cms/wysiwyg_config')->getConfig(array('add_variables' => false,
         'add_widgets' => false,
          'add_images' => true,
          'files_browser_window_url' => Mage::getSingleton('adminhtml/url')->getUrl('adminhtml/cms_wysiwyg_images/index'),
          'files_browser_window_width' => (int) Mage::getConfig()->getNode('adminhtml/cms/browser/window_width'),
          'files_browser_window_height'=> (int) Mage::getConfig()->getNode('adminhtml/cms/browser/window_height')
         ));

Further down:

$fieldset->addField('not-called-content', 'editor', array(
                                    'name'      => 'not-called-content',
                                    'label'     => Mage::helper('WHATEVER')->__('Content'),
                                    'title'     => Mage::helper('WHATEVER')->__('Content'),
                                    'style'     => 'width:550px; height:300px;',
                                    'required'  => false,
                                    'config'    => $wysiwygConfig,
                                    'wysiwyg'   => true
                                    ));

With thanks to Theodores

Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction

When having trouble with the error: “Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction” or when your place order / order confirmation gets stuck.

Fixing the order process getting stuck:

According to the Magento Forum, in MySQL, try:

TRUNCATE `log_customer`;
TRUNCATE `log_quote`;
TRUNCATE `log_summary`;
TRUNCATE `log_summary_type`;
TRUNCATE `log_url`;
TRUNCATE `log_url_info`;
TRUNCATE `log_visitor`;
TRUNCATE `log_visitor_info`;

Of course create a backup first. Doesn’t this fix the problem or do you have a better solution? Comment it!