Configuring city as dropdown option in checkout billing & shipping address

June 15, 2013  |  19 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Introduction

Magento provides country and region* as dropdown option in checkout addresses unlike city which is an input text field. This on one side provides flexibility but on other side increases chances of typo in city names.
If you want your store to have an extra operations like giving free shipping to certain cities, giving COD payment option to certain cities etc., it’s better to configure the input city field as dropdown.
Here we will be discussing on listing United Arab Emirates’ cities as dropdown option.

Steps

Assume MagePsycho_Citydropdown skeleton module has already been created.
1. Adding functions for populating cities.
File: app/code/local/MagePsycho/Citydropdown/Helper/Data.php
Code:

<?php
/**
 * @category   MagePsycho
 * @package    MagePsycho_Citydropdown
 * @author     magepsycho@gmail.com
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
class MagePsycho_Citydropdown_Helper_Data extends Mage_Core_Helper_Abstract
{
	public function getUaeCities()
	{
		$helper = Mage::helper('directory');
		$cities = array(
			$helper->__('Abu Dhabi'),
			$helper->__('Ajman'),
			$helper->__('Al Ain'),
			$helper->__('Dubai'),
			$helper->__('Fujairah'),
			$helper->__('Ras al Khaimah'),
			$helper->__('Sharjah'),
			$helper->__('Umm al Quwain'),
		);
		return $cities;
	}

	public function getUaeCitiesAsDropdown($selectedCity = '')
	{
		$cities = $this->getUaeCities();
		$options = '';
		foreach($cities as $city){
			$isSelected = $selectedCity == $city ? ' selected="selected"' : null;
			$options .= '<option value="' . $city . '"' . $isSelected . '>' . $city . '</option>';
		}
		return $options;
	}
}

Notes: You can also populate the cities in db tables to make it more dynamic. For example, you can create following tables similar to regions:
+ directory_country_city (city_id, country_id, code, default_name)
+ directory_country_city_name (locale, city_id, name)

2. Replacing input text city field to dropdown using javascript
2.1
File: app/design/frontend/[package]/[theme]/template/checkout/onepage/billing.phtml
Code: Add the following code to the last of above template

<script type="text/javascript">
	<?php
	$helper			 = Mage::helper('citydropdown');
	$address		 = Mage::getSingleton('checkout/session')->getQuote()->getBillingAddress();
	$defaultCity	 = $address->getCity();
	$citiesOptions	 = addslashes($helper->getUaeCitiesAsDropdown($defaultCity));
	?>

	var billingCity = '<?php echo $defaultCity ; ?>';
	function billingSwitchCityField(){
		var selectVal = jQuery('#billing\\:country_id option:selected').val();
		if(selectVal == "AE"){
			jQuery("#billing\\:city")
			.replaceWith('<select id="billing:city" name="billing[city]" class="required-entry">' +
				  '<option value=""></option>' +
				  '<?php echo $citiesOptions; ?>' +
				'</select>');
		}else{
			jQuery("#billing\\:city")
			.replaceWith('<input type="text" class=" input-text required-entry absolute-advice " title="City" value="' + billingCity + '" id="billing:city" name="billing[city]" autocomplete="off">');
		}

	}
   jQuery(document).ready(function(){
		billingSwitchCityField();
		jQuery('#billing\\:country_id').change(function() {
			billingSwitchCityField();
		});
   })
</script>

2.2
File: app/design/frontend/[package]/[theme]/template/checkout/onepage/shipping.phtml
Code: Add the following code to the last of above template

<script type="text/javascript">
	<?php
	$helper			 = Mage::helper('citydropdown');
	$address		 = Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress();
	$defaultCity	 = $address->getCity();
	$citiesOptions	 = addslashes($helper->getUaeCitiesAsDropdown($defaultCity));
	?>




	var shippingCity = '<?php echo $defaultCity ; ?>';
	function shippingSwitchCityField(){
		var selectVal = jQuery('#shipping\\:country_id option:selected').val();
		if(selectVal == "AE"){
			jQuery("#shipping\\:city")
			.replaceWith('<select id="shipping:city" name="shipping[city]" class="required-entry">' +
				  '<option value=""></option>' +
				  '<?php echo $citiesOptions; ?>' +
				'</select>');
		}else{
			jQuery("#shipping\\:city")
			.replaceWith('<input type="text" class=" input-text required-entry absolute-advice " title="City" value="' + shippingCity + '" id="shipping:city" name="shipping[city]" autocomplete="off">');
		}




	}
   jQuery(document).ready(function(){
		shippingSwitchCityField();
		jQuery('#shipping\\:country_id').change(function() {
			shippingSwitchCityField();
		});
   })
</script>

3. Reload the checkout page, you can see the city options for United Arab Emirates as:

City as dropdown field


In the similar manner you can configure the city options for other countries as well and for other sections like My Account > Address Book etc.

Thanks for reading & sharing.

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 filter shipping method in onepage checkout?

April 21, 2013  |  3 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Introduction

You may want to filter shipping method in onepage checkout for one of the following cases:
– Filter shipping method based on Customer Group
– Filter Shipping method based on Country, State, Zipcode etc
– Filter Shipping method based on products
– etc.

Unlike event: ‘payment_method_is_active’ for payment method filtration, we don’t have similar event: ‘shipping_method_is_active’ available for shipping method. May be Magento team forgot to implement it or it was too hard to implement due to the structure.
Whatever may be the reason we still can filter by overriding: Mage_Shipping_Model_Shipping::collectCarrierRates()

Solution

Suppose a skeleton module(MagePsycho_Shipmentfilter) has already been created. And for example purpose we will be hiding flat rate shipping for non-logged in customer.
1> Rewrite the shipping model class: ‘Mage_Shipping_Model_Shipping’
File: app/code/local/MagePsycho/Shipmentfilter/etc/config.xml
Code:

...
<global>
	...
	<models>
		<shipping>
			<rewrite>
				<shipping>MagePsycho_Shipmentfilter_Model_Shipping</shipping>
			</rewrite>
		</shipping>
	</models>
	...
</global>

2> Override the method: collectCarrierRates()
File: app/code/local/MagePsycho/Shipmentfilter/Model/Shipping.php
Code:

<?php
/**
 * @category   MagePsycho
 * @package    MagePsycho_Shipmentfilter
 * @author     magepsycho@gmail.com
 * @website    http://www.magepsycho.com
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 	*/
class MagePsycho_Shipmentfilter_Model_Shipping extends Mage_Shipping_Model_Shipping
{
    public function collectCarrierRates($carrierCode, $request)
    {
        if (!$this->_checkCarrierAvailability($carrierCode, $request)) {
            return $this;
        }
        return parent::collectCarrierRates($carrierCode, $request);
    }

