Run Magento code outside of Magento

Sometimes, running update scripts to quickly update the database or to export some data does not require a complete module to hold the code. For these types of operations you can build a shell script to get into Magento’s environment without executing a traditional browser based request. The file is basically the Magento index.php file with one major change, instead of Mage::run(’default’)we will simply use Mage::app(’default’). This type of file can be used to export pending orders, update product categorization, change available quantities, or any other type of automated maintenance.

<?php

require_once 'app/Mage.php';
umask(0);
Mage::app('default');

//add your own code below:
//load a category
$_category = Mage::getModel('catalog/category')->load(1);

//load a product
$_product = Mage::getModel('catalog/category')->load(1);

?>

Install and upgrade your custom module in Magento

Magento automatically installs or upgrades any module that it encounters during runtime. The installation files are located under YourModule/sql/yourmodule_setup/mysql4-install-X.Y.Z.php. The trigger for running this file is that your module’s version number is not present in the DB table `core_resource` and that you have defined a version number in your module’s etc/config.xml file. You will also need to define a global resource for your module’s setup, use a tag name of <yourmodule_setup>. Without the resource definition that includes both setup module and a connection, the installation or upgrade will not perform, even if you increase the version number.

etc/config.xml

<?xml version="1.0"?>
<config>
	<modules>
		<Company_YourModule>
			<version>1.1.0</version>
		</Company_YourModule>
	</modules>
	<global>
		<resources>
			<yourmodule_setup>
				<setup>
					<module>Company_YourModule</module>
				</setup>
				<connection>
					<use>core_setup</use>
				</connection>
			</yourmodule_setup>
		</resources>
	</global>
</config>

Given that XML file, and an absence of any record containing company_yourmodule in table `core_resource`, your module’s install file will be run the next time that module is executed. Once installed, upgrades can be triggered when you change the version number in the XML configuration file to be greater than the value in table `core_resource`. This will trigger a succession of any YourModule/sql/yourmodule_setup/mysql4-upgrade-X.Y.Z.php file that has a version number greater than the number found in the `core_resource` table.

Install module file sample: mysql4-install-1.0.0.php

<?php

$installer = $this;

$installer->startSetup();

$installer->run("INSERT INTO {$this->getTable('core_config_data')} (`path`, `value`) VALUES ('onestepcheckout/general/geoip_database', 'GeoIp/GeoLiteCity.dat')");

$installer->endSetup();

?>

Upgrade module file sample: mysql4-upgrade-1.0.0-1.1.0.php

<?php

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

$resource = Mage::getResourceModel('sales/order_collection');
if(!method_exists($resource, 'getEntity')){

    $table = $this->getTable('sales_flat_order');
    $query = 'ALTER TABLE `' . $table . '` ADD COLUMN `onestepcheckout_customercomment` TEXT CHARACTER SET utf8 DEFAULT NULL';
    $connection = Mage::getSingleton('core/resource')->getConnection('core_write');
    try {
        $connection->query($query);
    } catch (Exception $e) {

    }
}

$installer->endSetup();
?>

Magento delete orders

Magento by default does not allow you to delete orders. While this decision makes good sense, many users are still in need of this functionality to delete test orders. Deleting a order isn’t a simple task. Because you have to remove invoices, creditnotes and shipments firstly that related to order you want to delete. I coded a scirpt for that task. Hope useful!

<?php
// comment the following two lines before execute shis script
echo 'No access permission';
exit;

$mageFilename = '../app/Mage.php';
require_once $mageFilename;
Varien_Profiler::enable();
Mage::setIsDeveloperMode(true);
ini_set('display_errors', 1);
umask(0);
Mage::app('default');
Mage::register('isSecureArea', 1);

// order increment ID you want to delete. Delete one Order to execute this order once.
$incrementId = 100000037;
 
$order = Mage::getModel('sales/order')->loadByIncrementId($incrementId);
$invoices = $order->getInvoiceCollection();
foreach ($invoices as $invoice){
$invoice->delete();
}
$creditnotes = $order->getCreditmemosCollection();
foreach ($creditnotes as $creditnote){
$creditnote->delete();
}
$shipments = $order->getShipmentsCollection();
foreach ($shipments as $shipment){
$shipment->delete();
}
if ($order->delete()) {
	echo 'the Order of order # ' . $incrementId . ' has been deleted from database.';
} else {
	echo 'Deletion failed.';
}

?>

Magento secure shopping cart

