magento development

Magento Dataflow - Optimized Product Import [Part 3]

Magento Dataflow module comes with standard product adapter (see Magento Dataflow - Default Adapters [Part 2]). Sometimes though, default solution is not enough and you may want to create your own adapter processing products.

Creating own adapter is not hard, but if you forget two lines of code, you may be very surprised with its performance. These two lines you should add before calling $product->save():

$product->setIsMassupdate(true);
$product->setExcludeUrlRewrite(true);

First line sets $data variable 'is_massupdate', which can be later checked to save some postprocessing actions time. Some observers watching for catalog_product_save_after event check this value (i.e. CatalogRule module's Observer, which skips action of applying catalog rules on products if  $product->getIsMassupdate() returns  true )

Second line also sets $data variable 'exclude_url_rewrite', which is used by afterSave method of Mage_Catalog_Model_Product_Attribute_Backend_Urlkey to check if catalog url rewrite cache should be refreshed.

Those two lines allow you to save few seconds per each row of processed products, so keep in mind to not forget about them

Magento Translations Module

One of best things in magento is you can easily add new language versions to the site. It's all done using CSV files - 1 file for 1 module per language. For most popular languages translations are available as free Magento modules on magento connect.

What if we want to change the translations and don't lose changes after updating magento and translations? Or if we want to have translation common independently of module? Or just list all the texts that were added by us and are not original magento texts? We can create our own very simple translations module.

You need to edit only 2 files:

  • etc/config.xml
    <?xml version="1.0"?>
    <config>
            <modules>
                    <Baobaz_Translations>
                            <version>0.1.0</version>
                    </Baobaz_Translations>
            </modules>
           
            <global>
                    <helpers>
                            <translations>
                                    <class>Baobaz_Translations_Helper</class>
                            </translations>
                    </helpers>
            </global>
           
            <frontend>
                    <translate>
                            <modules>
                                    <Baobaz_Translations>
                                            <files>
                                                    <default>Baobaz_Translations.csv</default>
                                            </files>
                                    </Baobaz_Translations>
                            </modules>
                    </translate>
            </frontend>

    </config>
  • and helper/data.php:
    class Baobaz_Translations_Helper_Data extends Mage_Core_Helper_Abstract
    {

    }

and that's it... Module must be turned on (in etc/modules) and it will work fine.

in app/locale/fr_fr/ we create file Baobaz_Translations.csv and in it we put all the new translations we need.

Then, anywhere in magento - doesn't matter if it's template, block or controller - we can use:

echo Mage::helper('translations')->__('text to translate');

Yes. It is that simple.

Magento module: Create your own controller

Respect my authoritahMagento is based on MVC model. This model helps for defining models, view (layout + templates) and controllers. Despite big amount of modules available by default in Magento and on Magento Connect, you may want to create your own module and define your controller for you Magento website. No problem, this tutorial will explain you how to create your own controller and how to make it respect its authoritah to layouts and templates.

Purpose of this example controller will be to give result of two integers multiplication (very useful if you lost your calculator). Integers will be provided through a basic form. Result will be displayed in a Magento notification.

Before starting creation of your module, please turn off the cache management in order to see immediately every change.

Creating your module

Our extension will be named arithmetic. Folders needed for this extension are created.

$ mkdir -p app/code/local/Baobaz/Arithmetic/controllers
$ mkdir -p app/code/local/Baobaz/Arithmetic/etc

We create file app/code/local/Baobaz/Arithmetic/etc/config.xml, in order to register this extension

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <baobaz_arithmetic>
            <version>0.0.1</version>
        </baobaz_arithmetic>
    </modules>
</config>

And a file app/etc/modules/Baobaz_Arithmetic.xml for its activation:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Baobaz_Arithmetic>
            <active>true</active>
            <codePool>local</codePool>
        </Baobaz_Arithmetic>
    </modules>
</config>

For more informations about creation of a new extension, please check Wojtek's post "Developing module for Magento Tutorial - Where to Begin [Part 1]".

Creating a controller

You need now to create file app/code/local/Baobaz/Arithmetic/controllers/IntegerController.php and write method that will be used for multiplication.

Controller files must always follow pattern xxxxxController.php (xxxxx will be used after in url for calling this controller) and put in controllers folder.
Controllers methods names must follow pattern yyyyyAction (yyyyy will also be used in url for calling this controller). For the moment content of our file is:

class Baobaz_Arithmetic_IntegerController extends Mage_Core_Controller_Front_Action
{
    public function multiplyAction(){
    }
}

We need to indicate now that some controllers are available in Arithmetic modules. For doing that, we add the following content in app/code/local/Baobaz/Arithmetic/etc/config.xml file:

<config>
    ...
    <frontend>
        <routers>
            <arithmetic>
                <use>standard</use>
                <args>
                    <module>Baobaz_Arithmetic</module>
                    <frontName>arithmetic</frontName>
                </args>
            </arithmetic>
        </routers>  
    </frontend>
</config>

Let see how this router declaration works:

  • <frontend> indicates that router will be use in front part of website
  • <routers> is where you declare all your routers
  • <arithmetic> is identifier of this router
  • <use>standard</use> can take value standard (for front part) or admin (for admin part).
  • <module>Baobaz_Arithmetic</module> indicates which module contain controller that handles this router
  • <frontName>arithmetic</frontName> is router name that will be used in url

We can now modify multiplyAction method for making it displaying a message:

public function multiplyAction(){
    echo "Respect my authoritah";
}

When you call now url http://monsitemagento/arithmetic/integer/multiply message "Respect my authoritah" will be displayed. Let dissect this url:

  • arithmetic tells that controller is in Baobaz_Arithmetic module
  • integer tells that controllers/integerController.php file must be cehcked
  • multiply tells that multiplyAction method must be chosen in this file

Displaying a template

We define which layout file will be used in the module:

<config>
    ...
    <frontend>
        ...
        <layout>
            <updates>
                <arithmetic>
                    <file>arithmetic.xml</file>
                </arithmetic>
            </updates>
        </layout>
    </frontend>
</config>

We create app/design/frontend/default/default/layout/arithmetic.xml file in order to define which blocks will be used for controller that was just made.

<?xml version="1.0" encoding="UTF-8"?>
<layout version="0.1.0">
    <arithmetic_integer_multiply>
        <reference name="root">
            <action method="setTemplate">
                <template>page/1column.phtml</template>
            </action>
        </reference>
        <reference name="content">
            <block type="core/template" name="arithmetic_integer_multiply" template="arithmetic/integer/multiply.phtml"></block>
        </reference>
    </arithmetic_integer_multiply>
</layout>

Main template used by arithmetic/integer/multiply is page/1column.phtml. For "content" part of this template, only arithmetic_integer_multiply block will be displayed. This block does not need any particular management. It is then set with core/template type that is default type. Template file used will be arithmetic/integer/multiply.phtml.

Our template is defined, app/design/frontend/default/default/template/arithmetic/integer/multiply.phtml must then be created. This file will be empty for the moment..

For displaying correctly layout, it must be loaded in controller

public function multiplyAction(){
    $this->loadLayout();
    $this->renderLayout();
}

Interaction between template and controller

Our template will just have a basic form for providing integers to multiply

<form action="<?php echo Mage::getUrl('arithmetic/integer/multiply') ?>" method="post">
    <fieldset>
        <ul>
            <li>
                <label for="int1">Integer 1</label>
                <input type="text" id="int1" name="int1" />
            </li>
            <li>
                <label for="int2">Integer 2</label>
                <input type="text" id="int2" name="int2" />
            </li>
            <li><input type="submit" value="Multiply" /></li>
        </ul>
    </fieldset>
</form>

Action form url is again arithmetic/integer/multiply. Controller action must then be modified in order to manage data from form and to give result.

public function multiplyAction(){
    if ($this->getRequest()->isPost()){
        $int1 = $this->getRequest()->getPost('int1');
        $int2 = $this->getRequest()->getPost('int2');
        $result = $int1 * $int2;
    Mage::getSingleton('customer/session')->addSuccess("$int1 * $int2 = $result");
    }
    $this->loadLayout();
    $this->_initLayoutMessages('customer/session');
    $this->renderLayout();
}

In order to know if controller is called after using form, following instruction is used:

$this->getRequest()->isPost()

Result is put in 'customer/session' session. For being able to display this result in template, message template must be loaded in multiplyAction method:

$this->_initLayoutMessages('customer/session');

Add then in you template the following line where you want to display the result

echo $this->getMessagesBlock()->getGroupedHtml();

And here we are: we have now a new controller that displays result of a multiplication.

All files used in this tutorial are available in baobaz_arithmetic.tar.gz archive.

This post was inspired by Alan Storm's post Magento Front Controller. Take a look at his very interesting blog.

Developing module for Magento Tutorial - Where to Begin [Part 1]

Have you ever tried to read the complete list of features Magento comes with? Gosh, it's huge. And it's even more impressive when you look at Magento Connect and all the extensions available there. However, if you are reading this blog entry you are probably looking for something which is not available yet. Or maybe you are a developer who just loves to play with the code. Either way, for serious business reasons or just because of your hack-ish nature, developing a Magento module is fun.

Let's create something really really simple. The module. You might wonder what that nifty module will be doing. Well in this fist part it will does nothing but being declared in Magento!

Where to begin?

Take a loot at the directories structure of your Magento installation. In app > code > local, create a directory, which will be a kind of container for your modules, in Magento it's usually called code pool. If you wonder how to name it, I can say that company name you're working for is probably a good one. It is in my case ;)