	protected function _checkCarrierAvailability($carrierCode, $request = null)
	{
		$isLoggedIn	 = Mage::getSingleton('customer/session')->isLoggedIn();
		if(!$isLoggedIn){
			if($carrierCode == 'flatrate'){ #Hide Flat Rate for non logged in customers
				return false;
			}
		}
		return true;
	}
}

3> That’s all.

Please share any other ideas you have.

Hide other shipping methods when free shipping is enabled

March 19, 2013  |  1 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

When free shipping method is enabled, it is shown along with other available shipping methods unlike free payment method ‘Zero Subtotal Checkout’.
There is no harm in showing other payment methods along with free shipping method. Nevertheless some merchants wants to hide rest of the methods when it is enabled.

There are many ways to do it. One of the way is to override the method:
Mage_Checkout_Block_Onepage_Shipping_Method_Available::getShippingRates()

1. Rewrite the block class

File: app/code/local/MagePsycho/Shipmentfilter/etc/config.xml:
Code:

...
<blocks>
	...
	<checkout>
		<rewrite>
			<onepage_shipping_method_available>MagePsycho_Shipmentfilter_Block_Onepage_Shipping_Method_Available</onepage_shipping_method_available>
		</rewrite>
	</checkout>
	...
</blocks>
...

2. Override the getShippingRates() method

File: app/code/local/MagePsycho/Shipmentfilter/Block/Onepage/Shipping/Method/Available.php:
Code:

<?php
/**
 * @category   MagePsycho
 * @package    MagePsycho_Shipmentfilter
 * @author     magepsycho@gmail.com
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
class MagePsycho_Shipmentfilter_Block_Onepage_Shipping_Method_Available extends Mage_Checkout_Block_Onepage_Shipping_Method_Available
{
	public function getShippingRates()
	{
		$rates = parent::getShippingRates();
		if (array_key_exists('freeshipping', $rates)) {
			$rates = array('freeshipping' => $rates['freeshipping']);
		}

		return $rates;
	}
}

3. Refresh the cache. Bingo!

How to find the size / rows of Magento database & tables?

February 17, 2013  |  1 Comments  |  by Raj (MagePsycho)  |  Latest, Magento, Mysql

Introduction

Magento is a giant e-commerce application having more than 300 tables. It uses an eav model concept and provides different complex features which makes the database huge.

Sometimes you may wonder what’s the database size of your Magento database or individual tables so that you can work on some optimization task or freeing some of your server space.
Here we are going to discuss some SQL queries which seems to be helpful.

SQL Snippets

1. Find size & rows of Magento database

SELECT 
  TABLE_SCHEMA AS "Database",
  SUM(TABLE_ROWS) AS "Rows #",
  ROUND(
    SUM(DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024,
    2
  ) AS "Size (MB)" 
FROM
  information_schema.TABLES 
WHERE information_schema.TABLES.TABLE_SCHEMA = 'database-name' 
 GROUP BY TABLE_SCHEMA;

2. Find size & rows of Magento database tables

SELECT 
  TABLE_NAME AS "Table",
  TABLE_ROWS AS "Rows #",
  ROUND(
    (DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024,
    2
  ) AS "Size (MB)" 
FROM
  information_schema.TABLES 
WHERE information_schema.TABLES.TABLE_SCHEMA = 'database-name' 

3. Find size & rows of Magento log tables

SELECT 
  TABLE_NAME AS "Table",
  TABLE_ROWS AS "Rows #",
  ROUND(
    (DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024,
    2
  ) AS "Size (MB)" 
FROM
  information_schema.TABLES 
WHERE information_schema.TABLES.TABLE_SCHEMA = 'database-name' 
  AND (
    TABLE_NAME LIKE 'log_%' 
    OR TABLE_NAME LIKE 'report_%' 
    OR TABLE_NAME LIKE 'dataflow_%' 
    OR TABLE_NAME = 'catalog_compare_item'
  ) 
ORDER BY TABLE_ROWS DESC 

Magento log tables info

These queries can be useful for optimizing your database. Suppose say if log tables are huge with large no of data then you can optimize the log tables by truncating them using following SQL:

TRUNCATE dataflow_batch_export;
TRUNCATE dataflow_batch_import;
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;
TRUNCATE log_visitor_online;
TRUNCATE report_viewed_product_index;
TRUNCATE report_compared_product_index;
TRUNCATE report_event;
TRUNCATE index_event;
TRUNCATE catalog_compare_item;

Caution: Always take a DB backup before performing truncate operation.

Hope you found this article useful.
Thanks for reading!