Tutorial to create custom Magento module

I could not find one good tutorial on developing a custom module in Magento when I had a client requirement to develop one for his website. It was not like there weren’t any. If you Google “custom Magento module tutorial” or something similar you will get a lot of search results however I received a lot of errors when I tried using them in my case. Either they were not tested or they were not supposed to work in that way but I am not complaining as it helped me to learn more about Magento and custom module development in Magento.

Follow the steps below to create your first custom Magento module. This module will display entries from a database table and display it in the Magento admin panel. I have tested this module on Magento community versions 1.6.2 and 1.7.0 and it is working on both of them.

Run the following SQL statement in your mySQL database.

CREATE TABLE `sarfraz` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(100) DEFAULT NULL,
  `cus_email` varchar(100) DEFAULT NULL,
  `telephone` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

After running the above statement it should create a table by the name of “sarfraz” in your database. Now run the following insert SQL statements to create records in the table.

insert into `sarfraz` (`id`,`first_name`,`cus_email`,`telephone`) values (1,'Adam Sandler','[email protected]','1234567890');
insert into `sarfraz` (`id`,`first_name`,`cus_email`,`telephone`) values (2,'Joe Burton','[email protected]','5556667777');
insert into `sarfraz` (`id`,`first_name`,`cus_email`,`telephone`) values (3,'Alice Keys','[email protected]','66677788888');

Our table now has some entries so we can now proceed with the actual coding related changes.

First create your XML file in the app/etc/modules folder. In this tutotial my XML file is named as Adeel_Sarfraz.xml however you are free to choose what ever name is right for you.

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

The above code will register your module in Magento. Now we put in the actual code for the module.

Go to app/code/local folder and create a new folder by the name of “Adeel” and then create another folder within it by the name of “Sarfraz”.

Now create 5 folders in the app/etc/local/Adeel/Sarfraz folder.

1. Block
2. controllers
3. etc
4. Helper
5. Model

We will start now by creating a config.xml file in the etc folder. Once you have created it you can add the following code to the config.xml file.

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

    <modules>
        <Adeel_Sarfraz>
            <version>1.0.0</version>
        </Adeel_Sarfraz>
    </modules>
	
	<global>
	
		<helpers>
			<sarfraz>
				<class>Adeel_Sarfraz_Helper</class>
			</sarfraz>
		</helpers>

        <models>
            <sarfraz>
                <class>Adeel_Sarfraz_Model</class>
                <resourceModel>sarfraz_mysql4</resourceModel>
            </sarfraz>
            <sarfraz_mysql4>
                <class>Adeel_Sarfraz_Model_Mysql4</class>
                <entities>
                    <sarfraz>
                        <table>sarfraz</table>
                    </sarfraz>
                </entities>
            </sarfraz_mysql4>
        </models>		
		
		<blocks>
			<sarfraz>
				<class>Adeel_Sarfraz_Block</class>
			</sarfraz>
		</blocks>		
	
	</global>	
	
    <admin>
        <routers>
            <asarfraz>
                <use>admin</use>
                <args>
                    <module>Adeel_Sarfraz</module>
                    <frontName>asarfraz</frontName>
                </args>
            </asarfraz>
        </routers>
    </admin>
	
    <adminhtml>
        <menu>
            <customer>
                <children>
                    <list translate="title" module="sarfraz">
                        <title>Admin Title</title>
                        <sort_order>11</sort_order>
			<action>asarfraz/controller_list/index</action>
                    </list>
                </children>
            </customer>
        </menu>
    </adminhtml>
	
</config>

1. Now add the following folders and files in the Block folder.

Adminhtml
Adminhtml/Items
Adminhtml/Items.php
Adminhtml/Items/Grid.php

Open the Block/Adminhtml/Items.php and add the following PHP code to it.

<?php
class Adeel_Sarfraz_Block_Adminhtml_Items extends Mage_Adminhtml_Block_Widget_Grid_Container
{
    public function __construct()
    {
        $this->_controller = 'adminhtml_items';
        $this->_blockGroup = 'sarfraz';		
        $this->_headerText = Mage::helper('sarfraz')->__('Admin Title');
        parent::__construct();				
        $this->_removeButton('add');		
    }
}

Next open the Block/Adminhtml/Items/Grid.php and add the following PHP code to it.