So let's create it

$ cd app/code/local/
$ mkdir Baobaz

Then, in this container we should create the module. For the purpose of this blog let's call it "Reader" (in next few parts the reason of using this name should clarify, if not... well.. it's still sounds good ;) )

$ cd Baobaz
$ mkdir Reader

So far, so good. The next step is a little bit more complex, but still simple. Let's change current directory...

$ cd ../../../etc/modules/

Hm... where are we?

$ pwd /Your/favorite/place/for/web/projects/magento/app/etc/modules/

Now, you must tell Magento about the module. To do this you must create an xml file. Take your favorite editor and create a file called Baobaz_Reader.xml (as you probably guessed the name consists of <container_name>_<module_name>.xml

$ vi Baobaz_Reader.xml

Now put this into your file

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

Save file. And.... Voilà! That's one small step for a man, one giant leap for man...gento. :-)

To be sure that Magento knows about our new module let's login to admin panel. Navigate to System > Configuration, choose Advanced, from Advances section (yes, it's on the left).

You should see Baobaz_Reader at the top (or somewhere there) of the "Disable modules output" list.

OK, OK, OK... I admit that our nifty module makes nothing so far but be patience, in a few next parts we will add some more useful features.

To be continued...

Translation in Magento

Translations between languages is quite simple in Magento. For an advanced developer, who has already worked with PHP frameworks, it is not a matter to discuss, but for a beginner it may need a few words of explanation. All texts on the page that should be translated, are stored in CSV files, in /app/locale/[language] directory. The [language] part of the path is build of language code, e. g. 'en_US'.

