get current product id and product name in magento


$currentproduct = Mage::registry(‘current_product’);

// general product data
echo $currentproduct->getName();
echo $currentproduct->getId();
Advertisements

Magento for Developers: Part 4 – Magento Layouts, Blocks and Templatese


Developers new to Magento are often confused by the Layout and View system. This article will take a look at Magento’s Layout/Block approach, and show you how it fits into Magento MVC worldview.

Unlike many popular MVC systems, Magento’s Action Controller does not pass a data object to the view or set properties on the view object. Instead, the View component directly references system models to get the information it needs for display.

One consequence of this design decision is that the View has been separated into Blocks and Templates. Blocks are PHP objects, Templates are “raw” PHP files (with a .phtml extension) that contain a mix of HTML and PHP (where PHP is used as a templating language). Each Block is tied to a single Template file. Inside a phtml file, PHP’s $this keyword will contain a reference to the Template’s Block object.

A quick example. Take a look a the default product Template at

app/design/frontend/base/default/template/catalog/product/list.phtml

You’ll see the following PHP template code.

<?php $_productCollection=$this->getLoadedProductCollection() ?>    
    <?php if(!$_productCollection->count()): ?> <div class="note-msg">

        <?php echo $this->__("There are no products matching the selection."?>    </div>

    <?php else: ?>

The getLoadedProductCollection method can be found in the Template’s Block, Mage_Catalog_Block_Product_List as shown:

File: app/code/core/Mage/Catalog/Block/Product/List.php
...
public function getLoadedProductCollection()

{
    return $this->_getProductCollection();
}   
...

The block’s _getProductCollection then instantiates models and reads their data, returning a result to the template.

Nesting Blocks

The real power of Blocks/Templates come with the getChildHtml method. This allows you to include the contents of a secondary Block/Template inside of a primary Block/Template.

Blocks calling Blocks calling Blocks is how the entire HTML layout for your page is created. Take a look at the one column layout Template.

File: app/design/frontend/base/default/template/page/one-column.phtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="?php echo $this->getLang() ?>" lang="<?php echo $this->getLang() ?>">

<head>
<?php echo $this->getChildHtml('head'?>
</head>
<body class="page-popup <?php echo $this->getBodyClass()?$this->getBodyClass():'' ?>">

    <?php echo $this->getChildHtml('content'?>
    <?php echo $this->getChildHtml('before_body_end'?>

    <?php echo $this->getAbsoluteFooter() ?>
</body>

The template itself is only 11 lines long. However, each call to $this->getChildHtml(...) will include and render another Block. These Blocks will, in turn, use getChildHtml to render other Blocks. It’s Blocks all the way down.

The Layout

So, Blocks and Templates are all well and good, but you’re probably wondering

  1. How do I tell Magento which Blocks I want to use on a page?
  2. How do I tell Magento which Block I should start rendering with?
  3. How do I specify a particular Block in getChildHtml(...)? Those argument strings don’t look like Block names to me.

This is where the Layout Object enters the picture. The Layout Object is an XML object that will define which Blocks are included on a page, and which Block(s) should kick off the rendering process.

Last time we were echoing content directly from out Action Methods. This time let’s create a simple HTML template for our Hello World module.

First, create a file at

app/design/frontend/base/default/layout/local.xml

with the following contents

<layout version="0.1.0">
    <default>
        <reference name="root">

            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml" />
        </reference>

    </default>
</layout>

Then, create a file at

app/code/local/Magentotutorial/Helloworld/simple_page.phtml

with the following contents

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Untitled</title>
    <meta name="generator" content="BBEdit 9.2" />

    <style type="text/css">
        body {
            background-color:#f00;
        }
    </style>
</head>

<body>

</body>
</html>

Finally, each Action Controller is responsible for kicking off the layout process. We’ll need to add two method calls to the Action Method.

public function indexAction() {
    //remove our previous echo

    //echo 'Hello Index!';
    $this->loadLayout();
    $this->renderLayout();
}

Clear your Magento cache and reload your Hello World controller page. You should now see a website with a bright red background and an HTML source that matches what’s in simple_page.phtml.

What’s Going On

So, that’s a lot of voodoo and cryptic incantations. Let’s take a look at what’s going on.

First, you’ll want to install the Layoutviewer module. This is a module similar to the Configviewer module you built in the Hello World article that will let us peek at some of Magento’s internals.

Once you’ve installed the module (similar to how you setup the Configviewer module), go to the following URL

http://example.com/helloworld/index/index?showLayout=page

This is the layout xml for your page/request. It’s made up of <block />, <reference /> and <remove /> tags. When you call the loadLayout method of your Action Controller, Magento will

  1. Generate this Layout XML
  2. Instantiate a Block class for each <block /> and <reference /> tag, looking up the class using the tag’s name attribute as a global config path and store in the internal _blocks array of the layout object.
  3. If the <block /> tag contains an output attribute, its value is added to the internal _output array of the layout object.

Then, when you call the renderLayout method in your Action Controller, Magento will iterate over all the Blocks in the _blocks array, using the value of the output attribute as a callback method. This is almost always toHtml, and means the starting point for output will be that Block’s Template.

The following sections will cover how Blocks are instantiated, how this layout file is generated, and finishes up with kicking off the output process.

Block Instantiation

So, within a Layout XML file, a <block /> or <reference /> has a “type” that’s actually a Grouped Class Name URI

<block type="page/html" ...

<block type="page/template_links"

The URI references a location in the (say it with me) global config. The first portion of the URI (in the above examples page) will be used to query the global config to find the page class name. The second portion of the URI (in the two examples above, html and template_links) will be appended to the base class name to create the class Magento should instantiate.

We’ll go through page/html as an example. First, Magento looks for the global config node at

/global/blocks/page

and finds

<page>
    <class>
        Mage_Page_Block
    </class>
</page>

This gives us our base class name Mage_Page_Block. Then, the second part of the URI (html) is appended to the class name to give us our final Block class name Mage_Page_Block_Html. This is the class that will be instantiated.

Remember, Blocks are one of the Grouped Class Names in Magento, all which share a similar instantiation method.

The Difference Between <block /> and <reference />

We mentioned that both <blocks /> and <refernces /> will instantiate Block classes, and you’re probably wondering what the difference is.

<reference />’s are used to replace existing Blocks in a layout file. For example, consider the following layout snippet.

<block type="page/html" name="root" output="toHtml" template="page/2columns-left.phtml">

    <!-- ... sub blocks ... -->
</block>
<!-- ... -->
<reference name="root">

    <block type="page/someothertype" name="root" template="path/to/some/other/template" />
    <!-- ... sub blocks ... -->

    </block>
</reference>

Magento initially creates a page/html Block named root. Then, when it later encounters the reference with the same name (root), it will replace the original root <block /> with the <block /> enclosed in the <reference />.

This is what we’ve done in our local.xml file from above.

<layout version="0.1.0">
    <default>
        <reference name="root">
            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml" />

        </reference>
    </default>
</layout>

The Block named root has been replaced with our Block, which points at a different phtml Template file.

How Layout Files are Generated

So, we have a slightly better understanding of what’s going on with the Layout XML, but where is this XML file coming from? To answer that question, we need to introduce two new concepts; Handles and the Package Layout.

Handles

Each page request in Magento will generate several unique Handles. The Layoutview module can show you these Handles by using a URL something like

http://example.com/helloworld/index/index?showLayout=handles

You should see a list similar to the following (depending on your configuration)

  1. default
  2. STORE_bare_us
  3. THEME_frontend_default_default
  4. helloworld_index_index
  5. customer_logged_out

Each of these is a Handle. Handles are set in a variety of places within the Magento system. The two we want to pay attention to are default and helloworld_index_index. The default Handle is present in every request into the Magento system. The helloworld_index_index Handle is created by combining the frontName (helloworld), Action Controller name (index), and Action Controller Action Method (index) into a single string. This means each possible method on an Action Controller has a Handle associated with it.

Remember that “index” is the Magento default for both Action Controllers and Action Methods, so the following request

http://example.com/helloworld/?showLayout=handles

Will also produce a Handle named helloworld_index_index

Package Layout

You can think of the Package Layout similar to the global config. It’s a large XML file that contains every possible layout configuration for a particular Magento install. Let’s take a look at it using the Layoutview module

http://example.com/helloworld/index/index?showLayout=package

This may take a while to load. If your browser is choking on the XML rendering, try the text format

http://example.com/helloworld/index/index?showLayout=package&showLayoutFormat=text

You should see a very large XML file. This is the Package Layout. This XML file is created by combining the contents of all the XML layout files for the current theme (or package). For the default install, this is at

app/design/frontend/base/default/layout/

Behind the scenes there’s an <updates /> section of the global config that contains nodes with all the file names to load. Once the files listed in the config have been combined, Magento will merge in one last xml file, local.xml. This is the file where you’re able to add your customizations to your Magento install.

Combining Handles and The Package Layout

So, if you look at the Package Layout, you’ll see some familiar tags such as <block /> and <reference />, but they’re all surrounded by tags that look like

<default />
<catalogsearch_advanced_index />
etc...

These are all Handle tags. The Layout for an individual request is generated by grabbing all the sections of the Package Layout that match any Handles for the request. So, in our example above, our layout is being generated by grabbing tags from the following sections

<default />
<STORE_bare_us />

<THEME_frontend_default_default />
<helloworld_index_index />
<customer_logged_out />

There’s one additional tag you’ll need to be aware of in the Package Layout. The <update /> tag allows you to include another Handle’s tags. For example

<customer_account_index>
    <!-- ... -->

    <update handle="customer_account"/>
    <!-- ... -->
</customer_account_index>

Is saying that requests with a customer_account_index Handle should include <reference />s and <blocks />s from the <customer_account /> Handle.

Applying What We’ve Learned

OK, that’s a lot of theory. Lets get back to what we did earlier. Knowing what we know now, adding

<layout version="0.1.0">
    <default>
        <reference name="root">
            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml" />

        </reference>
    </default>
</layout>

to local.xml means we’ve overridden the “root” tag. with a different Block. By placing this in the <default /> Handle we’ve ensured that this override will happen for every page request in the system. That’s probably not what we want.

If you go to any other page in your Magento site, you’ll notice they’re either blank white, or have the same red background that your hello world page does. Let’s change your local.xml file so it only applies to the hello world page. We’ll do this by changing default to use the full action name handle (helloworld_index_index).

<layout version="0.1.0">
    <helloworld_index_index>
        <reference name="root">

            <block type="page/html" name="root" output="toHtml" template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml" />
        </reference>

    </helloworld_index_index>
</layout>

Clear your Magento cache, and the rest of your pages should be restored.

Right now this only applies to our index Action Method. Let’s add it to the goodbye Action Method as well. In your Action Controller, modify the goodbye action so it looks like

public function goodbyeAction() {
    $this->loadLayout();

    $this->renderLayout();          
}

If you load up the following URL, you’ll notice you’re still getting the default Magento layout.

http://example.com/helloworld/index/goodbye

We need to add a Handle for the full action name (helloworldindexgoodbye) to our local.xml file. Rather than specify a new <reference />, lets use the update tag to match the helloworld_index_index Handle.

<layout version="0.1.0">
    <!-- ... -->
    <helloworld_index_goodbye>
        <update handle="helloworld_index_index" />

    </helloworld_index_goodbye>
</layout>

Loading the following pages (after clearing your Magento cache) should now produce identical results.

http://example.com/helloworld/index/index
http://example.com/helloworld/index/goodbye

Starting Output and getChildHtml

In a standard configuration, output starts on the Block named root (because it has an output attribute). We’ve overridden root’s Template with our own

template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml"

Templates are referenced from the root folder of the current theme. In this case, that’s

app/design/frontend/base/default

so we need to climb five directories (../../../../../) then drill down to our custom page. Most Magento Templates are stored in

app/design/frontend/base/default/templates

Adding Content Blocks

A simple red page is pretty boring. Let’s add some content to this page. Change your <helloworld_index_index /> Handle in local.xml so it looks like the following

<helloworld_index_index>

    <reference name="root">
        <block type="page/html" name="root" template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml">
            <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/>

        </block>
    </reference>
</helloworld_index_index>

We’re adding a new Block nested within our root. This is a Block that’s distributed with Magento, and will display a customer registration form. By nesting this Block within our root Block, we’ve made it available to be pulled into our simple_page.html Template. Next, we’ll use the Block’s getChildHtml method in our simple_page.phtml file. Edit simple_page.html so it looks like this

<body>
    <?php echo $this->getChildHtml('customer_form_register'); ?>
</body>

Clear your Magento cache and reload the page and you should see the customer registration form on your red background. Magento also has a Block named top.links. Let’s try including that. Change your simple_page.html file so it reads

<body>
    <h1>Links</h1>
    <?php echo $this->getChildHtml('top.links'); <?php echo '?';?>>

</body>

When you reload the page, you’ll notice that your <h1>Links</h1> title is rendering, but nothing is rendering for top.links. That’s because we didn’t add it to local.xml. The getChildHtml method can only include Blocks that are specified as sub-Blocks in the Layout. This allows Magento to only instantiate the Blocks it needs, and also allows you to set difference Templates for Blocks based on context.

Let’s add the top.links Block to our local.xml

<helloworld_index_index>

    <reference name="root">
        <block type="page/html" name="root" template="../../../../../code/local/Magentotutorial/Helloworld/simple_page.phtml">
            <block type="page/template_links" name="top.links"/>

            <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/>
        </block>
    </reference>

</helloworld_index_index>

Clear your cache and reload the page. You should now see the top.links module.

Wrapup

That covers Layout fundamentals. If you found it somewhat daunting, don’t worry, you’ll rarely need to work with layouts on such a fundamental level. Magento provides a number of pre-built layouts which can be modified and skinned to meet the needs of your store. Understanding how the entire Layout system works can be a great help when you’re trouble shooting Layout issues, or adding new functionality to an existing Magento system.

Author Bio: Alan Storm is a technology strategist, software developer and writer living and working in Portland, OR.  He publishes articles on his website and is the creator of the Magento development extension Commerce Bug.

Magento for Developers: Part 3 – Magento Controller Dispatch


The Model-View-Controller (MVC) architecture traces its origins back to the Smalltalk Programming language and Xerox Parc. Since then, there have been many systems that describe their architecture as MVC. Each system is slightly different, but all have the goal of separating data access, business logic, and user-interface code from one another.

The architecture of most PHP MVC frameworks will looks something like this.

  1. A URL is intercepted by a single PHP file (usually called a Front Controller).
  2. This PHP file will examine the URL, and derive a Controller name and an Action name (a process that’s often called routing).
  3. The derived Controller is instantiated.
  4. The method name matching the derived Action name is called on the Controller.
  5. This Action method will instantiate and call methods on models, depending on the request variables.
  6. The Action method will also prepare a data structure of information. This data structure is passed on to the view.
  7. The view then renders HTML, using the information in the data structure it has received from the Controller.

While this pattern was a great leap forward from the “each php file is a page” pattern established early on, for some software engineers, it’s still an ugly hack. Common complaints are:

  • The Front Controller PHP file still operates in the global namespace.
  • Convention over configuration leads to less modularity.
    • URLs routing is often inflexible.
    • Controllers are often bound to specific views.
    • Even when a system offers a way to override these defaults, the convention leads to applications where it’s difficult/impossible to drop in new a new model, view, or Controller implementation without massive re-factoring.

As you’ve probably guessed, the Magento team shares this world view and has created a more abstract MVC pattern that looks something like this.

  1. A URL is intercepted by a single PHP file.
  2. This PHP file instantiates a Magento application.
  3. The Magento application instantiates a Front Controller object.
  4. Front Controller instantiates any number of Router objects (specified in global config).
  5. Routers check the request URL for a “match”.
  6. If a match is found, an Action Controller and Action are derived.
  7. The Action Controller is instantiated and the method name matching the Action Name is called.
  8. This Action method will instantiate and call methods on models, depending on the request.
  9. This Action Controller will then instantiate a Layout Object.
  10. This Layout Object will, based some request variables and system properties (also known as “handles”), create a list of Block objects that are valid for this request.
  11. Layout will also call an output method on certain Block objects, which start a nested rendering (Blocks will include other Blocks).
  12. Each Block has a corresponding Template file. Blocks contain PHP logic, templates contain HTML and PHP output code.
  13. Blocks refer directly back to the models for their data. In other words, the Action Controller does not pass them a data structure.

We’ll eventually touch on each part of this request, but for now we’re concerned with the Front Controller -> Routers -> Action Controller section.

Hello World

Enough theory, it’s time for Hello World. We’re going to

  1. Create a Hello World module in the Magento system
  2. Configure this module with routes
  3. Create Action Controller(s) for our routes

Create Hello World Module

First, we’ll create a directory structure for this module. Our directory structure should look as follows:

app/code/local/Magentotutorial/Helloworld/Block
app/code/local/Magentotutorial/Helloworld/controllers
app/code/local/Magentotutorial/Helloworld/etc
app/code/local/Magentotutorial/Helloworld/Helper
app/code/local/Magentotutorial/Helloworld/Model
app/code/local/Magentotutorial/Helloworld/sql

Then create a configuration file for the module (at path app/code/local/Magentotutorial/Helloworld/etc/config.xml):

<config>    
    <modules>
        <Magentotutorial_Helloworld>
            <version>0.1.0</version>
        </Magentotutorial_Helloworld>

    </modules>
</config>

Then create a file to activate the module (at path app/etc/modules/Magentotutorial_Helloworld.xml):

<config>
    <modules>
        <Magentotutorial_Helloworld>
            <active>true</active>

            <codePool>local</codePool>
        </Magentotutorial_Helloworld>
    </modules>
</config>

Finally, we ensure the module is active:

  1. Clear your Magento cache.
  2. In the Magento Admin, go to System->Configuration->Advanced.
  3. Expand “Disable Modules Output” (if it isn’t already).
  4. Ensure that Magentotutorial_Helloworld shows up.

Configuring Routes

Next, we’re going to configure a route. A route will turn a URL into an Action Controller and a method. Unlike other convention based PHP MVC systems, with Magento you need to explicitly define a route in the global Magento config.

In your config.xml file, add the following section:

<config>    
    ...
    <frontend>
        <routers>
            <helloworld>
                <use>standard</use>
                <args>

                    <module>Magentotutorial_Helloworld</module>
                    <frontName>helloworld</frontName>
                </args>
            </helloworld>
        </routers>  

    </frontend>
    ...
</config>

We have a lot of new terminology here, let’s break it down.

What is a <frontend>?

The <frontend> tag refers to a Magento Area. For now, think of Areas as individual Magento applications. The “frontend” Area is the public facing Magneto shopping cart application. The “admin” Area is the the private administrative console application. The “install” Area is the application you use to run though installing Magento the first time.

Why a <routers> tags if we’re configuring individual routes?

There’s a famous quote about computer science, often attributed to Phil Karlton:

“There are only two hard things in Computer Science: cache invalidation and naming things”

Magento, like all large systems, suffers from the naming problem in spades. You’ll find there are are many places in the global config, and the system in general, where the naming conventions seem unintuitive or even ambiguous. This is one of those places. Sometimes the <routers> tag will enclose configuration information about routers, other times it will enclose configuration information about the actual router objects that do the routing. This is going to seem counter intuitive at first, but as you start to work with Magento more and more, you’ll start to understand its world view a little better. (Or, in the words of Han Solo, “Hey, trust me!”).

What is a <frontName>?

When a router parses a URL, it gets separated as follows

http://example.com/frontName/actionControllerName/actionMethod/

So, by defining a value of “helloworld” in the <frontName> tags, we’re telling Magento that we want the system to respond to URLs in the form of

http://example.com/helloworld/*

Many developers new to Magento confuse this frontName with the Front Controller object. They are not the same thing. The frontName belongs solely to routing.

What’s the <helloworld> tag for?

This tag should be the lowercase version of you module name. Our module name is Helloworld, this tag is helloworld.

You’ll also notice our frontName matches our module name. It’s a loose convention to have frontNames match the module names, but it’s not a requirement. In your own modules, it’s probably better to use a route name that’s a combination of your module name and package name to avoid possible namespace collisions.

What’s <module>Magentotutorial_Helloworld</module> for?

This module tag should be the full name of your module, including its package/namespace name. This will be used by the system to locate your Controller files.

Create Action Controller(s) for our Routes

One last step to go, and we’ll have our Action Controller. Create a file at

app/code/local/Magentotutorial/Helloworld/controllers/IndexController.php

That contains the following

class Magentotutorial_Helloworld_IndexController extends Mage_Core_Controller_Front_Action {        

    public function indexAction() {

        echo 'Hello Index!';

    }

}

Clear your config cache, and load the following URL

http://exmaple.com/helloworld/index/index

You should also be able to load

http://exmaple.com/helloworld/index/
http://exmaple.com/helloworld/

You should see a blank page with the text “Hello World”. Congratulations, you’ve setup your first Magento Controller!

Where do Action Controllers go?

Action Controllers should be placed in a module’s controllers (lowercase c) folder. This is where the system will look for them.

How should Action Controllers be named?

Remember the <module> tag back in config.xml?

<module>Magentotutorial_Helloworld</module>

An Action Controller’s name will

  1. Start with this string specified in config.xml (Magentotutorial_Helloworld)
  2. Be followed by an underscore (Magentotutorial_Helloworld_)
  3. Which will be followed by the Action Controller’s name (Magentotutorial_Helloworld_Index)
  4. And finally, the word “Controller” (Magentotutorial_Helloworld_IndexController)

All Action Controllers need Mage_Core_Controller_Front_Action as an ancestor.

What’s that index/index nonsense?

As we previously mentioned, Magento URLs are routed (by default) as follows

http://example.com/frontName/actionControllerName/actionMethod/

So in the URL

http://example.com/helloworld/index/index

the URI portion “helloworld” is the frontName, which is followed by index (The Action Controller name), which is followed by another index, which is the name of the Action Method that will be called. (an Action of index will call the method public function indexAction(){...}.

If a URL is incomplete, Magento uses “index” as the default, which is why the following URLs are equivalent.

http://example.com/helloworld/index
http://example.com/helloworld

If we had a URL that looked like this

http://example.com/checkout/cart/add

Magento would

  1. Consult the global config to find the module to use for the frontName checkout (Mage_Checkout)
  2. Look for the cart Action Controller (Mage_Checkout_CartController)
  3. Call the addAction method on the cart Action Controller
Other Action Controller Tricks

Let’s try adding a non-default method to our Action Controller. Add the following code to IndexController.php

public function goodbyeAction() {
    echo 'Goodbye World!';

}

And then visit the URL to test it out:

http://example.com/helloworld/index/goodbye

Because we’re extending the Mage_Core_Controller_Front_Action class, we get some methods for free. For example, additional URL elements are automatically parsed into key/value pairs for us. Add the following method to your Action Controller.

public function paramsAction() {
    echo '<dl>';            

    foreach($this->getRequest()->getParams() as $key=>$value{
        echo '<dt><strong>Param: </strong>'.$key.'</dt>';

        echo '<dt><strong>Value: </strong>'.$value.'</dt>';
    }
    echo '</dl>';

}

and visit the following URL

http://example.com/helloworld/index/params?foo=bar&baz=eof

You should see each parameter and value printed out.

Finally, what would we do if we wanted a URL that responded at

http://example.com/helloworld/messages/goodbye

Here our Action Controller’s name is messages, so we’d create a file at

app/code/local/Magentotutorial/Helloworld/controllers/MessagesController.php

with an Action Controller named Magentotutorial_Helloworld_MessagesController and an Action Method that looked something like

public function goodbyeAction()         
{
    echo 'Another Goodbye';
}

And that, in a nutshell, is how Magento implements the Controller portion of MVC. While it’s a little more complicated than other PHP MVC framework’s, it’s a highly flexible system that will allow you build almost any URL structure you want.

Author Bio: Alan Storm is a technology strategist, software developer and writer living and working in Portland, OR.  He publishes articles on his website and is the creator of the Magento development extension Commerce Bug.

Magento for Developers: Part 2 – The Magento Config


The config is the beating heart of the Magento System. It describes, in whole, almost any module, model, class, template, etc. than you’ll need to access. It’s a level of abstraction that most PHP developers aren’t used to working with, and while it adds development time in the form of confusion and head scratching, it also allows you an unprecedented amount of flexibility as far as overriding default system behaviors go.

To start with, we’re going to create a Magento module that will let us view the system config in our web browser. Follow along by copying and pasting the code below, it’s worth going through on your own as a way to start getting comfortable with things you’ll be doing while working with Magento, as well as learning key terminology.

In this article…

Setting up a Module Directory Structure

We’re going to be creating a Magento module. A module is a group of php and xml files meant to extend the system with new functionality, or override core system behavior. This may meaning adding additional data models to track sales information, changing the behavior of existing classes, or adding entirely new features.

It’s worth noting that most of the base Magento system is built using the same module system you’ll be using. If you look in

app/code/core/Mage

each folder is a separate module built by the Magento team. Together, these modules form the community shopping cart system you’re using. Your modules should be placed in the following folder

app/code/local/Packagename

“Packagename” should be a unique string to Namespace/Package your code. It’s an unofficial convention that this should be the name of your company. The idea is to pick a string that no one else if the world could possibly be using.

app/code/local/Microsoft

We’ll use “Magentotutorial”.

So, to add a module to your Magento system, create the following directory structure

app/code/local/Magentotutorial/Configviewer/Block
app/code/local/Magentotutorial/Configviewer/controllers
app/code/local/Magentotutorial/Configviewer/etc
app/code/local/Magentotutorial/Configviewer/Helper
app/code/local/Magentotutorial/Configviewer/Model
app/code/local/Magentotutorial/Configviewer/sql

You won’t need all these folder for every module, but setting them all up now is a smart idea.

Next, there’s two files you’ll need to create. The first, config.xml, goes in the etc folder you just created.

app/code/local/Magentotutorial/Configviewer/etc/config.xml

The second file should be created at the following location

app/etc/modules/Magentotutorial_Configviewer.xml

The naming convention for this files is Packagename_Modulename.xml.

The config.xml file should contain the following XML. Don’t worry too much about what all this does for now, we’ll get there eventually

<config>
<modules>
<Magentotutorial_Configviewer>
<version>0.1.0</version>

</Magentotutorial_Configviewer>
</modules>
</config>

Finally, Magentotutorial_Configviewer.xml should contain the following xml.

<config>
<modules>
<Magentotutorial_Configviewer>

<active>true</active>
<codePool>local</codePool>
</Magentotutorial_Configviewer>
</modules>
</config>

That’s it. You now have a bare bones module that won’t do anything, but that Magento will be aware of. To make sure you’ve done everything right, do the following:

  1. Clear your Magento cache
  2. In the Magento Admin, go to System->Configuration->Advanced
  3. In the “Disable modules output” panel verify that Magentotutorial_Configviewer shows up

Congratulations, you’ve built your first Magento module!

Creating a Module Config

Of course, this module doesn’t do anything yet. When we’re done, our module will

  1. Check for the existence of a “showConfig” query string variable
  2. If showConfig is present, display our Magento config and halt normal execution
  3. Check for the existence of an additional query string variable, showConfigFormat that will let us specify text or xml output.

First, we’re going to add the following <global> section to our config.xml file.

<config>

<modules>...</modules>
<global>
<events>
<controller_front_init_routers>
<observers>
<Magentotutorial_configviewer_model_observer>
<type>singleton</type>

<class>Magentotutorial_Configviewer_Model_Observer</class>
<method>checkForConfigRequest</method>
</Magentotutorial_configviewer_model_observer>
</observers>
</controller_front_init_routers>

</events>
</global>
</config>

Then, create a file at

Magentotutorial/Configviewer/Model/Observer.php

and place the following code inside

<?php
class Magentotutorial_Configviewer_Model_Observer {

const FLAG_SHOW_CONFIG = 'showConfig';
const FLAG_SHOW_CONFIG_FORMAT = 'showConfigFormat';

private $request;

public function checkForConfigRequest($observer) {
$this->request = $observer->getEvent()->getData('front')->getRequest();
if($this->request->{self::FLAG_SHOW_CONFIG} === 'true'){
$this->setHeader();

$this->outputConfig();
}
}

private function setHeader() {
$format = isset($this->request->{self::FLAG_SHOW_CONFIG_FORMAT}) ?

$this->request->{self::FLAG_SHOW_CONFIG_FORMAT} : 'xml';
switch($format){
case 'text':
header("Content-Type: text/plain");
break;
default:

header("Content-Type: text/xml");
}
}

 private function outputConfig() {
die(Mage::app()->getConfig()->getNode()->asXML());

}
}

That's it. Clear your Magento cache again and then load any Magento URL with a showConfig=true query string

 

http://magento.example.com/?showConfig=true

What am I looking at?

You should be looking at a giant XML file. This describes the state of your Magento system. It lists all modules, models, classes, event listeners or almost anything else you could think of.

For example, consider the config.xml file you created above. If you search the XML file in your browser for the text Configviewer_Model_Observer you'll find your class listed. Every module's config.xml file is parsed by Magento and included in the global config.

Why Do I Care?

Right now this may seem esoteric, but this config is key to understanding Magento. Every module you'll be creating will add to this config, and anytime you need to access a piece of core system functionality, Magento will be referring back to the config to look something up.

A quick example: As an MVC developer, you've likely worked with some kind of helper class, instantiated something like

$helper_sales = new HelperSales();

One of the things Magento has done is abstract away PHP's class declaration. In Magento, the above code looks something like

$helper_sales = Mage::helper('sales');

In plain english, the static helper method will:

  1. Look in the <helpers /> section of the Config.
  2. Within <helpers />, look for a <sales /> section
  3. Within the <sales /> section look for a <class /> section
  4. Instantiate the class found in #3 (Mage_SalesRule_Helper)

While this seems like a lot of work (and it is), the key advantage is by always looking to the config file for class names, we can override core Magento functionality without changing or adding to the core code. This level of meta programming, not usually found in PHP, allows you to cleanly extend only the parts of the system you need to.

Author Bio: Alan Storm is a technology strategist, software developer and writer living and working in Portland, OR.  He publishes articles on his website and is the creator of the Magento development extension Commerce Bug.

 

Magento for Developers: Part 1 – Introduction to Magento


What is Magento? It’s the most powerful online eCommerce platform in the universe and is changing the face of eCommerce forever.

Of course, you already know that. What you may not realize is Magento’s also an object-oriented PHP Framework that can be used to develop modern, dynamic web applications that tap into Magento’s powerful eCommerce features.

This is the first in a series of articles in which wee’re going to go on a whirlwind tour of Magento’s programming framework features. Don’t worry if you don’t follow everything immediately. As you study the system more everything in this article will start to make sense, and you’ll soon be the envy of your colleagues stuck working with more primitive PHP systems.

In this article…

Or for the more visually oriented Magento_MVC.pdf.

Code Organized in Modules

Magento organizes its code into individual Modules. In a typical PHP Model-View-Controller (MVC) application, all the Controllers will be in one folder, all the Models in another, etc. In Magento, files are grouped together based on functionality, which are called modules in Magento.

Magento’s Code

For example, you’ll find Controllers, Models, Helpers, Blocks, etc. related to Magento’s checkout functionality in

app/code/core/Mage/Checkout

You’ll find Controllers, Models, Helpers, Blocks, etc. related to Magento’s Google Checkout functionality in

app/code/core/Mage/GoogleCheckout
Your Code

When you want to customize or extend Magento, rather than editing core files directly, or even placing you new Controllers, Models, Helpers, Blocks, etc. next to Magento code, you’ll create your own Modules in

app/code/local/Package/Modulename

Package (also often referred to as a Namespace) is a unique name that identifies your company or organization. The intent is that each member of the world-wide Magento community will use their own Package name when creating modules in order to avoid colliding with another user’s code.

When you create a new Module, you need to tell Magento about it. This is done by adding an XML file to the folder:

app/etc/modules

There are two kinds of files in this folder, the first enables an individual Module, and is named in the form: Packagename_Modulename.xml

The second is a file that will enable multiple Modules from a Package/Namespace, and is named in the form: Packagename_All.xml

Configuration-Based MVC

Magento is a configuration-based MVC system. The alternative to this would a convention-based MVC system.

In a convention-based MVC system, if you wanted to add, say, a new Controller or maybe a new Model, you’d just create the file/class, and the system would pick it up automatically.

In a configuration-based system, like Magento, in addition to adding the new file/class to the codebase, you often need to explicitly tell the system about the new class, or new group of classes. In Magento, each Module has a file named config.xml. This file contains all the relevant configuration for a Magento Module. At runtime, all these files are loaded into one large configuration tree.

For example, want to use Models in your custom Module? You’ll need to add some code to config.xml that tells Magento you want to use Models, as well as what the base class name for all your Models should be.

<models>
<packagename>
<class>Packagename_Modulename_Model</class>
<packagename>
</models>

The same goes for Helpers, Blocks, Routes for your Controllers, Event Handlers, and more. Almost anytime you want to tap into the power of the Magento system, you’ll need to make some change or addition to your config file.

Controllers

In any PHP system, the main PHP entry point remains a PHP file. Magento is no different, and that file is index.php.

However, you never CODE in index.php. In an MVC system, index.php will contains code/calls to code that does the following:

  1. Examines the URL
  2. Based on some set of rules, turns this URL into a Controller class and an Action method (called Routing)
  3. Instantiates the Controller class and calls the Action method (called dispatching)

This means the practical entry point in Magento (or any MVC-based system) is a method in a Controller file. Consider the following URL:

http://example.com/catalog/category/view/id/25

Each portion of the path after the server name is parsed as follows.

Front Name – catalog

The first portion of the URL is called the front name. This, more or less, tells magento which Module it can find a Controller in. In the above example, the front name is catalog, which corresponds to the Module located at:

app/code/core/Mage/Catalog

Controller Name – category

The second portion of the URL tells Magento which Controller it should use. Each Module with Controllers has a special folder named ‘controllers’ which contains all the Controllers for a module. In the above example, the URL portion category is translated into the Controller file

app/code/core/Mage/Catalog/controllers/CategoryController.php

Which looks like

class Mage_Catalog_CategoryController extends Mage_Core_Controller_Front_Action
{
}

All Controllers in the Magento cart application extend from Mage_Core_Controller_Front_Action.

Action Name – view

Third in our URL is the action name. In our example, this is “view”. The word “view” is used to create the Action Method. So, in our example, “view” would be turned into “viewAction”

class Mage_Catalog_CategoryController extends Mage_Core_Controller_Front_Action
{
public function viewAction()
{
//main entry point

}
}

People familiar with the Zend Framework will recognize the naming convention here.

Paramater/Value – id/25

Any path portions after the action name will be considered key/value GET request variables. So, in our example, the “id/25″ means there will get a GET variable named “id”, with a value of “25″.

As previously mentioned, if you want your Module to use Controllers, you’ll need to configure them. Below is the configuration chunk that enables Controllers for the Catalog Module

<frontend>
<routers>
<catalog>

<use>standard</use>
<args>
<module>Mage_Catalog</module>
<frontName>catalog</frontName>
</args>

</catalog>
</routers>
</frontend>

Don’t worry too much about the specifics right now, but notice the

<frontName>catalog</frontName>

This is what links a Module with a URL frontname. Most Magento core Modules choose a frontname that is the same as their Module name, but this is not required.

Multiple Routers

The routing described above is for the Magento cart application (often called the frontend). If Magento doesn’t find a valid Controller/Action for a URL, it tries again, this time using a second set of Routing rules for the Admin application. If Magento doesn’t find a valid Admin Controller/Action, it uses a special Controller named Mage_Cms_IndexController.

The CMS Controller checks Magento’s content Management system to see if there’s any content that should be loaded. If it finds some, it loads it, otherwise the user will be presented with a 404 page.

For example, the main magento “index” page is one that uses the CMS Controller, which can often throw newcomers for a loop.

Context-Based URI Model Loading

Now that we’re in our Action method entry point, we’ll want to start instantiating classes that do things. Magento offers a special way to instantiate Models, Helpers and Blocks using static factory methods on the global Mage class. For example:

Mage::getModel('catalog/product');
Mage::helper('catalog/product');

The string ‘catalog/product’ is called a Grouped Class Name. It’s also often called a URI. The first portion of any Grouped Class Name (in this case, catalog), is used to lookup which Module the class resides in. The second portion (‘product’ above) is used to determine which class should be loaded.

So, in both of the examples above, ‘catalog’ resolves to the Module app/code/core/Mage/Catalog.

Meaning our class name will start with Mage_Catalog.

Then, product is added to get the final class name

Mage::getModel('catalog/product');
Mage_Catalog_Model_Product

Mage::helper('catalog/product');

Mage_Catalog_Helper_Product

These rules are bound by what's been setup in each Module's config file. When you create your own custom Module, you'll have your own grouped classnames to work with Mage::getModel('myspecialprefix/modelname');.

You don't have to use Groupd Class Names to instantiate your classes. However, as we'll learn later, there are certain advantages to doing so.

 

 

Magento Models

Magento, like most frameworks these days, offers an Object Relational Mapping (ORM) system. ORMs get you out of the business of writing SQL and allow you to manipulate a datastore purely through PHP code. For example:

$model = Mage::getModel('catalog/product')->load(27);
$price = $model->getPrice();

$price += 5;
$model->setPrice($price)->setSku('SK83293432');
$model->save();

In the above example we're calling the methods "getPrice" and "setPrice" on our Product. However, the Mage_Catalog_Model_Product class has no methods with these names. That's because Magento's ORM uses PHP's _get and _set magic methods.

Calling the method $product->getPrice(); will "get" the Model attribute "price".

Calling $product->setPrice(); will "set" the Model attribute "price". All of this assumes the Model class doesn't already have methods named getPrice or setPrice. If it does, the magic methods will be bypassed. If you're interested in the implementation of this, checkout the Varien_Object class, which all Models inherit from.

If you wanted to get all the available data on a Model, call $product->getData(); to get an array of all the attributes.

You'll also notice it's possible to chain together several calls to the set method:

$model->setPrice($price)->setSku('SK83293432');

That's because each set method returns and instance of the Model. This is a pattern you'll see used in much of the Magento codebase.

Magento's ORM also contains a way to query for multiple Objects via a Collections interface. The following would get us a collection of all products that cost $5.00

$products_collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addFieldToFilter('price','5.00');

Again, you'll notice Magento's implemented a chaining interface here. Collections use the PHP Standard Library to implement Objects that have array like properties.

foreach($products_collection as $product)
{
echo $product->getName();
}

You may be wondering what the "addAttributeToSelect" method is for. Magento has two broad types of Model objects. One is a traditional "One Object, One Table" Active Record style Model. When you instantiate these Models, all attributes are automatically selected.

The second type of Model is an Entity Attribute Value (EAV) Model. EAV Models spread data across several different tables in the database. This gives the Magento system the flexibility to offer its flexible product attribute system without having to do a schema change each time you add an attribute. When creating a collection of EAV objects, Magento is conservative in the number of columns it will query for, so you can use addAttributeToSelect to get the columns you want, or addAttributeToSelect('*') to get all columns.

Helpers

Magento's Helper classes contain utility methods that will allow you to perform common tasks on objects and variables. For example:

$helper = Mage::helper('catalog');

You'll notice we've left off the second part of the grouped class name. Each Module has a default Data Helper class. The following is equivalent to the above:

$helper = Mage::helper('catalog/data');

Most Helpers inherit form Mage_Core_Helper_Abstract, which gives you several useful methods by default.

$translated_output = $helper->__('Magento is Great'); //gettext style translations

if($helper->isModuleOutputEnabled()): //is output for this module on or off?

Layouts

So, we've seen Controllers, Models, and Helpers. In a typical PHP MVC system, after we've manipulated our Models we would

  1. Set some variables for our view
  2. The system would load a default "outer" HTML layout>
  3. The system would then load our view inside that outer layout

However, if you look at a typical Magento Controller action, you don't see any of this:

/**
* View product gallery action
*/

public function galleryAction()
{
if (!$this->_initProduct()) {
if (isset($_GET['store']) &amp;&amp; !$this->getResponse()->isRedirect()) {
$this->_redirect('');

} elseif (!$this->getResponse()->isRedirect()) {
$this->_forward('noRoute');
}
return;

}
$this->loadLayout();
$this->renderLayout();
}

Instead, the Controller action ends with two calls

$this->loadLayout();
$this->renderLayout();

So, the "V" in Magento's MVC already differs from what you're probably used to, in that you need to explicitly kick off rendering the layout.

The layout itself also differs. A Magento Layout is an object that contains a nested/tree collection of "Block" objects. Each Block object will render a specific bit of HTML. Block objects do this through a combination of PHP code, and including PHP .phtml template files.

Blocks objects are meant to interact with the Magento system to retrieve data from Models, while the phtml template files will produce the HTML needed for a page.

For example, the page header Block app/code/core/Mage/Page/Block/Html/Head.php uses the head.phtml file page/html/head.phtml.

Another way of thinking about it is the Block classes are almost like little mini-controllers, and the .phtml files are the view.

By default, when you call

$this->loadLayout();
$this->renderLayout();

Magento will load up a Layout with a skeleton site structure. There will be Structure Blocks to give you your html, head, and body, as well as HTML to setup single or multiple columns of Layout. Additionally, there will be a few Content Blocks for the navigation, default welcome message, etc.

"Structure" and "Content" are arbitrary designations in the Layout system. A Block doesn't programmatically know if it's Structure or Content, but it's useful to think of a Block as one or the other.

To add Content to this Layout you need to tell the Magento system something like

"Hey, Magento, add these additional Blocks under the "content" Block of the skeleton"

or

"Hey, Magento, add these additional Blocks under the "left column" Block of the skeleton"

This can be done programmatically in a Controller action

public function indexAction()
{
$block = $this->getLayout()->createBlock('adminhtml/system_account_edit')

$this->getLayout()->getBlock('content')->append($block);
}

but more commonly (at least in the frontend cart application), is use of the XML Layout system.

The Layout XML files in a theme allow you, on a per Controller basis, to remove Blocks that would normally be rendered, or add Blocks to that default skeleton areas. For example, consider this Layout XML file:

<catalog_category_default>
<reference name="left">
<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml"/>

</reference>
</catalog_category_default>

It's saying in the catalog Module, in the category Controller, and the default Action, insert the the catalog/navigation Block into the "left" structure Block, using the catalog/navigation/left.phtml template.

One last important thing about Blocks. You'll often see code in templates that looks like this:

$this->getChildHtml('order_items')

This is how a Block renders a nested Block. However, a Block can only render a child Block if the child Block is included as a nested Block in the Layout XML file. In the example above our catalog/navigation Block has no nested Blocks. This means any call to $this->getChildHtml() in left.phtml will render as blank.

If, however, we had something like:

<catalog_category_default>
<reference name="left">
<block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml">

<block type="core/template" name="foobar" template="foo/baz/bar.phtml"
</block>
</reference>
</catalog_category_default>

From the catalog/navigation Block, we'd be able to call

$this->getChildHtml('foobar');

Observers

Like any good object-oriented system, Magento implements an Event/Observer pattern for end users to hook into. As certain actions happen during a Page request (a Model is saved, a user logs in, etc.), Magento will issue an event signal.

When creating your own Modules, you can "listen" for these events. Say you wanted to get an email every time a certain customer logged into the store. You could listen for the "customer_login" event (setup in config.xml)

<events>
<customer_login>
<observers>

<unique_name>
<type>singleton</type>
<class>mymodule/observer</class>
<method>iSpyWithMyLittleEye</method>

</unique_name>
</observers>
</customer_login>
</events>

and then write some code that would run whenever a user logged in:

<pre><code>class Packagename_Mymodule_Model_Observer
{

public function iSpyWithMyLittleEye($observer)
{
$data = $observer->getData();
//code to check observer data for out user,
//and take some action goes here

}
}

Class Overrides

Finally, the Magento System offers you the ability to replace Model, Helper and Block classes from the core modules with your own. This is a feature that's similar to "Duck Typing" or "Monkey Patching" in a language like Ruby or Python.

Here's an example to help you understand. The Model class for a product is Mage_Catalog_Model_Product.

Whenever the following code is called, a Mage_Catalog_Model_Product object is created

$product = Mage::getModel('catalog/product');

This is a factory pattern.

What Magento's class override system does is allow you to tell the system

"Hey, whenever anyone asks for a catalog/product, instead of giving them a Mage_Catalog_Model_Product,
give them a Packagename_Modulename_Model_Foobazproduct instead".

Then, if you want, your Packagename_Modulename_Model_Foobazproduct class can extend the original product class

class Packagename_Modulename_Model_Foobazproduct extends Mage_Catalog_Model_Product
{
}

Which will allow you to change the behavior of any method on the class, but keep the functionality of the existing methods.

 

 

class Packagename_Modulename_Model_Foobazproduct extends Mage_Catalog_Model_Product
{

public function validate()
{
//add custom validation functionality here
return $this;
}

}

As you might expect, this overriding (or rewriting) is done in the config.xml file.

<models>
<!-- tells the system this module has models -->
<modulename>
<class>Packagename_Modulename_Model</class>

</modulename>

 <!-- does the override for catalog/product-->
<catalog>

<rewrite>
<product>Packagename_Modulename_Model_Foobazproduct</product>
</rewrite>
</catalog>
</models>

One thing that's important to note here. Individual classes in your Module are overriding individual classes in other Modules. You are not, however, overriding the entire Module. This allows you to change specific method behavior without having to worry what the rest of the Module is doing.