<?php
class Adeel_Sarfraz_Block_Adminhtml_Items_Grid extends Mage_Adminhtml_Block_Widget_Grid
{	
    public function __construct()
    {
		
        parent::__construct();
        $this->setId('itemsGrid');
        $this->setDefaultSort('id');
		$this->setDefaultDir('DESC');		
    }
 
    protected function _prepareCollection()
    {
        $collection = Mage::getModel('sarfraz/sarfraz')->getCollection();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }
 
    protected function _prepareColumns()
    {
        $this->addColumn('id', array(
            'header' => Mage::helper('sarfraz')->__('ID'),
            'sortable' => true,
            'width' => '60',
            'index' => 'id'
        ));
 
        $this->addColumn('first_name', array(
            'header' => Mage::helper('sarfraz')->__('Full Name'),
            'sortable' => true,
            'width' => '60',
            'index' => 'first_name',
            'type'  => 'text'
        ));
 
        $this->addColumn('cus_email', array(
            'header' => Mage::helper('sarfraz')->__('Email Address'),
            'sortable' => true,
            'width' => '60',
            'index' => 'cus_email',
            'type'  => 'text'
        ));

		$this->addColumn('telephone', array(
            'header' => Mage::helper('sarfraz')->__('Answer'),
            'sortable' => true,
            'width' => '60',
            'index' => 'cus_telephone',
            'type'  => 'text'
        ));				

		return parent::_prepareColumns();
    }
 
}

2. Add the following folder and file to the controllers folder

Controller
Controller/ListController.php

Open the controllers/Controller/ListController.php file and add the following PHP code

<?php
class Adeel_Sarfraz_Controller_ListController extends Mage_Adminhtml_Controller_Action
{
    public function indexAction()
    { 
        $this->loadLayout()->_setActiveMenu('customers');
        $myblock = $this->_addContent($this->getLayout()->createBlock('sarfraz/adminhtml_items'));
        $this->renderLayout($myblock);
    }
}

3. Create the following file in the Helper folder.

Data.php

Add the following PHP code to Data.php file

<?php
class Adeel_Sarfraz_Helper_Data extends Mage_Core_Helper_Abstract
{

}

4. Add the following files and folders to the Model folder

Mysql4
Sarfraz.php
Mysql4/Sarfraz
Mysql4/Sarfraz.php
Mysql4/Sarfraz/Collection.php

Open the Model/Sarfraz.php file and add the following code

<?php
class Adeel_Sarfraz_Model_Sarfraz extends Mage_Core_Model_Abstract
{
    public function _construct()
    {
       parent::_construct();
       $this->_init('sarfraz/sarfraz');
    }
}

Then open the Model/Mysql4/Sarfraz.php file and add the following code

<?php
class Adeel_Sarfraz_Model_Mysql4_Sarfraz extends Mage_Core_Model_Mysql4_Abstract
{
    public function _construct()
    {   
        $this->_init('sarfraz/sarfraz', 'id');
    }
}

Finally open the Model/Mysql4/Sarfraz/Collection.php and add the code below

<?php
class Adeel_Sarfraz_Model_Mysql4_Sarfraz_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
    public function _construct()
    {
	parent::_construct();
        $this->_init('sarfraz/sarfraz');
    }
}

After adding the code changes above please clear the Magento cache and your module should come up under the customers menu by the name of “Admin Title”. If it does’t then logout and relogin into the Magento admin panel. You can change the “Admin Title” to some thing more meaningful by editing the etc/config.xml file.

If the above is too much time consuming for you then you can contact me and I will provide you the files in zip format.

If you have any feedback then please share by commenting.

How to resolve the Zen Cart Illegal mix of collations error

We had deployed a Zen Cart website and we came up with the following error in the Zen Cart admin panel when clicking on the Tools -> Send Email link.

Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation 'locate'

After checking the table collation in PHPmyAdmin I found that the subscribers table (created when the Newsletter Subscribe module was installed) collation was different to the configuration table.

Most people on the internet advised that the collation for the subscribers table and email_address field should be changed to that of the remaining tables in the database. I tried to change the collation of the subscribers table and the email_address field to that of the configuration table but met with no success.

I then changed it to ‘utf8_unicode_ci‘ and the page displayed without any problem or error.