If you have a look at the files content, you will find that every file structure is the same, one line contains two texts: one in your default language, and one in the destination language. To use the files - also called dictionaries - they must be declared in module's config file (config.xml). You should put this code directly inside your frontend or admin node of the config file.

<translate>
        <modules>
                <companyname_modulename>
                        <files>
                        <default>CompanyName_ModuleName.csv</default>
                        </files>
                </companyname_modulename>
        </modules>
</translate>

That's all. Now, when you want to use a phrase that should be translated, use:

echo $this->__('text to translate');

Magento will look in you dictionary files for the phrase 'text to translate' and will return the corresponding text in the proper language.

How to use Magento CMS feature to create multilingual content

Magento natively integrates CMS features.

Two types of objects are available to allow you to create content in Magento:

  • Pages, which allow you to display general information such as your terms of sale, contact information, store locations, et cetera.
  • Static blocks ( "static" is not quite the appropriate term as discussed below, it means that the content of these blocks is editable directly from the back-office, without modifying php source code), which allow you to enter information to be usually displayed on several pages, such as footers, headers, but also specific informations (predictive site shut down, promotions ...).

Note that these objects, static pages or blocks, may contain elements dynamically calculated and it is relatively easy to make create new ones for the webmaster (for instance to display current date and time, number of online customers, last item sold...).

Furthermore, these blocks and pages can also contain other blocks, static (ie, whose content is changed here) or not (block articles, bestsellers block ...).

Finally, these pages and blocks fit perfectly into the Magento architecture: for the same page or static block identifier, you can (but do not have to) create one version per View Store. It becomes very easy to create specific content for each language and the system will display the version corresponding to the language of the customer without you having wrote a single line of code.

Of course the system is not without fault and we can regret:

  • The lack of a rich text editing tool,
  • The inability to manage hierarchical content,
  • The lack of online form management.

We shall see very soon how to find a workaround these deficiencies.

Sharing informations between controllers and blocks in Magento

As we know magento has quite powerful templates system. The same thing we can say about logic part. In general magento consists of about 50 modules. There is module for checkout, for customer, for cms pages etc. Module consists of few parts - beside design parts there are: components, blocks, models and  helpers. Components contains all actions that happen when the module is used.  When we have url http://www.my-eshop.tld/index.php/a/b/c

it means that module a must run action c in controller b.

Blocks are responsible for preparing data for templates. Each template used must have it's block. Sometimes blocks are simple, sometimes they can be more complicated. Anyway in some situations it's useful to move data from controller to block so we won't have to calculate it few times in one request.

For that we can use possibility that magento gives us - registering data.

In controller we store our data in variable called $data, we can save it to register like this:

Mage::register('MyData',$dat);

Then in block file, you can get all this data, back using.

$data = Mage::registry('MyData');

It's a small feature that can be very useful sometimes.