Articles & Tutorials
Hide Price for Guest Users
Hello,
[Magento version 1.7]
A pretty common question we hear from Magento users and on the Magento forums is how to hide the product prices, both from the catalog and product page from guest users and show it only to logged in users. So today we will discuss how to do it and make a custom module out of it. You can download the full extension from http://aumento.ballistichq.com/extension/pricehide-hide-price-magento-catalog-page/
Now moving on to the code. The first file is the module installation file. This XML lets Magento know that we are creating a new extension and that it should look into the module perspective folders.
Module Installation File:
/app/etc/modules/Aumento_PriceHide.xml
<config> <modules> <Aumento_PriceHide> <active>true</active> <codePool>local</codePool> </Aumento_PriceHide> </modules> </config>
Two configuration files are still required, the module configuration file:
Module Configuration File:
/app/code/local/Aumento/PriceHide/etc/config.xml
<config> <modules> <Aumento_PriceHide> <version>1.0</version> </Aumento_PriceHide> </modules> <global> <blocks> <catalog> <rewrite> <product_price>Aumento_PriceHide_Block_Price</product_price> </rewrite> </catalog> </blocks> <helpers> <pricehide> <class>Aumento_PriceHide_Helper</class> </pricehide> </helpers> </global> </config>
And the admin configuration file, this file insert all the configuration fields to Magento Backend:
Module Admin Configuration File:
/app/code/local/Aumento/PriceHide/etc/system.xml
<config> <sections> <catalog> <groups> <pricehideconfig translate="label"> <label>Aumento.io Price Hide</label> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> <fields> <active translate="label"> <label>Enabled</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_model> <sort_order>1</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> </active> <title translate="label"> <label>Message to display</label> <frontend_type>text</frontend_type> <sort_order>2</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> </title> </fields> </pricehideconfig> </groups> </catalog> </sections> </config>
Now to the actual code, we are going to override Mage_Catalog_Block_Product_Price, this Block is responsible for displaying the product prices all over magento. We simply read the module settings from the configuration tables, and then override the _toHtml function. This function is responsible for rendering the block to presentation code. We are going to check the user login state and then load our template if both the module is enabled, and the user is not logged in. If that’s not the case, we will simply load the default template.
Module Block File:
/app/code/local/Aumento/PriceHide/BlockPrice.php
class Aumento_PriceHide_Block_Price extends Mage_Catalog_Block_Product_Price {
public $config_message = '';
private $config_enabled = '';
protected function _construct() {
parent::_construct();
$this->config_message = Mage::getStoreConfig('catalog/pricehideconfig/title');
$this->config_enabled = Mage::getStoreConfig('catalog/pricehideconfig/active');
}
protected function _toHtml(){
if ( ($this->config_enabled == 1) && !($this->helper('customer')->isLoggedIn()) ) {
$this->setTemplate('pricehide/price.phtml');
}
if (!$this->getProduct() || $this->getProduct()->getCanShowPrice() === false) {
return '';
}
return parent::_toHtml();
}
}
Now comes the presentation part, a one liner phtml file that loads a custom message set in the module configuration.
Module Template File:
/app/design/frontend/package/theme/template/pricehide/price.phtml
<p> <?php echo $this->config_message; ?> </p>
Am empty Helper file is also required for the module to work. Get the full module at http://aumento.ballistichq.com/extension/pricehide-hide-price-magento-catalog-page/ . And that’s how you do it.
Add a CMS Page to the Navigation Menu
Hi,
[Magento version 1.7]
Today we’re gonna create a simple module to allow the addition of a link to a specific cms page to your website navigational menu, maybe an About Page or Contact Us. We can simply do that by editing the top navigation file located at /app/design/frontend/abc/theme/template/catalog/navigation/top.phtml but this would leave a .last class attribute to the last category and this might miss the design a bit. So what we will be doing is overriding renderCategoriesMenuHtml function which is responsible for drawing the menu items.
Module Installation File:
/app/etc/modules/ABC_Catalog.xml
<?xml version="1.0"?> <config> <modules> <ABC_Catalog> <active>true</active> <codePool>local</codePool> </ABC_Catalog> </modules> </config>
Moving on to the module configuration:
Module Configuration File:
/app/code/local/ABC/Catalog/etc/config.xml
<?xml version="1.0"?> <config> <modules> <ABC_Catalog> <version>1.0</version> </ABC_Catalog> </modules> <global> <blocks> <catalog> <rewrite> <navigation>ABC_Catalog_Block_Navigation</navigation> </rewrite> </catalog> </blocks> </global> </config>
Now to some actual code, we are overriding the render category item with a slightly modified version that takes an extra parameter that specifies whether to append the last class attribue or not to the last drawn category.
Navigation Class:
/app/code/local/ABC/Catalog/Block/Navigation.php
<?php
/**
* Catalog navigation
*
* @category Mage
* @package Mage_Catalog
* @author Yehia Abdel Salam <yehiasalam@cairocubicles.com>
*/
class ABC_Catalog_Block_Navigation extends Mage_Catalog_Block_Navigation
{
/**
* Render categories menu in HTML
*
* @param int Level number for list item class to start from
* @param string Extra class of outermost list items
* @param string If specified wraps children list in div with this class
* @return string
*/
public function renderCategoriesMenuHtml($level = 0, $outermostItemClass = '', $childrenWrapClass = '', $markLast = true)
{
$activeCategories = array();
foreach ($this->getStoreCategories() as $child) {
if ($child->getIsActive()) {
$activeCategories[] = $child;
}
}
$activeCategoriesCount = count($activeCategories);
$hasActiveCategoriesCount = ($activeCategoriesCount > 0);
if (!$hasActiveCategoriesCount) {
return '';
}
$html = '';
$j = 0;
foreach ($activeCategories as $category) {
$html .= $this->_renderCategoryMenuItemHtml(
$category,
$level,
( $markLast ? ($j == $activeCategoriesCount - 1) : false ) ,
($j == 0),
true,
$outermostItemClass,
$childrenWrapClass,
true
);
$j++;
}
return $html;
}
}
Now we could open /app/design/frontend/abc/theme/template/catalog/navigation/top.phtml and add something like this, note that we set the last parameter of renderCategoriesMenuHtml to false so it wont append any last classes
<?php $_menu = $this->renderCategoriesMenuHtml(0,'level-top', '', false) ?>
<?php if($_menu): ?>
<div class="nav-container">
<ul id="nav">
<?php echo $_menu ?>
<li class="level0 nav-6 level-top last parent">
<a href="<?php echo $this->getUrl('about-us')?>" class="level-top">
<span>Programs & Services</span>
</a>
</li>
</ul>
</div>
<?php endif ?>
And thats how you create an entire module to do trivial stuff in magento.
Payments Methods Based On Customer Groups
Hi,
[Magento version 1.4.1.1]
Our main aim in this post is to offer a specific payment method(whether it’s PayPal, Check/Money Order or CreditCard) based on the group of the logged in customer. Furthermore, we’re going to create a new Order Status where each new order generated from our custom customer group automically get this order status assigned. We’re going to maintain Magento modularity by encapsulate this functionality in a module.
Let’s start by the module installation file, we’ll user ABC as our namespace and XYZ as our module name:
Module Installation File:
/app/etc/modules/ABC_XYZ.xml
<?xml version="1.0"?> <config> <modules> <ABC_XYZ> <active>true</active> <codePool>local</codePool> </ABC_XYZ> </modules> </config>
Nothing interesting going on here, so let’s move to the module configuration file:
Module Configuration File:
/app/code/local/ABC/XYZ/etc/config.xml
<?xml version="1.0"?> <config> <modules> <ABC_XYZ> <version>1.0</version> </ABC_XYZ> </modules> <global> <blocks> <checkout> <rewrite> <onepage_payment_methods>ABC_XYZ_Block_Onepage_Payment_Methods</onepage_payment_methods> </rewrite> </checkout> </blocks> <sales> <order> <statuses> <expedite translate="label"><label>Expedite</label></shop> </statuses> </order> </sales> </global> <frontend> <events> <checkout_type_onepage_save_order_after> <observers> <abc_xyz_model_type_observer> <type>model</type> <class>ABC_XYZ_Model_Type_Observer</class> <method>ProcessShopOrder</method> </abc_xyz_model_type_observer> </observers> </checkout_type_onepage_save_order_after> </events> </frontend> </config>
What we’re basically doing here are three things, first we are overriding the block responsible for showing each of the payments methods on the checkout. We also add our new order status Expedite. The last part is the most interesting one, we’re registering a new class we created as an observer for the checkout_type_onepage_save_order_after event. This means that whenever Magento receives an order from the user and saves it, it automatically calls our function. This is a common pattern that you can find everywhere (native win32 application using the available windows hooks for example).
We will also need to add another field in Magento Admin panel where we can set which customer group can use this payment methods, we can do this for the Check / Money Order methods with the following code, other payments methods can be added by changing the checkmo tag:
Module System Configuration File:
/app/code/local/ABC/XYZ/etc/system.xml
<config> <sections> <payment translate="label" module="payment"> <groups> <checkmo translate="label"> <fields> <specificgroups translate="label"> <label>Payment for Specific Customer Groups</label> <frontend_type>multiselect</frontend_type> <sort_order>52</sort_order> <source_model>adminhtml/system_config_source_customer_group</source_model> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </specificgroups> </fields> </checkmo> </groups> </payment> </sections> </config>
Now some PHP coding, we start with the observer class:
Observer Class:
/app/code/local/ABC/XYZ/Model/Type/Observer.php
<?php
class ABC_XYZ_Model_Type_Observer {
public function __construct(){
/*
* Event-Observer Magento
*/
}
public function ProcessShopOrder($observer) {
$method = Mage::getSingleton('checkout/session')->getQuote()->getPayment()->getMethodInstance();
$method_code = $method->getCode();
if ($method_code == 'checkmo') {
if (Mage::getSingleton('customer/session')->isLoggedIn()) {
$customer = Mage::getSingleton('customer/session');
$data = $customer->getCustomer();
$group_id = $data->getGroupId();
$specificgroups = explode(",",$method->getConfigData('specificgroups'));
if( in_array($group_id,$specificgroups) ){
$order= $observer->getEvent()->getOrder();
$order->setStatus('expedite')->save();
}
}
}
}
}
?>
We are setting here the status of any order belonging to the customer group we selected from Magento Admin Panel to Expedite. The only thing remaining to override the block responsible for showing the payments methods in the checkout:
The Block
/app/code/local/ABC/XYZ/Block/Onepage/Payment/Methods.php
<?php
class ABC_XYZ_Block_Onepage_Payment_Methods extends Mage_Checkout_Block_Onepage_Payment_Methods {
protected function _canUseMethod($method) {
/**
* Check for User Groups
*/
if ($method->getCode() == 'checkmo') {
if (Mage::getSingleton('customer/session')->isLoggedIn()) {
$customer = Mage::getSingleton('customer/session');
$data = $customer->getCustomer();
$group_id = $data->getGroupId();
$specificgroups = explode(",",$method->getConfigData('specificgroups'));
$testgroup = trim($method->getConfigData('specificgroups'));
if(!in_array($group_id,$specificgroups) && $testgroup!==""){
return false;
}
} else return false;
}
return parent::_canUseMethod($method);
}
}
?>
That’s about it, a bit long but easy to follow.
Modules URL Structure
Hi,
A very nice example from http://stackoverflow.com/questions/576908/how-does-magento-code-work/3581506#3581506 that always saves time:
example.com/magento/index.php/helloworld
is transparently pointing to
example.com/magento/index.php/helloworld/index/index
so basically you have the following URI structure for modules
example.com/magento/index.php/:module/:controller/:action/:param1[/:paramN ...]
Facebook Like Button on the Product Page
Hi,
[Megento Version: 1.4.1.1]
We’re going to add today the Facebook Like Button you’ve see everywhere on the Magento Product page. We’ll start by adding the Open Graph Meta Tags required by Facebook Like button to the head template.
/app/frontend/default/default/template/page/html/head.phtml
<?php if (Mage::registry('current_product')) { ?>
<!--start Facebook Open Graph Protocol-->
<meta property="og:site_name" content="Your Site Name" />
<meta property="og:title" content="<?php echo $this->getTitle() ?>" />
<meta property="og:type" content="product" />
<meta property="og:url" content="<?php echo Mage::helper('core/url')->getCurrentUrl() ?>"/>
<meta property="og:image" content="<?php echo Mage::helper('catalog/image')->init(Mage::registry('current_product'), 'small_image')->resize(100,100);?>"/>
<meta property="og:description" content="<?php echo $this->getDescription() ?>"/>
<meta property="fb:admins" content="your_profile_id_here" />
<!--end Facebook Open Graph Protocol-->
<?php } ?>
We also need to include the Facebook javascript SDK for this to work and parse the XFBML tag (Facebook basically replace the <fb:like>آ tag with an iFrame on pageload )
<script src="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBM
});
</script>
We can put the script to either footer.phtml or head.phtml. Performance-wise, it’s recommend to put it just before the body closing tag, i.e. in footer.phtml, however IE8 didn’t seem to honor this preference and we had to use the head.html file to make it work on IE.
The last step is to add the like button itself on the product page:
/app/frontend/default/default/template/catalog/product/view.phtml
<!-- Facebook Like Button - Start-->
<div class="facebook">
<fb:like href="<?php echo Mage::helper('core/url')->getCurrentUrl() ?>" layout="standard" show_faces="true" action="like" />
</div>
<!-- Facebook Like Button - End -->
We can put this anywhere we like the facebook button to appear on the page, we placed it just below the short description. You can also customize with different attribute, refer to the Facebook Like Button page for more details.
That should be it, hope that helped.
Mini Login Form Magento
Hi,
[Magento Version 1.4.1.1]
Magento provides a small login form that the user could use without navigating away from his current page, we can see this in customer.xml layout:
<!--
Load this update on every page when customer is logged in
-->
<customer_logged_in>
<reference name="top.links">
<action method="addLink" translate="label title" module="customer"><label>Log Out</label><url helper="customer/getLogoutUrl"/><title>Log Out</title><prepare/><urlParams/><position>100</position></action>
</reference>
</customer_logged_in>
<!--
Load this update on every page when customer is logged out
-->
<customer_logged_out>
<reference name="right">
<block type="customer/form_login" name="customer_form_mini_login" before="-" template="customer/form/mini.login.phtml"/>
</reference>
<reference name="top.links">
<action method="addLink" translate="label title" module="customer"><label>Log In</label><url helper="customer/getLoginUrl"/><title>Log In</title><prepare/><urlParams/><position>100</position></action>
</reference>
<remove name="wishlist_sidebar"></remove>
<remove name="reorder"></remove>
</customer_logged_out>
We are using the mini login form on the homepage, and we needed a way to display a different message depending on the user login status, the default behavior was to hide the whole form (we can see that the block is missing from the customer_logged_in login tag).
We change the mini form template to fit our needs as follows:
/app/design/frontend/default/default/template/customer/form/mini.login.phtml
<?php $rLoggedIn = $this->helper('customer')->isLoggedIn(); ?>
<div class="block block-login">
<div class="block-title">
<strong><span><?php echo $rLoggedIn ? 'WELCOMe' : $this->__('Login') ?></span></strong>
</div>
<?php if ($rLoggedIn) { ?>
<p class="welcome-msg" style="margin: 15px;">
Welcome back <strong><?php echo Mage::getSingleton('customer/session')->getCustomer()->getFirstname(); ?></strong>, <br /><br />
Browse to your <a href="<?php echo $this->getUrl('customer/account') ?>">Dashboard</a>
</p>
<?php } else { ?>
<form action="<?php echo $this->getPostActionUrl() ?>" method="post">
<div class="block-content">
<label for="mini-login"><?php echo $this->__('Email:') ?></label><input type="text" name="login[username]" id="mini-login" class="input-text" />
<label for="mini-password"><?php echo $this->__('Password:') ?></label><input type="password" name="login[password]" id="mini-password" class="input-text" />
<div class="actions">
<button type="submit" class="button"><span><span><?php echo $this->__('Login') ?></span></span></button>
</div>
</div>
</form>
<?php } ?>
</div>
Hope that helps.
Out of Stock Products Order Module
Hi,
[Magento version 1.4.1.1]
We needed to sort the out-of-stock products on our products listing last. There was already a working solution here http://www.magentocommerce.com/boards/viewreply/213176/, but friends don’t let friends edit Magento core module files, so we created a quick module to encapsulate it.
We’ll need to create three files to make this work:
The Class File:
/app/code/local/CustomMage/Catalog/Model/Resource/Eav/Mysql4/Peoduct
We’ll be extending here from Magento core class Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection since we only need to add the following three lines:
$this->getSelect()->joinLeft( array('_inventory_table' => $this->getTable('cataloginventory/stock_item')), "_inventory_table.product_id = e.entity_id", array('is_in_stock', 'manage_stock') );
$this->addExpressionAttributeToSelect('on_top', '(CASE WHEN (((_inventory_table.use_config_manage_stock = 1) AND (_inventory_table.is_in_stock = 1)) OR ((_inventory_table.use_config_manage_stock = 0) AND (1 - _inventory_table.manage_stock + _inventory_table.is_in_stock >= 1))) THEN 1 ELSE 0 END)', array());
$this->getSelect()->order('on_top DESC');
So our new class file looks like this:
<?php
class CustomMage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection
extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection {
/**
* Add attribute to sort order
*
* Same as parent method, with the addition of left join
*/
public function addAttributeToSort($attribute, $dir='asc')
{
$this->getSelect()->joinLeft( array('_inventory_table' => $this->getTable('cataloginventory/stock_item')), "_inventory_table.product_id = e.entity_id", array('is_in_stock', 'manage_stock') );
$this->addExpressionAttributeToSelect('on_top', '(CASE WHEN (((_inventory_table.use_config_manage_stock = 1) AND (_inventory_table.is_in_stock = 1)) OR ((_inventory_table.use_config_manage_stock = 0) AND (1 - _inventory_table.manage_stock + _inventory_table.is_in_stock >= 1))) THEN 1 ELSE 0 END)', array());
$this->getSelect()->order('on_top DESC');
if ($attribute == 'position') {
if (isset($this->_joinFields[$attribute])) {
$this->getSelect()->order("{$attribute} {$dir}");
return $this;
}
$this->getSelect()->order("cat_index_position {$dir}");
// optimize if using cat index
$filters = $this->_productLimitationFilters;
if (isset($filters['category_id']) || isset($filters['visibility'])) {
$this->getSelect()->order('cat_index.product_id ' . $dir);
}
else {
$this->getSelect()->order('e.entity_id ' . $dir);
}
return $this;
}
$storeId = Mage::app()->getStore()->getId();
if ($attribute == 'price' && $storeId != 0) {
$this->addPriceData();
$this->getSelect()->order("price_index.min_price {$dir}");
return $this;
}
if($attribute == 'is_saleable'){
$this->getSelect()->order("is_saleable " . $dir);
return $this;
}
if ($this->isEnabledFlat()) {
$column = $this->getEntity()->getAttributeSortColumn($attribute);
if ($column) {
$this->getSelect()->order("e.{$column} {$dir}");
}
else if (isset($this->_joinFields[$attribute])) {
$this->getSelect()->order($this->_getAttributeFieldName($attribute).' '.$dir);
}
return $this;
} else {
$attrInstance = $this->getEntity()->getAttribute($attribute);
if ($attrInstance && $attrInstance->usesSource()) {
$attrInstance->getSource()
->addValueSortToCollection($this, $dir);
return $this;
}
}
return parent::addAttributeToSort($attribute, $dir);
}
}
The Configuration File:
/app/code/local/CustomMage/Catalog/etc
The file contains the instruction Magento needs to know what class we’ll be overriding.
<?xml version="1.0"?> <config> <modules> <MageDev_Catalog> <version>0.0.1</version> </MageDev_Catalog> </modules> <global> <models> <catalog_resource_eav_mysql4> <rewrite> <product_collection>CustomMage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection</product_collection> </rewrite> </catalog_resource_eav_mysql4> </models> </global> </config>
The Module Installer:
/app/etc/modules
Instruct Magento to run our new CustomMage module.
<?xml version="1.0"?> <config> <modules> <CustomMage_Catalog> <active>true</active> <codePool>local</codePool> <depends> <Mage_Catalog /> </depends> </CustomMage_Catalog> </modules> </config>
One last thing to mention, the solution on the forum appears to have limitations:
I just realized something.
It doesn’t work for bundle products with mandatory bundle items, when all the bundle items are out of stock and the main bundle product is in stock.
If someone finds a solution for this please post it here.
Hope that helped.
Preorder Buttons: Part II
Hi,
[Magento Version 1.4.1.1]
In the last post we discussed how we can control the caption of the Add To Cart button to reflect our BackOrdering settings from the admin. What we want to do next is to offer the Magento admin more fine-grained control over the PreOredering setting. The idea is to add an “Allow Preorder” option on each product so we can individually select the preoredering option for each product.
Step A: The Admin
Login in the Magento Admin and browse to Manage Attributes from the Catalog Menu. Add a new preorder attribute with the following settings:
- Catalog Input Type for Store Owner: Yes/No
- Values Required: Yes
- Used in Product Listing: Yes
We’ll then need to add our new attribute to the General Attribute Set from the Manage Attribute Sets menu.
Step B: The Code
All we need now is to retrieve the Preorder attribute in both addtocart.phtml and list.phtml, for the former we’ll add:
<?php $rpreorder = $_product->getData('preorder'); ?>
and the latter:
<?php $rpreorder = $_product->getPreorder(); ?>
The getPreorder function didn’t work for some reason in the addtocart.html so we settle with the getData function for now.
It was mentioned in severeal forums that we also need to add this line to the catalog.xml layout file:
<action method="addAttribute"><attribute>Preorder</attribute></action>
But this wasn’t necessary in my case and Magento picked it up automatically, maybe this was needed in older Magento version.
Initial Magento Product Image Zoom Scale
Hi,
Magento version: Magento 1.4.1.1
Today i was facing problems with the Product.Zoom class supplied with Magento default installation. The problem was if we’re uploading a portrait image (height > width), the initial zoom level was covering the whole picture and a big part was cut off.
After digging around, the javascript line responsible for this behavior turned out to be in
/js/varien/product.js,
in the scale function specifically:
<?php
var overSize = (this.imageDim.width > this.containerDim.width && this.imageDim.height > this.containerDim.height);
this.imageZoom = this.floorZoom+(v*(this.ceilingZoom-this.floorZoom));
if (overSize) {
if (this.imageDim.width > this.containerDim.width) {
this.imageEl.style.width = (this.imageZoom*this.containerDim.width)+'px';
}
if(this.containerDim.ratio){
this.imageEl.style.height = (this.imageZoom*this.containerDim.width*this.containerDim.ratio)+'px'; // for safari
}
} else {
this.slider.setDisabled();
}
?>
As you can see from the above code, Magento only consider the product image oversized if BOTH the width and the height are bigger in dimension than the container. We’ll need to change this to an OR condition instead, so an image is considered oversized if either its width or height are bigger than the container dimensions. Futhermore, we need to handle the case for portrait images. The following mods should do just that:
<?php
var overSize = (this.imageDim.width > this.containerDim.width) || (this.imageDim.height > this.containerDim.height);
this.imageZoom = this.floorZoom+(v*(this.ceilingZoom-this.floorZoom));
if (overSize) {
if (this.imageDim.width > this.imageDim.height ){
//landscape
this.imageEl.style.width = (this.imageZoom*this.containerDim.width)+'px';
} else {
//portrait
this.imageEl.style.height = (this.imageZoom*this.containerDim.height)+'px';
}
if(this.containerDim.ratio){
this.imageEl.style.height = (this.imageZoom*this.containerDim.width*this.containerDim.ratio)+'px'; // for safari
}
} else {
this.slider.setDisabled();
}
?>
That’s about it. Any suggestions or better best practices are mostly welcomed.
Preorder Buttons on Magneto Product Details/Grid/List views
Hi,
What we’ll be trying to accomplish: Show a PreOrder button instead of the default Order button
on Magento 1.4.1.1 whenever:
- The product’s stock quantity is zero.
- Backordering is enabled and the “Allow Qty Below 0 and Notify Customer†option is selected.
- The Stock Availability is set to “In Stockâ€
Step A: the addtocart.phtml
We’ll start with the addtocart.phtml template located in
/app/design/frontend/default/default/catalog/product/view/addtocart.phtml,
where the defaults are your interface and template names respectively.
We’ll basically replace the default button markup with the following code:
<?php define(CUSTOM_BACKORDERS_YES_NOTIFY , 2) ;
define(CUSTOM_STOCK_INSTOCK, 1);
$rquantity = (int)Mage::getModel('cataloginventory/stock_item')->loadByProduct($_product)->getQty();
$rbackorder = $_product->getStockItem()->getBackorders();
$rstockavailability = $_product->getStockItem()->getIsInStock(); ?>
<?php if (($rquantity == 0) && ($rbackorder == CUSTOM_BACKORDERS_YES_NOTIFY) && ($rstockavailability == CUSTOM_STOCK_INSTOCK) ) { ?>
<button type="button" title="<?php echo $this->__('Preorder') ?>" class="button btn-cart" onclick="productAddToCartForm.submit()"><span><span><?php echo $this->__('Preorder') ?></span></span></button>
<?php } else { ?>
<button type="button" title="<?php echo $this->__('Add to Cart') ?>" class="button btn-cart" onclick="productAddToCartForm.submit()"><span><span><?php echo $this->__('Add to Cart') ?></span></span></button>
<?php } ?>
Note that we are retrieving the Preorder string using Magento localiztion feature, we need to add this line
“Preorder”,”Preorder”
to
/app/design/frontend/default/default/locale/en_US/translate.csv
I actually tried to add a new file Mage_Catalog.csv in the above location which would make more sense but Magento didn’t pick it up.
Step B: the list.phtml
We’ll use the same code in list.phtml in two different places, the list mode listing and grid mode listing
<?php define(CUSTOM_BACKORDERS_YES_NOTIFY , 2) ;
define(CUSTOM_STOCK_INSTOCK, 1);
$rquantity = (int)Mage::getModel('cataloginventory/stock_item')->loadByProduct($_product)->getQty();
$rbackorder = Mage::getModel('catalog/product')->load($_product->getId())->getStockItem()->getBackorders();
$rstockavailability = $_product->getStockItem()->getIsInStock(); ?>
<?php if (($rquantity == 0) && ($rbackorder == CUSTOM_BACKORDERS_YES_NOTIFY) && ($rstockavailability == CUSTOM_STOCK_INSTOCK) ) { ?>
<button type="button" title="<?php echo $this->__('Preorder') ?>" class="button btn-cart" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product) ?>')"><span><span><?php echo $this->__('Preorder') ?></span></span></button>
<?php } else { ?>
<button type="button" title="<?php echo $this->__('Add to Cart') ?>" class="button btn-cart" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product) ?>')"><span><span><?php echo $this->__('Add to Cart') ?></span></span></button>
<?php } ?>
As you can see the only change is using
<?php $rbackorder = Mage::getModel('catalog/product')->load($_product->getId())->getStockItem()->getBackorders(); ?>
instead of
<?php $rbackorder = $_product->getStockItem()->getBackorders(); ?>
This is probably because Magento didn’t load the full product model on the list view (the only difference I could think of is
the getProduct() ?> line inthe addtocart.phtml).
That’s about it, any suggestions or better best practices are greatly appreciated.