Archive for the ‘Magento eCommerce Tips’ Category

14
Apr

A quick debugging tip for developers. You can write custom messages into Magento’s system log  or exception log – helpful for knowing the current variable or object state etc.

How to enable log

Admin > System > Configuration > Developer > Log Settings
Set “Enabled” to “Yes”

Code to write into the log

Mage::log("Hello");
Mage::log("Hello Again");

Where to check?

Your custom message should most probably be in the var/log/system.log file but, in case you do not find it there, you can also check the var/log/exception.log file.

New log messages are found at the bottom of the log file.
Ensure that your log folder and files have enough permissions to write.

The log message will look like this:

2010-04-14T13:40:24+00:00 DEBUG (7): Hello
2010-04-14T13:41:31+00:00 DEBUG (7): Hello Again

, ,

12
Mar

In this article I want to give some custom options to my product named “Linux

Select Product from Catalog -> Manage Products

On the Left side you can take “Custom Options” inside “Product Information

Click on the button “Add New Option

You can give as follows:

Title : Company

Input Type : Drop Down

Is Required : Yes

Sort Order : 1

After that you will get an option below like “Add New Row“. Click that Button

Input the fields as below:

Title: Red Hat
Price: 2000
Price Type: Fixed
SKU: rh123
SortOrder: 1

Add a New Row and give data as below:

Title: Suse
Price: 900
Price Type: Fixed
SKU: suse123
SortOrder: 2

Here one more option is being given. So click on the “Add New Option” again.

You can give as follows:

Title : Edition

Input Type : Radio Button

Is Required : Yes

Sort Order : 1

Click “Add New Row” and input the fields as below:

Title: Home
Price: 500
Price Type: Fixed
SKU: home123
SortOrder: 1

Give one more “Add New Row” and input as below:

Title: Office
Price: 750
Price Type: Fixed
SKU: office123
SortOrder: 2

It looks like the picture shown below:

Custom Options

You can add any number of rows as you want.

The Front End will show options like this:

Custom Options

Similarly you can also use Text Area, Text Field, Radio Button, Multiple Select and Check boxes. Try with new values and have a look.

, ,

08
Mar

How to enable the default Magento contact form?

  1. System > Configuration > Contacts
  2. Click the ‘Contact Us’ section on this page. Set ‘Enable Contact Us’ to ‘Yes’
  3. Click the ‘Email Options’ section on the same page, just under it. Set the email address to which the comments should come, subject and email template.
  4. Click ‘Save Config’

How to add a custom field to the default Magento contact form?

  1. Open [magento_root]/app/design/frontend/default/default/template/contacts/form.phtml in your favorite editor
  2. Copy & paste an existing form field code on this script and rename the field to create new field. For example:
    This is the default  code in the file:

    <div class="input-box">
    <label for="email"><?php echo Mage::helper('contacts')->__('Email') ?> <span class="required">*</span></label><br />
    <input name="email" id="email" title="<?php echo Mage::helper('contacts')->__('Email') ?>" value="<?php echo $this->htmlEscape($this->helper('contacts')->getUserEmail()) ?>" class="required-entry input-text validate-email" type="text" />
    </div>
    <div class="clear"></div>
    <div class="input-box">
    <label for="telephone"><?php echo Mage::helper('contacts')->__('Telephone') ?></label><br />
    <input name="telephone" id="telephone" title="<?php echo Mage::helper('contacts')->__('Telephone') ?>" value="" class="input-text" type="text" />
    </div>

    This is how the modified code will look like:

    <div class="input-box">
    <label for="email"><?php echo Mage::helper('contacts')->__('Email') ?> <span class="required">*</span></label><br />
    <input name="email" id="email" title="<?php echo Mage::helper('contacts')->__('Email') ?>" value="<?php echo $this->htmlEscape($this->helper('contacts')->getUserEmail()) ?>" class="required-entry input-text validate-email" type="text" />
    </div>
    <div class="clear"></div>
    <!-- New Field Code-->
    <div class="input-box">
    <label for="website"><?php echo Mage::helper('contacts')->__('Website') ?></label><br />
    <input name="website" id="website" title="<?php echo Mage::helper('contacts')->__('Website') ?>" value="" class="input-text" type="text" />
    </div>
    <div class="clear"></div>
    <!-- End New Field Code-->
    <div class="input-box">
    <label for="telephone"><?php echo Mage::helper('contacts')->__('Telephone') ?></label><br />
    <input name="telephone" id="telephone" title="<?php echo Mage::helper('contacts')->__('Telephone') ?>" value="" class="input-text" type="text" />
    </div>
  3. Now go to, System > Transaction Emails.
  4. Click ‘Add New Template’
  5. In the ‘Load default template’ section, select ‘Contact Form’ template and click ‘Load Template’
  6. The template content will look like this:
    Name: {{var data.name}}
    E-mail: {{var data.email}}
    Telephone: {{var data.telephone}}
    
    Comment: {{var data.comment}}
  7. Add your new field in this content like this:
    Name: {{var data.name}}
    E-mail: {{var data.email}}
    Website: {{var data.website}}
    Telephone: {{var data.telephone}}
    
    Comment: {{var data.comment}}
  8. Enter a new ‘Template Name’ and click ‘Save template’
  9. Now go to, System > Configuration > Contacts
  10. Click the ‘Email Options’ section on this page and select the new template that you just created.
  11. Click ‘Save Config’

