16

Can someone please explain this?

Let me tell you what i know. If the first three points are good, please explain the 4 point.

  1. Request come to controller.
  2. In Controller Action we initiate Models.
  3. Models collects or generates all the information needed by connecting to database etc.

What happens after that?

  1. How do models transfer data to Blocks, or do Blocks get data from models?

  2. Templates get the prepared data and show on the screen

    • Also, does the request ever goes back to controller again?

Please explain. I'm confused at several places.

Knase
  • 1,224
  • 14
  • 23
Ricky Sharma
  • 1,839
  • 3
  • 24
  • 44

3 Answers3

39

Nothing transfers data to the blocks. After a controller action has done its model interacting, it's responsible for

  1. Loading a layout object (which, indirectly, loads and creates block objects)

  2. Tell that layout object to render a page.

Most Magento controller actions do this with two calls at the end of a controller action.

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

In Magento, nothing sets data on the view. Instead, the view (that is, the block objects) ask the system for data. You can see an example of this in the Mage_Tag_Block_Customer_View block class.

#File: app/code/core/Mage/Tag/Block/Customer/View.php    
...
public function getTagInfo()
{
    if (is_null($this->_tagInfo)) {
        $this->_tagInfo = Mage::getModel('tag/tag')
            ->load($this->getTagId());
    }
    return $this->_tagInfo;
}    
...

Here, this block's getTagInfo method asks the model directly for its information. This way, the front-end template developer has access to a

$this->getTagInfo();

method. I also have it on good authority that a block's _prepareLayout method is the perfect place to put most, if not all, of your data fetching code in a block.

A second pattern you'll see used is the Magento registry pattern. This is a Magento system that lets you set a system-wide (but not PHP) global variable.

Mage::register('foo', 'some value');
echo Mage::registry('foo');

Sometimes a Magento developer will use the registry to set a variable up in a controller action, and then grab is back out in the blocks. For example, in the admin console's invoice controller.

#File: app/code/core/Mage/Adminhtml/controllers/Sales/Order/InvoiceController.php
protected function _initInvoice()
{
    ...
    $invoice = Mage::register('current_invoice', $invoice);
    return $invoice;
}    

and then a Block will reference it later.

#File: app/code/core/Mage/Sales/Block/Order/Print/Invoice.php
public function getInvoice()
{
    return Mage::registry('current_invoice');
}

I'm not wild about the registry pattern, but it's used by the core team, so it's probably kosher.

Finally, if you're looking to emulate the "dumb view" pattern used in most PHP MVC frameworks, try something like this

$this->loadLayout();
$block = $this->getLayout()->getBlock('block_name');
$block->setSomeData('My Data');
$block->setData('alternate_syntax', 'Some other data');
$this->renderLayout();

and then in the block and/or template file.

echo $this->getSomeData();
echo $this->getData('some_data');

echo $this->getAlternateSyntax();
echo $this->getData('alternate_syntax');

After you call loadLayout, Magento will have created all the block objects. What you're doing above is getting reference to a specific block object, and then setting its data.

Per Vinai's comments below, there's also a block's assign method to consider.

Similar to setData, after calling loadLayout (or from a block's _prepareLayout) method, you can do something like

$this->loadLayout();
$block = $this->getLayout()->getBlock('block_name');
$block->assign('my_view_var','Something for the view');
$this->renderLayout();

and then in your block's phtml file, you'd be able to output that view variable

echo $my_view_var;
Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • 1
    Alan, we should start coordinating. Your response is the best because it answers the OPs implicit question, which is, "Magento, Y U NO RENDER LIKE MOST PHP MVC?", something we've all asked... :-D – benmarks Mar 19 '12 at 02:48
  • 1
    Thanks Alan, you summed it up nicely! The only thing I can think of to add is the `assign()` method of the template block. Rarely used, but some people prefere it to `setData()` because it matches the classical "assign template variables" approach. – Vinai Mar 19 '12 at 07:53
  • Thanks Alan. Very nice explanation, cleared most of my doubts. – Ricky Sharma Mar 20 '12 at 00:01
  • Alan:I am so much grateful to you. when ever i have a doubt googling will take me to your answer. :) – zamil Mar 01 '14 at 13:01
3
  1. Correct, via the Front Controller and routers
  2. Not quite. Magento's ViewModel implementation is facilitated in part by having the views (Blocks) instantiate their own models.
  3. Yes, through resource models.

When blocks are rendered via the typical $this->loadLayout()->renderLayout() flow in the action controller, if they use templates, those templates are include()d at render time.

After a renderLayout() call, execution is still in the scope of the controller action we've dispatched to, so you can access the rendered response by getting the request object.

Key plot points:

  1. index.php calls Mage::run()
  2. Mage::run calls Mage_Core_Model_App::run()
  3. App::run() calls Mage_Core_Controller_Varien_Front, first its init() method which gathers and sets up routers, then dispatch() which does the following:

    a. Database URL rewrites

    b. Configuration rewrites (deprecated)

    c. Match the correct controller action via the router. Execution jumps from Front Controller to action controller. Invoking blocks using layout or manually will then pass execution to block classes and models and templates, and then we (normally) return to the controller action.

    d. Sending the response object (with the assumption that it's been altered by an action controller).

If you look at Mage_Core_Controller_Varien_Front::dispatch(); you'll see the call to$this->getResponse()->sendResponse();` which will flush output, preceded by an event (controller_front_send_response_before) which can be used as a hook to add or manipulate anything response-related.

benmarks
  • 23,384
  • 1
  • 62
  • 84
1

Nope, it leaves the controller (the one controlling the request) and then moves to the views, where it is rendered. Once the view (block[s]) has rendered the request is essentially over (save for mostly url helpers, which have no logic to speak of but are sometimes processed in the controller after the view is rendered.) unless you have some sort of hook triggered afterwards.

I use this flowchart and this series (Alan Storm is the guy) to learn about Magento request routing.

bkconrad
  • 2,620
  • 3
  • 20
  • 30
  • 1
    Technically, a typical request to rendering workflow uses `renderLayout()`, after which execution continues in the controller action. It's just not a common case that the response object would be altered in the controller action after `renderLayout()`, hence the next line is usually the end of the method and therefore a return to the front controller. /hairsplit – benmarks Mar 19 '12 at 01:20