By default, Magento shopping cart page is not secured, and does not need to be. HOWEVER, customers don抰 know that is does not need to be. When customers get to the shopping cart page and find that it is not secure, they ASSUME that the checkout process is not secure and DO NOT proceed to checkout. This results in cart abandonment and a lost sale.

This article assumes that you have gotten an SSL certificate installed and already have the login, wishlist, and my account pages directing to https by enabling ‘Use Secure URLs in Frontend’ in the Magento admin panel -> Configuration -> Web. Making the entire site use https is out of the question.

If you like the feature, let’s see how

MB_SecureUrl

We’re going to write a small extension that will force Magento to display your cart using SSL. This extension only needs three files, so go ahead and create them.

app/etc/modules/MB_SecureUrl.xml

<?xml version="1.0"?>
<config>
    <modules>
        <MB_SecureUrl>
            <active>true</active>
            <codePool>community</codePool>
        </MB_SecureUrl>
    </modules>
</config>

app/code/community/MB/SecureUrl/etc/config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <MB_SecureUrl>
            <version>0.5.0</version>
        </MB_SecureUrl>
    </modules>
    <frontend>
        <secure_url>
            <checkout_cart>/checkout/cart</checkout_cart>
            <checkout>/checkout/cart</checkout>
        </secure_url>
		<routers>
			<checkout>
				<args>
					<modules>
						<MB_SecureUrl before="Mage_Checkout">MB_SecureUrl</MB_SecureUrl>
					</modules>
				</args>
			</checkout>
		</routers>
    </frontend>
</config>

For config.xml file, you will probably need to create a few directories found in the path as they most likely don’t exist. Also, in this extension we use [community] code pool as consider compatibility. Because Magento CE 1.7 doesn’t support [local] code pool again.

app/code/community/MB/SecureUrl/controllers/CartController.php

<?php
require_once 'Mage/Checkout/controllers/CartController.php';
class MB_SecureUrl_CartController extends Mage_Checkout_CartController
{
	/**
     * Delete shoping cart item action
     */
    public function deleteAction()
    {
        $id = (int) $this->getRequest()->getParam('id');
        if ($id) {
            try {
                $this->_getCart()->removeItem($id)
                  ->save();
            } catch (Exception $e) {
                $this->_getSession()->addError($this->__('Cannot remove the item.'));
                Mage::logException($e);
            }
        }
		$url = Mage::getUrl('*/*');
		if ($_SERVER['HTTPS'] == 'on') {
			$url = str_replace('http:', 'https:', $url);
		}
        $this->_redirectReferer($url);
    }
}
?>

CartController.php rewrites Magento Core controller Mage_Checkout_CartController, but only rewrite one action, deleteAction. That makes sure to redirect to cart page still after remove an item form the cart.

To get this working, refresh the Magento cache (don’t forget to recompile the Magento compiler, if already enabled) and browse to your shopping cart! If it isn’t working, check that you have enabled SSL URL’s for the front end of your Magento site (System > Configuration > Web > Secure > Use Secure URLs in Frontend).

Secure Any Magento Page with SSL You Need

The above extension will force the Magento shopping cart to use your secure URL, but can easily be adapted to force any Magento page to use SSL. To do this, simply add extra entries to the config.xml file you just created, using the same syntax as the [checkout_cart] option.

Hope that helps

Rename or remove [Add New] button from grid in magento admin

Normally, the [Add New] button is present on upper right corner of Grid Page in Magento backend. If you don’t wish to show the [Add New] button in the Grid you need to code a little to disable it.

How to rename [Add New] button

Go to grid page block file, the directory likes app -> code -> local -> YourNamespace -> YourModule -> Block -> Adminhtml -> YourFile.php
Then add the following code to __construct function

$this->_addButtonLabel = Mage::helper('yourmodulename')->__('Add Report'); //you can replace 'Add Report' with any text you want

How to remove [Add New] button

Go to grid page block file, the directory likes app -> code -> local -> YourNamespace -> YourModule -> Block -> Adminhtml -> YourFile.php
Then add the following code to __construct function(note: the remove statement code must be under the parent constructor statement)

parent::__construct();
$this->_removeButton('add');

Also, how to remove back, delete, and save button in edit page

Go to grid page block file, the directory likes app -> code -> local -> YourNamespace -> YourModule -> Block -> Adminhtml -> YourFile -> edit.php
Then add the following code to __construct function(note: the remove statement code must be under the parent constructor statement)

