Category: magento


In this post I will show you how to write a basic magento model to do AMD activities to a db table.

Under package “Mypackage” & module “Mymod” we will create model Test so that we can do AMD with table named “test”.

1.Here we first create a table test in db.

CREATE TABLE `test` (
`test_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 25 ) NOT NULL
) ENGINE = MYISAM 

2.Create shell module Mymod under package Mypackage

//file:- /app/etc/modules/Mypackage_Mymod.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mypackage_Mymod>
            <active>true</active>
            <codePool>local</codePool>
        </Mypackage_Mymod>
    </modules>
</config> 

3.Write config.xml for Mymod module. Here we declate a handler for table test .
//file:-/app/code/local/Mypackage/Mymod/etc/config.xml

/
<?xml version="1.0"?>
<config>
    <modules>
        <Mypackage_Mymod>
            <version>0.1.0</version>
        </Mypackage_Mymod>
    </modules>
    <global>
        <models>
			<mymod>
				<!-- Init model for mymod module -->
				<class>Mypackage_Mymod_Model</class>
				<!-- Init db config handler for mymod models -->
				<resourceModel>mymod_mysql4</resourceModel>
			</mymod>
			
			<!-- declaring model vs db table relation -->
            <mymod_mysql4>
                <class>Mypackage_Mymod_Model_Mysql4</class>
				<!-- declate table test -->
                <entities>
                    <test>
                        <table>test</table>
                    </test>
                </entities>
				<!-- -/- -->
            </mymod_mysql4>
			<!-- -/- -->
        </models>
		
		
		<!-- Setup db read & write connection for Mymod module -->
		<resources>
			<!-- db write connection -->
			<mymod_write>
				<connection>
					<use>core_write</use>
				</connection>
			</mymod_write>
			<!-- db read connection -->
			<mymod_read>
				<connection>
					<use>core_read</use>
				</connection>
			</mymod_read>
		</resources>
		<!-- -/- -->
    </global>
</config>

4.Write model Test.php. Here we configure this model with the handler of table test.
file:-/app/code/local/Mypackage/Mymod/model/Test.php

<?php

class Mypackage_Mymod_Model_Test extends Mage_Core_Model_Abstract
{
	
	public function _construct()
    {
        parent::_construct();
        $this->_init('mymod/test');
    }
}

5.Create the resource model for model test.
file:-/app/code/local/Mypackage/Mymod/model/Mysql4/Test.php. here we also configure the primary key id of the table test.

<?php

class Mypackage_Mymod_Model_Mysql4_Test extends Mage_Core_Model_Mysql4_Abstract
{
    public function _construct()
    {    
        $this->_init('mymod/test', 'test_id');
    }
}

6.Create a collection class so that we can retrive data from table test.
file:-/local/Mypackage/Mymod/model/Mysql4/Test/Collection.php

<?php

class Mypackage_Mymod_Model_Mysql4_Test_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
    public function _construct()
    {
        parent::_construct();
        $this->_init('Mymod/test');
    }
}

That’s it we are done. Now we can do AMD operation with table test with model test.

	$model = Mage::getModel('mymod/test')
		->setName('Asad Rahman')
		->save();

Also use the test collection model to retrive collection data from table test.

	$model = Mage::getModel('mymod/test')
	$data = $model->getCollection()->getData();
	print_r($data);
$productId = 10; 
$attributeName = 'my_attribute_name';

$product = Mage::getModel('catalog/product')->load($productId);
$attributes = $product->getAttributes();

$attributeValue = null;		
if(array_key_exists($attributeName , $attributes)){
	$attributesobj = $attributes["{$attributeName}"];
	$attributeValue = $attributesobj->getFrontend()->getValue($product);
}
echo $attributeValue; //attribute value for 'my_attribute_name'

Here is a code snippet exaple how you fetch all products by it’s attribute set name:-

//Fetch attribute set id by attribute set name
$attrSetName = 'my_custom_attribute';
$attributeSetId = Mage::getModel('eav/entity_attribute_set')
	->load($attrSetName, 'attribute_set_name')
	->getAttributeSetId();

//Load product model collecttion filtered by attribute set id
$products = Mage::getModel('catalog/product')
	->getCollection()
	->addAttributeToSelect('name')
	->addFieldToFilter('attribute_set_id', $attributeSetId);

//process your product collection as per your bussiness logic
$productsName = array();
foreach($products as $p){
	$productsName[] = $p->getData('name');
}
//return all products name with attribute set 'my_custom_attribute'
print_r($productsName); 

That’s it, have happy time with magento 🙂 !!!

Percentage off tier pricing in magento

Magento has an awesome feature for setting different tiered pricing for different quantity and also for different customer group. But sometimes you may feel this is problematic since you have to set a fixed price for tier items.

So, Lets make our hands dirty to extend this feature so that we can set a percentage which % amount will be deducted from the item price.

Step1

Magento keeps product’s tier pricing information in product_entity_tier_price table. Fist lets create a new column “tier_type” to keep track which type(Fixed / percentage) of tier pricing to be applied.

File:/app/etc/modules/Mypackage_Tierpricing.xml

<?xml version="1.0"?>
<config>
	<modules>

		<Mypackage_Tierpricing>
			<active>true</active>
			<codePool>local</codePool>
		</Mypackage_Tierpricing>

	</modules>
</config>

File:/app/code/local/Mypackage/Tierpricing/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <Mypackage_Tierpricing>
            <version>0.1.0</version>
        </Mypackage_Tierpricing>
    </modules>
	<global>
        <resources>
            <tierpricing_setup>
                <setup>
                    <module>Mypackage_Tierpricing</module>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </tierpricing_setup>
            <tierpricing_write>
                <connection>
                    <use>core_write</use>
                </connection>
            </tierpricing_write>
            <tierpricing_read>
                <connection>
                    <use>core_read</use>
                </connection>
            </tierpricing_read>
        </resources>
    </global>
</config>

File: /app/code/local/Mypackage/Tierpricing/sql/tierpricing_setup/mysql4-install-0.1.0.php

<?php

	$installer = $this;
	$installer->startSetup();

	$installer->run(
		"ALTER TABLE {$this->getTable('catalog_product_entity_tier_price')}  ADD tier_type INT NOT NULL AFTER qty ; ");
	$installer->endSetup();

Step2

Lets change the product admin panel to save/update tier type (Fixed/percentage). We override the block Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Attribute_Backend_Tierprice so that it fetches the “tier_type”
from db.

File:-app/code/local/Mypackage/Catalog/Model/Product/Attribute/Backend/Tierprice.php


<?php

class Mypackage_Catalog_Model_Resource_Eav_Mysql4_Product_Attribute_Backend_Tierprice extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Attribute_Backend_Tierprice

{
    public function loadProductPrices($product, $attribute)
    {
		//add new column name to the sql
        $select = $this->_getReadAdapter()->select()
            ->from($this>getMainTable(), array(
                'website_id', 'all_groups', 'cust_group' => 'customer_group_id',
                'price_qty' => 'qty', 'price' => 'value', 'tier_type',
            ))
            ->where('entity_id=?', $product->getId())
            ->order('qty');
        if ($attribute->isScopeGlobal()) {
            $select->where('website_id=?', 0);
        }
        else {
            if ($storeId = $product->getStoreId()) {
                $select->where('website_id IN (?)', array(0, Mage::app()->getStore($storeId)->getWebsiteId()));
            }
        }
        return $this->_getReadAdapter()->fetchAll($select);
    }
}

?>

To save tier_type we to override the backend model Mage_Catalog_Model_Product_Attribute_Backend_Tierprice.

File:-app/code/local/Mypackage/Catalog/Model/Resource/Eav/Mysql4/Product/Attribute/Backend/Tierprice.php

<?php
class Mypackage_Catalog_Model_Product_Attribute_Backend_Tierprice extends Mage_Catalog_Model_Product_Attribute_Backend_Tierprice
{
    public function afterSave($object)
    {
        $this->_getResource()->deleteProductPrices($object, $this->getAttribute());
        $tierPrices = $object->getData($this->getAttribute()->getName());

        if (!is_array($tierPrices)) {
            return $this;
        }

        $prices = array();
        foreach ($tierPrices as $tierPrice) {
            if (empty($tierPrice['price_qty']) || !isset($tierPrice['price']) || !empty($tierPrice['delete'])) {
                continue;
            }

            $useForAllGroups = $tierPrice['cust_group'] == Mage_Customer_Model_Group::CUST_GROUP_ALL;
            $customerGroupId = !$useForAllGroups ? $tierPrice['cust_group'] : 0;
            $priceKey = join('-', array(
                $tierPrice['website_id'],
                intval($useForAllGroups),
                $customerGroupId,
                $tierPrice['price_qty']
            ));
			//Add new column "tier_type"
            $prices[$priceKey] = array(
                'website_id'        => $tierPrice['website_id'],
                'all_groups'        => intval($useForAllGroups),
                'customer_group_id' => $customerGroupId,
                'qty'               => $tierPrice['price_qty'],
                'value'             => $tierPrice['price'],
				'tier_type'			=> $tierPrice['tier_type'],
            );
        }

        if ($this->getAttribute()->getIsGlobal() == Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_WEBSITE) {
            if ($storeId = $object->getStoreId()) {
                $websites = array(Mage::app()->getStore($storeId)->getWebsite());
            }
            else {
                $websites = Mage::app()->getWebsites();
            }

            $baseCurrency   = Mage::app()->getBaseCurrencyCode();
            $rates          = $this->_getWebsiteRates();
            foreach ($websites as $website) {
                /* @var $website Mage_Core_Model_Website */
                if (!is_array($object->getWebsiteIds()) || !in_array($website->getId(), $object->getWebsiteIds())) {
                    continue;
                }
                if ($rates[$website->getId()]['code'] != $baseCurrency) {
                    foreach ($prices as $data) {
                        $priceKey = join('-', array(
                            $website->getId(),
                            $data['all_groups'],
                            $data['customer_group_id'],
                            $data['qty']
                        ));
                        if (!isset($prices[$priceKey])) {
                            $prices[$priceKey] = $data;
                            $prices[$priceKey]['website_id'] = $website->getId();
                            $prices[$priceKey]['value'] = $data['value'] * $rates[$website->getId()]['rate'];
                        }
                    }
                }
            }
        }

        foreach ($prices as $data) {
            $this->_getResource()->insertProductPrice($object, $data);
        }

        return $this;
    }
}

Change the admin phtml for tier pricing.
File:-/app/design/adminhtml/default/default/template/catalog/product/edit/price/tier.phtml

<?php
<?php $_htmlId      = $this->getElement()->getHtmlId() ?>
<?php $_htmlClass   = $this->getElement()->getClass() ?>
<?php $_htmlName    = $this->getElement()->getName() ?>
<?php $_readonly    = $this->getElement()->getReadonly() ?>
<?php $_multiWebsite= $this->isMultiWebsites(); ?>
<tr>
    <td class="label"><?php echo $this->getElement()->getLabel() ?></td>
</tr>
<tr>	
    <td colspan="10" class="grid tier">
    <table cellspacing="0" class="data border" id="tiers_table">
        <?php if ($_multiWebsite): ?>
        <col width="135" />
        <?php endif; ?>
        <col width="120" />
        <col width="60" />
        <col width="140"/>
        <col width="60" />
        <thead>
		
            <tr class="headings">
                <th <?php if (!$_multiWebsite): ?>style="display:none"<?php endif; ?>><?php echo Mage::helper('sales')->__('Website') ?></th>
                <th><?php echo Mage::helper('catalog')->__('Customer Group') ?></th>
                <th><?php echo Mage::helper('catalog')->__('Qty') ?></th>
				<!-- Added -->
				<th><?php echo Mage::helper('catalog')->__('Type') ?></th>
				<!-- -/- -->
                <th><?php echo $this->getPriceColumnHeader(Mage::helper('catalog')->__('Price')) ?></th>
                <th class="last"><?php echo Mage::helper('catalog')->__('Action') ?></th>
            </tr>
			
            <tr id="<?php echo $_htmlId ?>_add_template" class="template no-display">
				<!-- Website list -->
                <td <?php if (!$_multiWebsite): ?>style="display:none"<?php endif; ?>>
                <select disabled="no-template" class="<?php echo $_htmlClass ?> required-entry" name="<?php echo $_htmlName ?>[__index__][website_id]" id="tier_price_row___index___website">
                    <?php foreach ($this->getWebsites() as $_websiteId => $_info): ?>
                    <option value="<?php echo $_websiteId ?>"><?php echo $_info['name'] ?><?php if (!empty($_info['currency'])): ?> [<?php echo $_info['currency'] ?>]<?php endif; ?></option>
                    <?php endforeach ?>
                </select>
                </td>
				<!-- -/- -->
				
				<!-- Customer Group -->
                <td>
                <select disabled="no-template" class="<?php echo $_htmlClass ?> custgroup required-entry" name="<?php echo $_htmlName ?>[__index__][cust_group]" id="tier_price_row___index___cust_group">
                    <?php foreach ($this->getCustomerGroups() as $_groupId=>$_groupName): ?>
                    <option value="<?php echo $_groupId ?>"><?php echo htmlspecialchars($_groupName) ?></option>
                    <?php endforeach ?>
                </select>
                </td>
				<!-- -/- -->
				
				<!-- Qty -->
                <td class="nobr">
                    <input disabled="no-template" class="<?php echo $_htmlClass ?> qty required-entry validate-greater-than-zero" type="text" name="<?php echo $_htmlName ?>[__index__][price_qty]" value="'#{qty}'" /> <small class="nobr"><?php echo Mage::helper('catalog')->__('and above')?></small>
                </td>
				<!-- -/- -->
				
				<!-- Added type-->
				<td class="nobr">
				<select disabled="no-template" class="<?php echo $_htmlClass ?> tiertype required-entry" name="<?php echo $_htmlName ?>[__index__][tier_type]" id="tier_price_row___index___tier_type" >
					<option value="0">Fixed</option>
					<option value="1">% Off</option>
				</select>
                </td>
				<!-- -/- -->
				
				<!-- Price -->
                <td>
                    <input disabled="no-template" class="<?php echo $_htmlClass ?> required-entry validate-greater-than-zero" type="text" name="<?php echo $_htmlName ?>[__index__][price]" value="'#{price}'" />
                </td>
				<!-- -/- -->
				
				<!-- Button -->
                <td class="last"><input type="hidden" name="<?php echo $_htmlName ?>[__index__][delete]" class="delete" disabled="no-template" value="" /><button title="Delete Tier" class="scalable delete icon-btn delete-product-option" onclick="tierPriceControl.deleteItem(event);return false"><span>Delete</span></button></td>
				<!-- -/- -->
            </tr>
        </thead>
        <tfoot>
            <tr>
                <td <?php if (!$_multiWebsite): ?>style="display:none"<?php endif; ?>></td>
                <td colspan="5" class="a-right"><?php echo $this->getAddButtonHtml() ?></td>
            </tr>
        </tfoot>
        <tbody id="<?php echo $_htmlId ?>_container">
        </tbody>
    </table>

<script type="text/javascript">
//<![CDATA[
    var tierPriceControl = {
        template : new Template('<tr>' + $('<?php echo $_htmlId ?>_add_template').innerHTML.replace(/__index__/g, '#{index}').replace(/ disabled="?no-template"?/g, '').replace(/ disabled/g, '').replace(/="'([^']*)'"/g, '="$1"') + '</tr>'),
        itemsCount : 0,
        deleteButton: false,
        addItem  : function () {
            <?php if ($_readonly): ?>
            if (arguments.length < 4) {
                return;
            }
            <?php endif; ?>
            var data = {};
            data.website_id = 0;
            data.group = '<?php echo $this->getDefaultCustomerGroup() ?>';
            data.qty = '';
            data.price = '';
            data.index = this.itemsCount++;
            if(arguments.length == 5) {
                data.website_id = arguments[0];
                data.group      = arguments[1];
                data.qty        = arguments[2];
                data.price      = arguments[3];
				data.tier_type  = arguments[4];
            }
            Element.insert($('<?php echo $_htmlId ?>_container'), {'bottom':this.template.evaluate(data)});
            $('tier_price_row_'+data.index+'_cust_group').value = data.group;
            $('tier_price_row_'+data.index+'_website').value 	= data.website_id;
            $('tier_price_row_'+data.index+'_tier_type').value 	= data.tier_type;
            <?php if ($_readonly): ?>
            $('<?php echo $_htmlId ?>_container').select('input', 'select')
                .each(this.disableElement);
            $('<?php echo $_htmlId ?>_container').up('table').select('button')
                .each(this.disableElement);
            <?php endif; ?>
        },
        disableElement: function(elem) {
            elem.disabled = true
            elem.addClassName('disabled');
        },
        deleteItem : function(event) {
            var tr = Event.findElement(event, 'tr');
            if (tr) {
                Element.select(tr, '.delete').each(function(elem){elem.value='1'});
                Element.select(tr, ['input', 'select']).each(function(elem){elem.hide()});
                Element.hide(tr);
                Element.addClassName(tr, 'no-display template');
            }
        }
    }
    <?php foreach ($this->getValues() as $_item): ?>

    tierPriceControl.addItem('<?php echo $_item['website_id'] ?>', '<?php echo $_item['cust_group'] ?>', '<?php echo $_item['price_qty']*1 ?>','<?php echo sprintf('%.2f', $_item['price']) ?>','<?php echo $_item['tier_type'] ?>');
    <?php endforeach; ?>
    <?php if ($_readonly): ?>
        $('<?php echo $_htmlId ?>_container').up('table').select('button')
            .each(tierPriceControl.disableElement);
    <?php endif; ?>
//]]>
</script>
</td>
</tr>

Step 3

To change tier pricing we override the Mage_Catalog_Model_Product_Type_Price model’s getTierPrice method.

File:-/app/code/local/Mypackage/Catalog/Model/Product/Type/Price.php

<?php
class Mypackage_Catalog_Model_Product_Type_Price extends Mage_Catalog_Model_Product_Type_Price
{
	public function getTierPrice($qty=null, $product)
    {
        $allGroups = Mage_Customer_Model_Group::CUST_GROUP_ALL;
        $prices = $product->getData('tier_price');

        if (is_null($prices)) {
            if ($attribute = $product->getResource()->getAttribute('tier_price')) {
                $attribute->getBackend()->afterLoad($product);
                $prices = $product->getData('tier_price');
            }
        }
		
        if (is_null($prices) || !is_array($prices)) {
            if (!is_null($qty)) {
                return $product->getPrice();
            }
            return array(array(
                'price'         => $product->getPrice(),
                'website_price' => $product->getPrice(),
                'price_qty'     => 1,
                'cust_group'    => $allGroups,
            ));
        }

        $custGroup = $this->_getCustomerGroupId($product);
        if ($qty) {
            $prevQty = 1;
            $prevPrice = $product->getPrice();
            $prevGroup = $allGroups;
            foreach ($prices as $price) {
                if ($price['cust_group']!=$custGroup && $price['cust_group']!=$allGroups) {
                    // tier not for current customer group nor is for all groups
                    continue;
                }
                if ($qty < $price['price_qty']) {
                    // tier is higher than product qty
                    continue;
                }
                if ($price['price_qty'] < $prevQty) {
                    // higher tier qty already found
                    continue;
                }
                if ($price['price_qty'] == $prevQty && $prevGroup != $allGroups && $price['cust_group'] == $allGroups) {
                    // found tier qty is same as current tier qty but current tier group is ALL_GROUPS
                    continue;
                }

                $prevPrice  = $price['website_price'];
                $prevQty    = $price['price_qty'];
				if($price['tier_type'] == 1 && $price['price_qty'] <= 100){
					$off = ($price['website_price'] * $product->getPrice())/100;
					$prevPrice = $product->getPrice()  - $off; 
					
				}
                $prevGroup  = $price['cust_group'];
            }
            return $prevPrice;
        } else {
            foreach ($prices as $i=>$price) {
                if ($price['cust_group']!=$custGroup && $price['cust_group']!=$allGroups) {
                    unset($prices[$i]);
                }
            }
        }

        return ($prices) ? $prices : array();
    }
}
?>

At last register all these override into config.xml.

File:-/app/code/local/Mypackage/Catalog/etc/config.xml

<?xml version="1.0"?>
<config>

	<modules>
	   <Mypackage_Catalog>
		 <version>0.1.0</version>
	   </Mypackage_Catalog>
	</modules>
	
		
    <global>
		<models>
            <catalog>
                <rewrite>
					<product_type_price>Mypackage_Catalog_Model_Product_Type_Price</product_type_price>
					<product_attribute_backend_tierprice>Mypackage_Catalog_Model_Product_Attribute_Backend_Tierprice</product_attribute_backend_tierprice>
				</rewrite>
			</catalog>							
			<catalog_resource_eav_mysql4>
                <rewrite>
					<product_attribute_backend_tierprice>Mypackage_Catalog_Model_Resource_Eav_Mysql4_Product_Attribute_Backend_Tierprice</product_attribute_backend_tierprice>
                </rewrite>							
			</catalog_resource_eav_mysql4>
		</models>
	</global>

</config>

Register this local catalog module in etc.

File:/app/etc/modules/Mypackage_Catalog.xml

<?xml version="1.0"?>
<config>
	<modules>

		<Mypackage_Catalog>
			<active>true</active>
			<codePool>local</codePool>
		</Mypackage_Catalog>

	</modules>
</config>