How to add a link to the contact form on the top menu bar?

  1. Open [magento_root]/app/design/frontend/default/default/template/catalog/navigation/top.phtml in your favorite editor
  2. After this line:
    <?php endforeach ?>

    Add a line like this:

    <li><a href="<?php echo $this->getUrl('contacts')?>"><?php echo $this->__('Contact Us') ?></a></li>
  3. Click the Magento cache; System > Cache Management

Reference links:

  1. http://www.magentocommerce.com/wiki/how_to/add_fields_to_contact_form
  2. http://www.richardcastera.com/2009/08/17/magento-add-additional-fields-to-the-contact-form/
  3. http://www.magentocommerce.com/boards/viewthread/21707/
  4. http://inchoo.net/ecommerce/magento/contact-form-in-magento/

, ,

12
Feb
Try the following steps,
1. Try accessing your admin using your ip (if it is localhost, try 127.0.0.1).
2. If you cant get around with the above step then as a last step try the following,
Navigate within your Magento database to the MySQL table core_config_data and look for a row with the field path set to the valueweb/cookie/cookie_domain. There might be multiple entries, but the one with the scope_id set to 0 should be matching the domainname you’re using to access Magento.
3. Another problem could be that the so-called Secure URLs and/or Unsecure URLs do not match the current hostname.
In the same MySQL table core_config_data you might find various entries with path starting withweb/secure/base or web/unsecure/base. These also need to point to the right hostname. Note that the number of occurances could range from 0 (nothing configured) to 10 (everything configured).

04
Feb

I got the following fatal error while trying to intall Magento 1.3.2.4 in XAMPP 1.7.3
Fatal error: Method Varien_Object::__tostring() cannot take arguments in C:\xampp\htdocs\magento\lib\Varien\Object.php on line 488