parent::__construct();
$this->_removeButton('delete');
$this->_removeButton('save');
$this->_removeButton('back');

 

 

How to distinguish between category pages and product pages in Magento?

In some Magento projects, you need to distinguish that whether you are on a category browsing page or a product view page. IF you want that, the following code may been useful for you.

Distinguish category pages

	$_category = Mage::registry('current_category');
	if ($_category->getId()) {
		// Now the page is a category browsing page.
	} else {
		// Now the page is not a category browsing page.
	}

Distinguish product pages

	$_product = Mage::registry('current_product');
	if ($_product->getId()) {
		// Now the page is a product view page.
	} else {
		// Now the page is not a product view page.
	}

Magento display top level categories and subcategories of current category

<?php
/*
 * http://www.magentobooker.com
 *
 * Display top level categories and
 * subcategories of the current category
 *
 * This code is compatible with Magento CE 1.3 and up
 *
**/
?>
<?php $_helper = Mage::helper('catalog/category') ?>
<?php $_categories = $_helper->getStoreCategories() ?>
<?php $currentCategory = Mage::registry('current_category') ?>
<?php if (count($_categories) > 0): ?>
    <ul class="category">
        <?php foreach($_categories as $_category): ?>
            <li>
                <a href="<?php echo $_helper->getCategoryUrl($_category) ?>">
                    <?php echo $_category->getName() ?>
                </a>
                <?php if ($currentCategory->getId() && $currentCategory->getId() == $_category->getId()): ?>
                    <?php $_category = Mage::getModel('catalog/category')->load($_category->getId()) ?>
                    <?php $_subcategories = $_category->getChildrenCategories() ?>
                    <?php if (count($_subcategories) > 0): ?>
                        <ul class="subcategory">
                            <?php foreach($_subcategories as $_subcategory): ?>
                                <li>
                                    <a href="<?php echo $_helper->getCategoryUrl($_subcategory) ?>">
                                        <?php echo $_subcategory->getName() ?>
                                    </a>
                                </li>
                            <?php endforeach; ?>
                        </ul>
                    <?php endif; ?>
                <?php endif; ?>
            </li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Magento display top level categories and subcategories

<?php
/*
 * http://www.magentobooker.com
 *
 * Display top level categories and subcategories
 *
 * This code is compatible with Magento CE 1.3 and up
 *
**/
?>
<?php $_helper = Mage::helper('catalog/category') ?>
<?php $_categories = $_helper->getStoreCategories() ?>
<?php if (count($_categories) > 0): ?>
    <ul class="category">
        <?php foreach($_categories as $_category): ?>
            <li>
                <a href="<?php echo $_helper->getCategoryUrl($_category) ?>">
                    <?php echo $_category->getName() ?>
                </a>
                <?php $_category = Mage::getModel('catalog/category')->load($_category->getId()) ?>
                <?php $_subcategories = $_category->getChildrenCategories() ?>
                <?php if (count($_subcategories) > 0): ?>
                    <ul class="subcategory">
                        <?php foreach($_subcategories as $_subcategory): ?>
                            <li>
                                <a href="<?php echo $_helper->getCategoryUrl($_subcategory) ?>">
                                    <?php echo $_subcategory->getName() ?>
                                </a>
                            </li>
                        <?php endforeach; ?>
                    </ul>
                <?php endif; ?>
            </li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Magento display top level categories

<?php
/*
 * http://www.magentobooker.com
 *
 * Display top level categories
 *
 * This code is compatible with 1.3 and up
 *
**/
?>
<?php $_helper		= Mage::helper('catalog/category') ?>
<?php $_categories	= $_helper->getStoreCategories() ?>
<?php if (count($_categories) > 0): ?>
    <ul class="category">
        <?php foreach($_categories as $_category): ?>
            <li>
                <a href="<?php echo $_helper->getCategoryUrl($_category) ?>">
                    <?php echo $_category->getName() ?>
                </a>
            </li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Why You Shouldn’t Merge JavaScript in Magento

Most people – myself included – thought that merging all of your separate Javascript files was a healthy way to speed up the front end of your site. The logic was that by merging the files, you reduce the number of requests the browser makes when loading your site. This allows the request to complete quicker and results in your page loading faster. Let’s walk through an example of this process in Magento and see what happens.

