I've got a bestsellers module which I've written and it works great, however I want to be able to change the collection size it returns via the XML, rather than the php/phtml.
Something like this:
<block type="catalog/product_list" name="bestsellers" limit="3"
template="custom/bestsellers.phtml" />
or something like:
<block type="catalog/product_list" name="bestsellers"
template="custom/bestsellers.phtml">
<action method="setLimit">3</action>
</block>
Is this possible?
I'm currently changing the limit via the phtml with:
->setPageSize(3)
->setCurPage(1);
But that is hard coded and nasty, I need to be able to use my phtml file as template for many cases of the bestsellers module being called from anywhere with the XML + limit in the XML.
Thanks in advance if anyone can shed light on this!
The block Mage_Catalog_Block_Product_List inherits from the Varien_Object class which contains the methods getData() and setData(), as well as the magic methods get*() and set*(). These methods allow us to store (you guessed it) keyed-data within an object.
The <action /> tags in the XML allows us to perform method calls on the block instances. You're nearly there with your second example, but the syntax is:
<block type="catalog/product_list" name="bestsellers">
<action method="setLimit"><value>3</value></action>
</block>
Which is equivalent to:
<block type="catalog/product_list" name="bestsellers">
<action method="setData"><key>limit</key><value>3</value></action>
</block>
Which is roughly equivalent to:
$block = new Mage_Catalog_Block_Product_List();
$block->setLimit(3);
With the data set in the object we can now access through the getData() or get*() methods by calling $this->getLimit() or $this->getData('limit') making our block code:
->setPageSize($this->getLimit())
->setCurPage(1);
You should probably perform a check for the existence of the limit data first and provide a default value if none is provided in the XML.
Note: The name of the children in the <action /> tag don't matter. It's the order of the arguments that's important. We could just as well have called <action method="setLimit"><foo>3</foo></action> and it still would have worked.
Related
I've just started reading up on Magentos (1.9 CE) layout and how it works with the XML and PHTML files.
I've come across the structural blocks and content blocks.
I'm looking at the page.xml file of Magento 1.9 installed RWD package DEFAULT theme.
I've pasted below what i believe is the header, content, and footer from the page.xml file.
My questions
1) Is a block considered to be a content block the moment it is assigned a "template="XXXX.phtml" attribute? EAnd if not, it is known as a structural block?
2) For structural blocks that don't have the template="XXX", how does it ultimately link with the phtml files? My question comes from the context of looking at the header block as seen below, some of its sub blocks have "template" attribute, but none of them seem to point to the header.phtml from "\template\page\html" directory, which i have been editing to customize the look of the Magento site Welcome message.
<block type="page/html_header" name="header" as="header">
<block type="page/template_links" name="top.links" as="topLinks"/>
<block type="page/switch" name="store_language" as="store_language" template="page/switch/languages.phtml"/>
<block type="core/text_list" name="top.menu" as="topMenu" translate="label">
<label>Navigation Bar</label>
<block type="page/html_topmenu" name="catalog.topnav" template="page/html/topmenu.phtml">
<block type="page/html_topmenu_renderer" name="catalog.topnav.renderer" template="page/html/topmenu/renderer.phtml"/>
</block>
</block>
<block type="page/html_wrapper" name="top.container" as="topContainer" translate="label">
<label>Page Header</label>
<action method="setElementClass"><value>top-container</value></action>
</block>
<block type="page/html_welcome" name="welcome" as="welcome"/>
</block>
<block type="core/text_list" name="content" as="content" translate="label">
<label>Main Content Area</label>
</block>
<block type="page/html_footer" name="footer" as="footer" template="page/html/footer.phtml">
<block type="page/html_wrapper" name="bottom.container" as="bottomContainer" translate="label">
<label>Page Footer</label>
<action method="setElementClass"><value>bottom-container</value></action>
</block>
<block type="page/switch" name="store_switcher" as="store_switcher" after="*" template="page/switch/stores.phtml"/>
<block type="page/template_links" name="footer_links" as="footer_links" template="page/template/links.phtml">
<action method="setTitle"><title>Quick Links</title></action>
</block>
<block type="page/template_links" name="footer_links2" as="footer_links2" template="page/template/links.phtml">
<action method="setTitle"><title>Account</title></action>
</block>
<!-- This static block can be created and populated in admin. The footer_links cms block can be used as a starting point. -->
<!--<block type="cms/block" name="footer_social_links">
<action method="setBlockId"><block_id>footer_social_links</block_id></action>
</block>-->
</block>
There are mainly two types of blocks in Magento
Structural blocks :- These blocks actually defines the structure of a page block. This is where content blocks resides
Example : Header, Left, Right, Footer and Main blocks (defines in page.xml)
Content blocks :- These blocks are actually holding the content. Depends upon the type of block, the content hold by these block varies
Example :- Any custom blocks, core/template block, cms/page block etc.
Normally every content block should come under any of the strucural block that described above. These content block holds different contents depend upon its type. For example, a cms/page block is intend to hold cms page content that we set through admin section. catalog/product_view block is used to hold a product view contents. As you already noted these two content blocks use to hold contents but the content differes from block to block, depending on its type specified. With that said let us look on your problem
1) Structural blocks holds structures of a page. Content block comes under each structual block. So in the above layout code, the block with type page/html_header is a structural block. While all other blocks that come inside this block are content blocks of above structural block. In other words they are children of the header structural block. Now let us look on this header block at backside.
#File : app/code/core/Mage/Page/Block/Html/Header.php
<?php
class Mage_Page_Block_Html_Header extends Mage_Core_Block_Template
{
public function _construct()
{
$this->setTemplate('page/html/header.phtml');
}
......
}
There it is. Our structural block is actually assigned with a template through backend. This means the template that correspond to our header block resides in app/design/frontend/<your_package>/<your_theme>/template/page/html/header.phtml. If you open that file, you can see that the template is actually defining the header portion of our page using html, css and js and invoking its child blocks using a method getChildHtml(). Some portion of above file is shown below.
.....
<div class="quick-access">
<?php echo $this->getChildHtml('topSearch') ?>
<p class="welcome-msg"><?php echo $this->getChildHtml('welcome') ?> <?php echo $this->getAdditionalHtml() ?></p>
<?php echo $this->getChildHtml('topLinks') ?>
<?php echo $this->getChildHtml('store_language') ?>
.....
</div>
.....
As you can see above, it invokes child blocks(in other words content blocks) that are defined in page.xml layout file, using getChildHtml() method.
This means, if you add a custom child block inside the header structural block and didn't invoke it in header.phtml using getChildHtml() method, your content block is not going to show in frontend.
You can also set header.phtml to the block header through page.xml like this
<block type="page/html_header" name="header" as="header" template="page/html/header.phtml" />
There is no issue with this. So in short, we cannot generally say that a block that defines a phtml file is either a content block or a strutural block. How both of these block defferes purely depends on what those block holds.
Short Note : A cotent block can contain other content blocks. What we are doing is most of the time is this. Means adding our custom content block to another content block that already existing in magento.
2) If you look on different blocks in magento, you can see that, irrespective of structural block/content block, a block can set with a template through layout or through backend. We can set template to a block using an observer also. I already stated how header structural block set with the template header.phtml
. In this case it is through backend side.
Hopes that helps you to understand the concept.
EDIT
No you are absoulutely wrong. In magento layout holds the entire structure of a page. It holds all the blocks that need to render for a particular page.
Suppose you have a url www.mydomain.com/index.php/customer/account. What magento does now is it will split the url and find which module generates such url. So here above url split like this
base url => www.mydomain.com/index.php/
frontend name => customer/
action controller name => account/
method => index/
Now magento will query for which module that is responsible for frontend name customer. It is defined by Mage_Customer core module. Now magento look for which controller that handles this url. In our url, the part that mention this is account. So it will look for AccountController.php file in Mage_Customer module. Now again magento look for whcih method that handles the url. But in our url there is no method specified. So it will assume the method index hence. Now look on that method.
#File:app/code/core/Mage/Customer/controllers/AccountController.php
/**
* Default customer account page
*/
public function indexAction()
{
$this->loadLayout();
// some other codes
$this->renderLayout();
}
This is the important section. First the method calls a function loadLayout(). This simple code does a lot of work behind the curton. It will generate a big layout tree that is correspond to the page that is going to show in frontend. Where is this layouts are defined ? Of course.. It is in layout XML files. Layout xml file defines what are the blocks that should render for this above url and what are not. For an example in this context, follwing handles will be processed by magento.
default
STORE_default
THEME_frontend_default_default
customer_account_index
customer_logged_in
customer_account
Magento will generate a huge layout tree that will consist of all blocks that is specified under these layout handles. Layout handles may come in any layout XML file that comes under the directory app/design/frontend/<your_package>/<your_theme>/layout. After creating this layout tree, magento will render these layout tree using the code renderLayout(). Basically what it will do is convert these layout into html and render it (For this it uses template files and skin).
Now what would you think ? Layout XMLs are that simple? :)
I have found lot information about Magento view generation system and this info looks clear but for some reason I have situation where magento patters not works for me. So my company bought module for checkout page and after some time they decided customize it(add some functionality). If we look to magento standarts we see that we never change source code cuz we have lot mechanism for extend it what actually we tried and found some problem. Have I have peace of layout xml which I would like append my custom block:
<block type="ecommerceteam_echeckout/onepage_billing" name="checkout.onepage.address.billing" as="billing_address" template="ecommerceteam/echeckout/billing.phtml">
<block type="ecommerceteam_echeckout/onepage_billing" name="checkout.onepage.address.billing.form" as="address" template="ecommerceteam/echeckout/billing/address.phtml" />
</block>
And inside billing_address block after address block I would like append my block by follows code:
<layout>
<checkout_cart_index>
<reference name="checkout.onepage">
<block type="kukimi_deliverydate/delivery_date" name="delivery_date" after="address" template="kukimi/deliverydate/delivery/date.phtml" />
</reference>
</checkout_cart_index></layout>
And this is doesn't works but if I change reference to the content it will work what reason for this? how I can manage situations like this?
I am trying to change checkout/cart.phtml through layout update in my module's layout file i.e. mymodule.xml
<layout>
<checkout_cart_index>
<reference name="checkout.cart">
<action method="setCartTemplate"><value>mymodule/checkout/cart.phtml</value></action>
</reference>
</checkout_cart_index>
</layout>
But It is not working. Any clues?
Ankita, What I'm about to write is the actual way to get what you want. While the official answer by John Hickling will work, it is not how Magento intended the main cart template to be modified.
Magento deliberately chose to use different methods for setting the cart templates, namely, setCartTemplate and setEmptyTemplate. They can be seen in Magento's own app/design/frontend/base/default/layout/checkout.xml. This was done so that two templates can be managed, each to handle their own condition. The first condition is for a cart with items, while the second condition is for a cart without items. By using the common setTemplate method, that distinction will be lost: a cart with items and a cart without items will both display the same template. This is no good.
You were so close. You were correct in trying to use the setCartTemplate method. That is what you should be using. However, you were missing one essential method call that would allow Magento to even consider using it: you forgot to include the chooseTemplate method call. Note Magento's own checkout.xml file:
<block type="checkout/cart" name="checkout.cart">
<action method="setCartTemplate"><value>checkout/cart.phtml</value></action>
<action method="setEmptyTemplate"><value>checkout/cart/noItems.phtml</value></action>
<action method="chooseTemplate"/>
Look at that last method call, chooseTemplate. If you look in app/code/core/Mage/Checkout/Block/Cart.php you will see the following method, within which those familiar setCartTemplate and setEmptyTemplate methods are called, but because they are magic methods, they are not easily searchable in Magento's source, which is problematic for a lot of people:
public function chooseTemplate()
{
$itemsCount = $this->getItemsCount() ? $this->getItemsCount() : $this->getQuote()->getItemsCount();
if ($itemsCount) {
$this->setTemplate($this->getCartTemplate());
} else {
$this->setTemplate($this->getEmptyTemplate());
}
}
You were missing that chooseTemplate method call. This is what your own layout XML file should look like:
<checkout_cart_index>
<reference name="checkout.cart">
<action method="setCartTemplate"><value>mymodule/checkout/cart.phtml</value></action>
<action method="setEmptyTemplate"><value>mymodule/checkout/noItems.phtml</value></action>
<action method="chooseTemplate"/>
</reference>
</checkout_cart_index>
I recommend you update your code if it is still under your control. This is how Magento intended the cart templates to be updated. The common setTemplate method is too destructive for this task. Granularity was Magento's intention, so updates should maintain that granularity. I also recommend you mark this as the correct answer.
The method is setTemplate not setCartTemplate, like so:
<layout>
<checkout_cart_index>
<reference name="checkout.cart">
<action method="setTemplate"><value>mymodule/checkout/cart.phtml</value></action>
</reference>
</checkout_cart_index>
</layout>
Take my example :
<block type="core/template" name="my.name" as="myName" template="path/to/template.phtml"/>
What purpose does the "as=myName" declaration serve.
What abstract class defines these default attributes for layout handling?
here i can give brief understanding regarding magento block xml tag
name = Name of the block. It should be unique in the page.
**as = Alias **. Smaller form of name. It should be unique in it's parent block.
template = The template file (View) this block is attached to.
You can call methods from block type inside this by using $this.. e.g. $this->getName()
name vs. as example:
<reference name="left">
<block type="block/blocktype1" name="first_block" template="template1.phtml">
<block type="abc/abc" name="ty1" as="common" template="abc.phtml"/>
</block>
<block type="block/blocktype1" name="second_block" template="template2.phtml">
<block type="xyz/xyz" name="ty2" as="common" template="xyz.phtml"/>
</block>
</reference>
So, you can now call block name ty1 from first_block AND ty2 from second_block as $this->getChildHtml('common');, but see both the blocks called will be different as per their calling parent.
for detail class go throw this
Understanding Magento Block and Block Type
hope this will sure help you.
When you use as, you can call $this->getChildHtml("as_value") on the phtml template.
The name must be unique, and can be used for < reference > blocks, < remove >, etc.
For example (catalog.xml):
<block type="catalog/product_view" name="product.info" template="catalog/product/view.phtml">
<block type="catalog/product_view_media" name="product.info.media" as="media" template="catalog/product/view/media.phtml"/>
I am working on a custom module which takes product images from external server. I have overridden a few functions to get the required result.
But I have to override base/default/template/catalog/product/view/media.phtml inside my custom module.
I tried to do this from default/layout/MY_MODULE.xml but that did not work for me.
How do I override the media.phtml inside default/MY_MODULE.
I think calling setTemplate method is not the right way to override template file in your case. For me it looks a bit redundant and unclear: you are assigning template to block and the immediately reassign it with action. I would do this way:
<catalog_product_view>
<reference name="product.info">
<block type="catalog/product_view_media" name="product.info.media" as="media" template="MY-MODULE/catalog/product/view/media.phtml" />
</reference>
</catalog_product_view>
I think you could also reference to product.info.media directly and call setTemplate action there