The exact SQL I ran in mySQL was

ALTER TABLE subscribers CHANGE email_address email_address varchar( 96 ) NOT NULL default '' UNIQUE COLLATE 'utf8_unicode_ci';
ALTER TABLE customers COLLATE 'utf8_unicode_ci';
ALTER TABLE subscribers COLLATE 'utf8_unicode_ci';
ALTER TABLE customers CHANGE customers_email_address customers_email_address varchar( 96 ) NOT NULL default '' UNIQUE COLLATE 'utf8_unicode_ci';

The first statement in the SQL above is thanks to DrByte.

Hope the above helped someone.

How to offer discounts and coupons to customers using Facebook Like

I have come across some businesses offering discounts, offers, video downloads, documents etc. to users on Facebook and in return they ask them to simply Like their page. This is quite a good idea as it helps them to attract leads/sales from Facebook and it serves as an additional revenue generation model apart from search engines.

Take for example the following application on Facebook for a UK based retailer

http://www.facebook.com/babyboom2000uk/app_201143516562748

They are offering discounts to Facebook users who Like the above URL. Once you click on
the Like button on the top right the page will display the discount code which customers
can then use to get discount on their purchase.

I was fascinated by this and was curious to know more about how they were doing it as
store owners would definitely benefit from this. I set about creating an application and
after it was done I tried using the FB.Event.subscribe method which was being advocated
by everyone to use to track the click event on the Like button. Though they were correct,
this was working only when the Like button was embedded on the actual page but was not
working in the above case.

I then tried to check whether any thing was being passed to my application from Facebook
and sure enough a signed_request variable was being sent but it was sending a long
alphanumeric string. I sensed that this was encrypted so I Googled and found a method to
decrypt the string.

$signed_request = $_REQUEST["signed_request"];
list($encoded_sig, $payload) = explode('.', $signed_request, 2); 
$data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);

When I dumped the $data variable it printed out the complete array and displayed it as follows:

Array
(
    [algorithm] => HMAC-SHA256
    [issued_at] => 1353404943
    [page] => Array
        (
            [id] => 
            [liked] => 
            [admin] => 
        )

    [user] => Array
        (
            [country] => pk
            [locale] => en_US
            [age] => Array
                (
                    [min] => 0
                    [max] => 12
                )

        )

)

From above we can check for the value returned in $data[‘page’][‘liked’]. If it’s 1 then
the page is liked by the user other wise the page is not liked.

In this way you can display two different pages to the Facebook user depending on their
response.

Hopefully this would have helped any one facing problem while implementing this.

Please contact me if you still face any issues and I will try my best to help you.

List of test credit card numbers for Sage Pay

If you are using Sage Pay Form and wish to test whether the integration is working correctly or not you can use the following test credit card numbers for placing orders.

Visa (VISA)
4929000000006

MasterCard (MC)
5404000000000001

Visa Debit / Delta (DELTA)
4462000000000003

Solo (SOLO)
6334900000000005
Issue 1

UK Maestro / International Maestro (MAESTRO)
5641820000000005
Issue 01

American Express (AMEX)
374200000000004

Visa Electron (UKE)
4917300000000008

Please enter a future credit card expiry date.

Impact of EU Cookie Directive on your website

The EU passed a law over a year back which required website owners based in the EU to notify visitors that they are using cookies on their website.

Cookies are basically text files stored on the visitor’s browser. Websites use these cookies to identify visitors. Sometimes they use them to store visitor information that may be further used to identify visitor likes and dislikes with out the visitor himself knowing what is being saved on his browser.

The EU cookie law wants website owners to inform their visitors whether they are using cookies on their website or not and if they are using cookies then what type of information are they storing on them. The law also requires the website owners to inform whether the visitor information they are storing in cookies would be made available to 3rd party marketing companies or not.

Visitors can then make an informed decision on whether to continue browsing the website or not once they know what type of information is being saved and how it’s going to be used.

If you are based in the EU and have a website then I can audit your website for free. Feel free to contact me for cookie review of your website.
 

Improve bounce rate for slow loading web pages

Most websites take a lot of time while loading their category or product listing pages. Visitors don’t bother to wait long if they don’t see any visual progress in their browsers. For this reason it’s best to display a graphic (for e.g. animated GIF) so that visitors know that the website is loading their results.