Imagine you have just enabled Javascript Merging in the Magento Admin and then a user visits your site. Magento collates all of the XML layout files and determines which Javascript files should be included for the homepage request. These files are then merged into a single file and saved using an md5 hash of their filenames. This new file, named f0eb853c09ba3e46ad21fb89b8d57626.js, is then served to the user, who downloads it and stores it in their browser cache.

Next, the user clicks a link to one of your categories. Once again, Magento collates the XML layout files and determines what Javascript files are needed for the category page. At this point, Magento realises that the exact same Javascript files used on the homepage are needed on this page. Instead of remaking the single Javascript file, Magento serves the Javascript file created when the user visited the homepage. This time though, the browser knows that it already has this file and therefore doesn’t download it again. Instead, the file is loaded from the user’s cache, saving time, bandwidth and computer resources!

So far everything has gone well; we have made the page load faster while decreasing the load on the server, but…

Next, the user is enticed by one of your products and clicks through to a product page. Once again, Magento determines which Javascript files are needed based on the layout XML files. At this point, Magento realises that the same Javascript files on the homepage are needed as well two new files. As a merged file containing this combination of Javascript doesn’t exist, Magento merges the files and creates a new Javascript file named 139f9771ba2ba0cae754978fab4a3c35.js. Roughly 80% of this file is the Javascript that was used on the homepage and has been downloaded and cached by the user already. The final 20% accounts for the new Javascript files used solely on the product page. This file is then served to the user who is forced to download it all! Although this file is made of 80% code that has already been cached, the user’s browser is completely unaware of this and will download 100% of the file and cache it again!

The intended result of merging Javascript is a decreased page load time, but in the above scenario, the user has been forced to re-download a massive chunk of Javascript! This will no doubt have increased page load time (downloading ~40kb of Javascript that doesn’t need to be downloaded), which goes against the idea of Javascript merging.

Let’s consider what would happen if we hadn’t of merged our Javascript…

When the user first requests the homepage, the merged Javascript files would be downloaded individually. Although the combined size of these Javascript files matched the size of the merged Javascript file, this request would take longer as each file would need to be requested, downloaded and cached separately. 1-0 merging!

Second, the user requested the category page, which uses the same files that were just downloaded and cached on the homepage. As a result this takes exactly the same amount of time it would if the Javascript files had been merged. In both instances, all of the required Javascript has already been downloaded and cached, meaning the browser can skip the Javascript altogether.

Lastly, the user visits the product page, which uses the Javascript files found on the home and category pages as well as two new files. The files used on both the homepage and category page have already been cached, so the browser skips these files and downloads and caches the two new files that haven’t been experienced before! In the merging scenario, we mentioned that files used on the homepage in Magento make up about 80% of the total Javascript on the product page. With merging disabled, we have managed to skip downloading the 80% and just download the 20%. In the merging example, the full 100% had to be re-downloaded!

This problem is compounded for each page your user visits that has a combination of Javascript not found on another page. Each time this happens, the user will be forced to re-download Javascript that it has already downloaded and cached under a different filename.

What Can Be Done?

While the current Magento merging system is flawed, it would only take a minor adjustment to fix this critical issue.

In Magento, Javascript is added to the Head block through XML and stored in an array, which is then iterated over and displayed in the head section of your HTML code. Our suggestion is that instead of simply adding Javascript files to the head, an extra parameter be included that allows you to specify a group for the Javascript. Consider the following code:

<default>
 <reference name="head">
   <action method="addJs"><script>prototype/prototype.js</script><group>global</group></action>
   <action method="addJs"><script>scriptaculous/builder.js</script><group>global</group></action>
   <action method="addJs"><script>scriptaculous/effects.js</script><group>global</group></action>
   <action method="addJs"><script>varien/form.js</script><group>global</group></action>  </reference>
</default>
 
<catalog_product_view>
 <reference name="head">
   <action method="addJs"><script>varien/product.js</script><group>product</group></action>
   <action method="addJs"><script>varien/configurable.js</script><group>product</group></action>
 </reference>
</catalog_product_view>

Notice the addition of the paramter group? Instead of all of the Javascript being merged into a single file, the group parameter would be be used to decide which Javascript files get merged together. The above code would result in two separate merged files being created for the product page; one containing the global Javascript that is used on the homepage (and the whole of the site) and a second containing the Javascript used solely on the product page. Although this would create an extra page request, time would still be saved by not having to re-download the Javascript used on the homepage! If this technique were applied to the whole Magento site, each piece of Javascript would be downloaded once and once only!

Older posts «