Follow the steps given below to fix this issue:-
1)File: /lib/Varien/Object.php (Line 484)
Change from
public function ___toString(array $arrAttributes = array(), $valueSeparator=’,')
To this
public function __invoke(array $arrAttributes = array(), $valueSeparator=’,')

2)File /app/code/core/Mage/Core/Controller/Request/Http.php (Line 274)
Change from
$host = split(‘:’, $_SERVER['HTTP_HOST']);
To this
$host = explode(‘:’, $_SERVER['HTTP_HOST']);

05
Jan

Setting up a Multiple Currency for your Magento Shop is very easy to do. These are the basic steps to do that.
Login as admin and select System->Configuration, then select ’Currency Setup’ fromGENERAL section, then select Base currency, Default display currency, allowed currencies (press Control Key and Click the needed currencies) and ’Save Config’
Majento setup multiple=

Also choose the Automated Currency Transaction update and make the configurations
Majento multiple=

Now come to “System->Manage Currencies’ menu

Majento multiple=

Majento Multiple=

Bingo!…. You are done.

Visit the frontend and reload (you may need to refresh your cache) to see the drop down added to your product pages.

, ,

01
Jun

Retrieving database information in Magento can be a bit complicated. Technically it is possible to write raw SQL statements to load entities and all their associated attribute values. However due to the EAV database design it is difficult to write query statements to get exactly what you want. Utilizing an EAV modeling pattern allows for unlimited attributes on any product, category, customer, or order, but EAV also depletes a programmer’s ability to write queries against the data.

SQL query example:

Lets look at an example of an SQL statement which gets all the products (product_id) in a  particular category:

$sql = “SELECT product_id  FROM catalog_category_product WHERE category_id=3″;
$data = Mage::getSingleton(‘core/resource’) ->getConnection(‘core_read’)->fetchAll($sql);

You will get the product ids as an array in $data. However if we wanted to get all the attributes of a product based on the product_id then it is difficult as all the product info is not present in one table. Due to the EAV design it is loosely present in many tables. So we require to join a number of tables to get the product information. In such cases we should try using Magento’s built-in query methods instead of directly running queries on the database. More on built in query methods later.

Analyzing the code:

Lets analyze the code:

$data = Mage::getSingleton(‘core/resource’);

First of all we require a connection to the database to perform queries. This is done by using resources. The role of a ‘resource’ in Magento is to manage database connections. So using getSingleton(‘core/resource’) we get an instance of Mage_Core_Model_Resource.

Check out that file and you will see a function called getConnection($name). This is used to get a ‘read’ or ‘write’ connection (‘core_read’ – read connection, ‘core_write’ – write connection).

The fetchAll function executes the query and returns all the rows to $data. The results are returned as an array of results (array of arrays).

How do i know the table names ?

You can see Magento’s database design by checking this link http://www.magentocommerce.com/wiki/development/magento_database_diagram.

The core ResourceModel has a method called getTable() to get the table name of any model in the system. Table names do not have to follow the name of the model, an end-user can change the table names by changing an XML setting.Therefore, it is best to use the getTable method of the core resource.

$res = Mage::getSingleton(’core/resource’)->getConnection(’core_read’);
$tableName = $res->getTable(’catalog/product’);

This will give $tableName as ’catalog_product_entity’. This happens because we have the following XML configuration in the catalog module’s config file.

<global>
<models>
<catalog>
<class>Mage_Catalog_Model</class>
<resourceModel>catalog_resource_eav_mysql4</resourceModel>
</catalog>

<catalog_resource_eav_mysql4>
<class>Mage_Catalog_Model_Resource_Eav_Mysql4</class>
<entities>
<product>
<table>catalog_product_entity</table>
</product>

ResourceModels:

ResourceModels manage the different read/write connections and automatically figure out table names based on convention and perform the database related operations for a particular Model. ie. It acts as an abstraction to retrieve (or write) data from database. For eg: in the Mage_Catalog_Model_Product class you can see that to get data it uses the getResource() function. This function gets the ResourceModel to perform the required database operation.

For the Catalog Module the ResourceModels can be found in Catalog\Model\Resource\Eav\Mysql4 directory. (this is defined in the config.xml – search for <resourceModels>). For getting ResourceModels we use the getResourceModel() method.

Mage::getResourceModel(‘catalog/product’) will create an instance of  Mage_Catalog_Model_Resource_Eav_Mysql4_Product.php. (If class names have the term Mysql4 in their names, they are generally called ResourceModels. If the word Entity appears in the class name, then the resource is an EAV Entity). You will get the same result by calling Mage::getModel(‘catalog/resource_eav_mysql4_product’) also.

Now take a look at this Product.php file. You will see some sql statements like the one below:

public function getIdBySku($sku)
{return $this->_read->fetchOne(‘select entity_id from ‘.$this->getEntityTable().’ where sku=?’, $sku);}

So if we need to get the product id for a sku value then we can simply do it by:

$id = Mage::getResourceModel(‘catalog/product’)->getIdBysku(5);

Its easier to use predefined functions than write your own query statements. Similiarly there are other funtions predefined in the Resource Models that you can use to retrieve data from database.

Entities:

Entities extend Magento’s resource objects. Basically, Entities pair up to selected Models and help them save to the database. Entities behave mostly like resource models, as they are just a special sub-class of resources. Entities for catalog module are in Mage\Catalog\Model\Entity.

Collections:

Entities are designed to load one item, or record, at a time. But, most of the time when we think of database queries we want to write a SELECT statement that gives use a result with multiple rows. The entity models are not able to do that.

What if we want to select all records from the database matching some criteria. Normally, a simple SELECT statement with a WHERE clause would work. But, things are not that simple because of the EAV design. Not all of the data that makes up an entity lives in one table, so we need to JOIN more tables. To properly construct a WHERE clause we would have to know exactly which tables our specific data is stored in. This is the problem that collections solve. Loading an arbitrary number of records based on criteria is the job of entity collection.

Probably the most useful method of a collection is the addAttributeToFilter method. This method takes an attribute code and a condition.

$products = Mage::getModel(’catalog/product’)->getCollection();
$products->addAttributeToFilter(’sku’, ’9999’);
$products->load();

In the above example, the condition is a simple string, but the condition can also be an array. When passing a condition array, the key of the array designates the type of comparison. The type can be eq, for equals, like for like comparisons, gt for a greater than comparison. Here is the same example above, but searching for an array of product IDs.

$products = Mage::getModel(’catalog/product’)->getCollection();
$products->addAttributeToFilter(’entity_id’, array(’in’=> array(1,2,36,35) ));
$products->load();

//runs the query
SELECT  * FROM ‘catalog_product_entity‘ WHERE  (e.entity_id in (1, 2, 36, 35))
*/

For loading selected attribute values , we can use the addAttributeToSelect method.

This is only scratching the surface of the SQL that you can generate with collections. Look at the Eav/Model/Entity/Collection/Abstract.php file for a full list of methods to manipulate your SQL. Remember that collections are the only way to load entity objects if you need to use a WHERE clause other than querying against the table’s primary key field. When dealing with non-entity models, you can always write raw SQL and run it against a resource connection.

Using Zend to prepare statements:

You may have noticed that in the ResourceModel files magento team does not use plain SQL.  Instead they prepare it using Zend_Db This means that Magento is using Zend for handling requests.

$read = Mage::getSingleton(’core/resource’)->getConnection(’core_read’);
echo get_class($read);

You will get the class as Varien_Db_Adapter_Pdo_Mysql which is an extension of Zend_Db_Adapter Abstract. You can create an instance of a Zend_Db_Select object using the select() method of a Zend_Db_Adapter_Abstract object. This can be used to build ‘SELECT’ queries. When building the query, you can add clauses of the query one by one. There is a separate method to add each clause to the Zend_Db_Select object. For example:

To Build this query:
“SELECT product_id, product_name, price FROM “products” WHERE price > 100.00″ //this is not a magento query

We can write:
$select = $read->select()->from(‘products’, array(‘product_id’, ‘product_name’, ‘price’))->where(‘price > 100.00′);

More on how to build ‘SELECT’ statements using Zend here : http://framework.zend.com/manual/en/zend.db.select.html.

Lets look at an example:

What if you want to load all products in a particular category:

$resource = Mage::getSingleton(‘core/resource’);
$read = $resource->getConnection(‘core_read’);
$categoryProductTable=$read->getTableName(‘category/category_product’)
$select = $read->select()->from(array(‘cp’=>$categoryProductTable))->where(‘cp.category_id=?’, ’3′);
$products=$read->fetchAll($select);
foreach($products as $row)
{
$product = Mage::getModel(‘catalog/product’)->load($row['product_id']);
echo $product->getName();
}

27
May

We will be looking into some important methods used frequently in Magento like getModel(), getData(), getSingleton()..

getModel() (To create an instance of a Model class) :

The getModel() function is used to create an instance of a Model class. For example

$Product = Mage::getModel(’catalog/product’);

would basically tell Magento to create an instance of Mage_Catalog_Model_Product class. (ie. Mage/Catalog/Model/Product.php). If we now echo the get_class($Product) we would get the Mage_Catalog_Model_Product text displayed in our browser. You can use all methods defined in Product.php. Some useful methods include getName, getPrice, getTypeId, getStatus which we can execute like:

echo ‘Product name: ‘. $Product->getName();
echo ‘Product price: ‘ . $Product->getPrice();

However, the above code wont give you anything. You will not see any price displayed in the browser. This is because you haven’t loaded the product using load() method. We have to use the load method to load some data into our newly instantiated object.

$Product->load(53);

where 53 is the product id.

getData() (To get relevant data from the object instance)

This function is used to get relevant data from the object instance. Let’s say you want to retrieve the SKU value. You can use some other method for that like getSku() method that needs to be executed on object of Product type or you can:

echo $Product->getData(’sku’);

getData can be executed with or without any parameters passed to it. If you execute it without any parameters you get the array variable as a result. You can’t directly echo the array. You would have to map it to some field, like echo $arayVar['someField']. What are all the available fields? To find out you can do something like

echo ‘<pre>’;
print_r($Product->getData());
echo ‘</pre>’;

You can get the sku of the product by:

$ProductData = $Product->getData();
echo $ProductData->sku;

or:

echo $Product->getData(’sku’)

Another example:

$ProductData = $Product->getData();
$StockItem = $ProductData->stock_item;

Now your $StockItem variable is a object of type Mage_CatalogInventory_Model_Stock_Item and you can go ahead and use the same principle used until this point to find it’s methods and the data it holds.

First use the getModel to create the instance of object then you use getData to retrieve the data from that instance.

getSingleton():

One of the important architectural feature is it’s Singleton design pattern. In short, Singleton design pattern ensures a class has only one instance. Therefore one should provide a global point of access to that single instance of a class.

So when you are using getSingleton you are calling already instantiated object. So if you get the empty array as a result, it means the object is empty. Only the blueprint is there, but nothing is loaded in it.

Registry:

We had told that Magento provides global point of access to single instance of classes. How does Magento do it ?. This is done by using an array called registry in Mage.php.

Checkout the Mage.php file and you will see an array variable called _registry.

In order to provide global point of access for an instance of a class we must register the object into the registry array by using a function called register. For example:

Mage::register(’events’, new Varien_Event_Collection());
Mage::register(’config’, new Mage_Core_Model_Config());

Here is the code of the register() function:

public static function register($key, $value, $graceful = false)
{
if(isset(self::$_registry[$key])) {
if ($graceful) {
return;
}
Mage::throwException(’Mage registry key “‘.$key.’” already exists’);
}
self::$_registry[$key] = $value;}

Basically what it does it registers an object with name as $key and the instance as $value into the registry array. You can also unregister an object using the unregister() function.

Whenever we want to call an object from the registry we use the registry() function. For example in the getConfig() method:

public static function getConfig()
{
return Mage::registry(’config’);
}

Previously we had registered an  instance of Mage_Core_Model_Config as ‘config’. The getConfig() function returns that instance. Here is the code for registry() function:

public static function registry($key)
{
if (isset(self::$_registry[$key])) {
return self::$_registry[$key];
}
return null;
}

Analyzing the getModel() and getSingleton() code:

The getModel() and getSingleton() functions works by using this registry. Here is the code for the getModel() function:

public static function getModel($modelClass=”, $arguments=array())
{
return Mage::getConfig()->getModelInstance($modelClass, $arguments);
}

Basically it gets the Config object (This object is formed by parsing all the config.xml files, so it contains all config details of all moules) and then makes an instance of the model that is specified by the $modelClass argument.(for example if $modelClass is ‘catalog/product it creates an instance of Mage_Catalog_Model_Product)

Now take a look at the getSingleton Method:

public static function getSingleton($modelClass=”, array $arguments=array())
{
$registryKey = ‘_singleton/’.$modelClass;
if (!Mage::registry($registryKey)) {
Mage::register($registryKey, Mage::getModel($modelClass, $arguments));
}
return Mage::registry($registryKey);
}

Basically it checks wheter an instance of that class is already existing, if so return that instance else create a new instance and register it as ‘_singleton/$modelClass’.

26
May

Login as admin, then select System->configuration, then select ‘Sales‘ from left Nav and click on ‘Minimum order amount‘.
Then select Yes from Enable dropdown, enter Minimum order amount, enter message and also enter error message that will be shown in shopping cart.

,

26
May

For understanding controllers we need to know how the Magento processes requests from the browser. Look at the following diagram which shows the page request flow. This diagram will give you an idea of the job of controllers.

page-request-flow

Job of the controller:

The controller receives a URL request from the browser. The controller has to process this request to find what code to run. This is done via a router. Routing is the process of taking a URL and decomposing it into parameters to determine which module, controller, and action of that controller should receive the request. The URL gets “routed” to a particular Controller, which in turns tells Magento what to do. The Controller tells Magento which layout is to be used. This determines which modules are put into place, which in turn tells Magento what Views to output. The data from the Models are given to the Views to be displayed. In the scheme of things here, Blocks fit roughly between the View and the Model.

Decomposing the URL:

Now we will see how a browser request to a URL gets translated into module execution. Generally speaking any URL can be deconstructed as follows:

decomposeurl

As you can see the above URL has been decomposed to get the module as ‘customer’, the controller as ‘AccountController’ and the action to be performed as ‘indexAction()’. Checkout the AccountController file in Mage\Customer\controllers\ . You will see an indexAction() function. This is the function that is executed by the above URL request.

indexAction

If the URL was http://example.com/magento/(index.php)/customer/account/login then the loginAction() will be executed.

So The front controller (the controller that receives requests from browser) “dispatches” the request to its internal list of “routers” and determines if any of the routers “match()” the request’s parameters. If so, then a new MVC Controller (‘AccountController’ in this case) is created from the matching module and, again, the request is “dispatched” to this controller object. The final MVC-style controller is technically a “Front Action” (Notice that AccountController extends Mage_Core_Controller_Front_Action).This new Action object dynamically calls one of its own action methods (‘indexAction()’) and marks the request as being “dispatched” (i.e. finished).

How the routers find a match?

The front controller has an array of “routers” that it uses to decide which module the URL should trigger. This correlation between URL and module name is defined in the config.xml files of the modules. Checkout the config.xml file of the Customer module you will see this:

customerrouterxml

Once a router has found a match of the first part of the URL to a defined frontName value from the XML, this value gets directly translated into a module name with a little adjustment to the capitalization of the words (ie. ‘customer’ to ‘Customer’) The controller and the action names are taken from the URL.

What if you only enter example.com/customer/ and not specify the controller and action you want. Find out what result you get. You will be directed to the 404 Not Found error page. How did that happen? If any value is missing, the defaults are taken from the config.xml of the Core module. If any indicators of module, controller, or action are missing from the URL the values are read from the default tag under web. By default, the CMS supplies these XML values it its own config.xml file. You can change these values by going to System->Configuration->Web.

defaultroute

Actions:

Actions are classes that extend Mage_Core_Controller_Front_Action which, in turn, extends Mage_Core_Controller_Varien_Action.(‘AccountController’ is an Action). A request is dispatched to an action method (‘indexAction()’). Action methods have the word “Action” appended to their names to distinguish them from normal class methods. Appending a word to the method name also helps to stop people from running unexpected methods from the URL. Imagine someone requesting example.com/index.php/customer/account/__destruct. If the system did not protect action names, the resulting method call would look something like this:

$controllerInstance->__destruct();

Something like this could potentially be a vector to open up attacks on your site. So Magento protects the action method names by appending Action to any value taken from the URL. Action and action methods are where the primary business logic for a request happens. Typically the action methods will load a model or two based on IDs or other URL parameters, kick off a few methods of these models, and then run the layout sequence.

Launch your browser and type http://127.0.0.1/magento/index.php/customer/account/login/. This should execute the loginAction() method in AccountController class of Customer module.

The Output will be as follows:

login

, , , , , , , , , , , , ,