Remember any sort of communication with your visitor is better than no communication at all as you may lose a potential sale on your website.

I have coded a small example as to how it should work. You are free to change it according to your requirement.

Comments?

How to remove the Fatal error: SOAP-ERROR: Parsing Schema: unexpected in sequence error?

If you use the eBay API in any way on your website related to your products or orders then you may have come across an issue which happened last week probably around Jan 26 2012. If you call any eBay API method you will encounter the following error message:

Fatal error: SOAP-ERROR: Parsing Schema: unexpected  in sequence in eBaySOAP.php on line 87

Line 87 in eBaySOAP.php comes out to be “parent::__construct($session->wsdl, $session->options);” in the following function

public function __construct(eBaySession $session) {
	$this->session = $session;
	$this->__setHeaders();
	parent::__construct($session->wsdl, $session->options);
}

After much searching I came across this forum post on X.com which recommended that we change the URL of eBay WSDL from http://developer.ebay.com/webservices/latest/eBaySvc.wsdl to http://developer.ebay.com/webservices/753/eBaySvc.wsdl.

Go to line 21 on your eBaySOAP.php file and change the WSDL URL to

$this->wsdl = 'http://developer.ebay.com/webservices/753/eBaySvc.wsdl';

After making the change you should not encounter the error message and everything should work fine.

Comments?

How to 301 redirect static URLs using htaccess

If your website has static URLs i.e. do not contain any query string parameters and you need to redirect your old website URLs to your new website URLs then you don’t have to fear anymore. You can write simple redirect statements in your .htaccess file and you don’t have to be regular expression guru.

Add the following line in your .htaccess file

redirect 301 /oldname http://www.example.com/newname 

As you can see in the above statement the htaccess tells the Apache web server to 301 redirect URL if it encounters /oldname to http://www.example.com/newname. You can add as many statements as you like however if there are over 20 plus URLs then consider using RedirectMatch. You should try your hand at regular expressions and get the job done in few statements.

Comments?

How to implement CAPTCHA on Zencart registration forms?

A client of ours was facing an issue with spam registrations on his website. We had developed his website on Zencart. The issue was that spam bots were registering on his website in the hundreds and the client wanted us to put a stop to this.

I decided on implementing CAPTCHA on the registration forms so that these unwanted registrations are stopped. I browsed the Zencart website and found the CAPTCHA Anti-Robot Registration mod.

I downloaded and, as is the practice, copied the files to the relevant directories. After copying over the files, however, it was not working smoothly. I then made some changes to the includes/modules/create_account.php file and it started working on our local development machine.

After checking several times I moved the files to our production server. Our production server was running PHP 4.3.3 and the code stopped working online as it was developed keeping in mind PHP 5. I again made the changes in the includes/modules/create_account.php and includes/templates/<your_template_folder>/templates/tpl_modules_create_account.php file and it started working online too.

Just beware of PHP and its versions or you’ll get into a lot of trouble 🙂

Hope the above helped

How to parse an XML file in PHP using XML parser

PHP offers a new and fast way to parse XML files. This approach makes it easier to parse very large XML files in a couple of seconds.

Here goes the code to parse a sample XML file. The XML in that file is also given below

<!-- The XML to be parsed with the code below -->

<person>
<name>Adeel Sarfraz</name>
<phone>123456790</phone>
<address>Karachi</address>
</person>


<?php

$parser = xml_parser_create();

// This function is called whenever a start tag is encountered 
function start($parser,$element_name,$element_attrs) {
 switch($element_name) {
    case "NAME":
           //Add code here
	    break; 
    case "PHONE":
            //Add code here
	    break; 
    case "ADDRESS":
            //Add code here
	    break; 
    default:
           //Add code here
	   break;
   }
}

// This function is called whenever an end tag is encountered
function stop($parser,$element_name) {
}

// This function is called when the data within the tags is to be read
function char($parser,$data) {
  	print $data;
}
xml_set_element_handler($parser,"start","stop");
xml_set_character_data_handler($parser,"char");
xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,true);
$fp=fopen("example.xml","r");
while ($data=fread($fp,4096))  {
  xml_parse($parser,$data,feof($fp)) or die (sprintf("XML Error: %s at line %d", xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser)));
}
xml_parser_free($parser);
?>

Hope the above helps.