Debugging Tips: Filename cannot be empty

November 21, 2014  |  No Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Problem

You may have seen this kind of error frequently logged in your Magento log file var/log/system.log:

Warning: include(): Filename cannot be empty in /app/code/core/Mage/Core/Block/Template.php on line xxx

This error means Magento is trying to include a template block with an empty string set as it’s template.

Solution

In order to find which template file is missing, simply edit the code in Mage_Core_Block_Template::fetchView() as:

public function fetchView($fileName)
{
    ...
    try {
        $includeFilePath = realpath($this->_viewDir . DS . $fileName);
        if (strpos($includeFilePath, realpath($this->_viewDir)) === 0 || $this->_getAllowSymlinks()) {
            /* edit - start */
            if (!is_file($includeFilePath)) {
                Mage::log($fileName, null, 'missing-phtml.log', true);
            }
            /* edit - end */
            include $includeFilePath;
        } else {
            Mage::log('Not valid template file:'.$fileName, Zend_Log::CRIT, null, null, true);
        }

    } catch (Exception $e) {
        ob_get_clean();
        throw $e;
    }
    ...
}

You will see the missing .phtml filenames being logged. And search for that name in your layout xmls which will show you where it went wrong.
Happy Debugging!

Using get_defined_vars() for debugging local scope variables

May 8, 2013  |  2 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Suppose say, you want to debug variables in some observer method.
You can either log the each variable or simply dump it. But when you have many variables, debugging each variables can be an overhead task. For this purpose we can use PHP’s inbuilt function: get_defined_vars()
How to use it?
CODE:

//put this line at the top of your variable declarations
$vars = get_defined_vars();

// Now your regular stuffs goes here...
$foo = 'foo';
$bar = 'bar';
$data = $model->getData();

// Only stores all the variables defined in current scope
$vars = array_diff(get_defined_vars(), $vars);

//Now you can either dump
Zend_Debug::dump($vars);
//or keep in log
Mage::log($vars, null, 'var-debug.log', true)

This approach can be very handy in debugging any local scope variables. At least it provides an easier approach with clean code.

How to fix the issue: Product images missing in backend but not in frontend

June 22, 2012  |  54 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Introduction

Recently i have to fix a strange issue for one of my client. Issue was that product images were missing from ‘Images’ tab of product edit form (as depicted below), though they were displaying fine in the frontend.

Missing Product Images In Backend

After going through the table relationship for catalog product images (as shown below), it was found that required rows were missing in table ‘catalog_product_entity_media_gallery’. Note that table ‘catalog_product_entity_media_gallery_value’ is not necessary unless you want to sort or label the product images.

Schema For Product Images

By inspecting the html code of Images tab of product edit form via firebug, it was seen that ‘value’ attribute had empty json:

<input type="hidden" value="[]" name="product[media_gallery][images]" id="media_gallery_content_save">

Further Debugging:
Using template path hints extension for admin: Easy Template Path Hints, it was found that the responsible template and block class were: ‘app/design/adminhtml/default/default/template/catalog/product/helper/gallery.phtml’ and ‘Mage_Adminhtml_Block_Catalog_Product_Helper_Form_Gallery_Content’ respectively.

Looking at the block class and .phtml code it was found that Input element has empty json because of Mage_Adminhtml_Block_Catalog_Product_Helper_Form_Gallery_Content::getImagesJson() method.

When populated the images from table ‘catalog_product_entity_varchar’ to ‘catalog_product_entity_media_gallery’ for those products, products images were seen at the backend(refer to the code below for more info).
Here goes the steps for fixing:

Steps

1> Prepare CSV file(update_missing_images.csv) with only one field: sku and upload to the root of Magento installation.
Note: This will contain the sku of those products whose images are missing at the backend.

2> Create a file: update_missing_images.php and upload to the root of magento installation and paste the following code:

<?php
/**
 * @author		MagePsycho <info@magepsycho.com>
 * @website		http://www.magepsycho.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 _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:: $productId=' . $productId . ', $image=' . $_image['value'];
				echo $missingImageUpdates.'<br />';
				_log($missingImageUpdates);
			}
		}
		$separator = str_repeat('=', 100);
		_log($separator);
		echo $separator . '<br />';
	}
}
/***************** 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 = '<hr />';
$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 . '). <br />';

		}catch(Exception $e){
			$message .=  $count .'> Error:: While Upating Images of Sku (' . $sku . ') => '.$e->getMessage().'<br />';
		}
	}else{
		$message .=  $count .'> Error:: Product with Sku (' . $sku . ') does\'t exist.<br />';
	}
	$count++;
}
echo $message;

3> Open your browser and run the following url:
http://your-magento-url/update_missing_images.php

4> That’s all. Try to browse the Images tab, now you will see those missing images.

Hope this helps somebody!

How to find current url in Magento

May 27, 2012  |  6 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

You must be wondering we can simply use:

$currentUrl = Mage::helper('core/url')->getCurrentUrl()

or

$currentUrl = Mage::getUrl('*/*/*', array('_current' => true));

in order to find the current url.

But it does’t always work as expected.
Try to use the above code in a page which has some missing css, js, images etc.(You can use Firebug in order to find such error which are so called Network error.). You will see that above code captures the url for missing file(js, css, images etc.) which is obviously not the required current url.

You can simply fix above error by using the following code:

if (!in_array(Mage::app()->getFrontController()->getAction()->getFullActionName(), array('cms_index_noRoute', 'cms_index_defaultNoRoute'))) {
	$currentUrl = Mage::helper('core/url')->getCurrentUrl();
}

Cheers!

Debugging technique in Magento: Process of elimination

May 27, 2012  |  No Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Introduction:

Debugging is a methodical process of finding and reducing the number of bugs, or defects, in a computer program or a piece of electronic hardware, thus making it behave as expected.

Source: Wikipedia

Nobody is perfect so don’t expect an application to be perfect. As a developer you may face many bugs during your application development, some are easy to trace & fix and some are hard to trace & fix.

Suppose say you got an strange error which doesn’t give any hint of what it could be then what will you do?
Of course you will try to isolate the error, but how? Answer: By Process of Elimination

Steps:

Here are some tips for debugging in Magento by process of elimination (I would even say the renaming/commenting techniques):
1. Rename the current theme folder and check if error is eliminated or not
If error is eliminated then try to isolate the error by renaming the xml layout or .phtml template files
else go ahead with step 2.

2. Rename the app/code/local folder and check if error is eliminated or not
If error is eliminated then try to isolate the error by disabling the local modules one by one
else go ahead with step 3.

Some Notes:
Even you disable all the local modules using following code in app/etc/local.xml:

<disable_local_modules>true</disable_local_modules>

or
using following code in app/etc/modules/*.xml

<active>false</active> 

This will help you but not much. Suppose consider the case that you have core class overridden in app/code/local/Mage directory.
Do this kind of overriding require any xml declarations? Answer: Nope.
So in this case app/code/local folder renaming can be helpful instead of disabling the module via xml file.

3. Rename the app/code/community folder and check if error is eliminated or not
If error is eliminated then try to isolate the error by disabling the local modules one by one
else go ahead with scratching your head 😉

Suppose, for example, you find that the the main culprit was some community module. You should try to comment the different section of config.xml of that module. This will help you in locating the main culprit file. Once the culprit file is found then you can narrow it down by commenting different section of the code until you find that main single line of code. Once the main culprit line/code is found then it’s upto you how to fix.

Hope this article was useful.
Thanks for reading!