I have a custom category attribute that i want to add to the body class. As far as I could find out what people do is
Override the CategoryController and add something like $root->addBodyClass($category->getMyAttribute()); But I don't want to override core classes...
In the admin panel they add something like <reference name=”root”><action method=”addBodyClass”><className>caravan-motorhome-lighting</className></action></reference> to each and every category not using the attribute itself but adding the class directly. As I already have an attribute, I surely don't want do clone it and add the class this way.
So what my favourite solution would be is some layout update I can add to the local.xml that says
<reference name=”root”>
<action method=”addBodyClass”>
<className>
get value of my custom attribute here dynamically
</className>
</action>
</reference>
Does anyone have an idea how this could work or another idea that I didn't even think of?
You can use a really cool feature of Magento layout XML to achieve this. You'll need a module to achieve it. Either create a module specifically for this or use a theme module if you have one — this is up to you to decide what you think is best.
I'll show you an example where I'll add a class containing the ID of the category to the body tag:
In my layout XML, I'm going to add via the catalog_category_default handle. This way, I can use Mage::registry('current_category') later to retrieve the current category. So, in your layout XML do something similar to this:
<catalog_category_default>
<reference name="root">
<action method="addBodyClass">
<className helper="mymodule/my_helper/getCategoryClass" />
</action>
</reference>
</catalog_category_default>
This attribute is the important part: helper="mymodule/my_helper/getCategoryClass". This is equivalent to calling Mage::helper('mymodule/my_helper')->getCategoryClass(); in code.
Whatever is returned from that function will be used as the value for the <className> node. You may want to use a different helper that you deem more appropriate, this is up to you to decide.
Carrying on the with the example, here's the function:
public function getCategoryClass() {
return 'category-id-' . Mage::registry('current_category')->getId();
}
You'll want to change the code so that it retrieves the value of your attribute. e.g getMyAttribute() on the category returned by Mage::registry('current_category').
Also, you'll need to ensure that the return is something that is suitable as a CSS class. In this example we don't need to do anything as the ID will always be just number which will be appended to category-id-. If the value of your attribute is not always going to be safe you might want to consider using something like this
Related
Let us consider i am working with registration form and i want to add new field in it say "Company". here I know all the process of creating module and add field in /customer/form/register.phtml file, but i want to do it in different way.
Here is my query, i want to do this with the help of xml file in which i create my new xml file and give reference to customer_form_register block with new .phtml file (without editing /customer/form/register.phtml) with all back end process of creating module same.
Problem
I now understand your question is asking if you can insert additional form fields on the customer register form without editing the customer/form/register.phtml template. Unfortunately, in Magento’s layout I don’t believe there is currently a way to create a reference to a block inside of another reference except from within the same. So, while there is not a very clean way to do this using only layout XML, there is one way you could accomplish it...
A Possible Solution
In order to insert a new child block into the customer_form_register block, we need to override the layout definition for that block. That is not ideal because you replace any other definitions of that block, so you need to be careful to incorporate any other necessary layout updates into your new one as well. One to consider is that this block gets redefined in captcha.xml, so if you need that functionality you need to add in those updates to your new definition as well.
We will then be inserting a new block, customer.form.register.newsletter. This is because that child block name is already being invoked in the register.phtml template like this $this->getChildHtml('customer.form.register.newsletter'), but it does not seem to be used for anything else that I noticed. So once we have defined our new block with this name, it will be inserted into the page below the existing newsletter checkbox:
<customer_account_create>
<reference name="content">
<block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml">
<block type="page/html_wrapper" name="customer.form.register.fields.before" as="form_fields_before" translate="label">
<label>Form Fields Before</label>
</block>
<!-- This is our new block. -->
<block type="core/template" name="customer.form.register.newsletter" template="customer/form/custom_register.phtml"/>
</block>
</reference>
</customer_account_create>
Alternative
I think you may want to consider something like this extension instead, since it seems to make it easy to add custom registration fields.
There exists
public Mage_Core_Block_Abstract::setParentBlock(Mage_Core_Block_Abstract $block)
Evidently it takes a block object as an argument. I have tried:
<reference name="myBlock">
<action method="setParentBlock">
<block><reference name="newParent"/></block>
</action>
</reference>
But, no luck.
Any ideas?
Is it even possible to someone pass a block object to a method, from within a layout file?
You can't call that method from layout XML files. The layout XML file's <action/> node is mainly (see below) used to pass strings as parameters. That means any method where the type hint explicitly requires an object
Mage_Core_Block_Abstract $block
is not meant to be called from the XML. It's a method used in the implementation of the system, and one the layout XML system is meant to protect you from accidentally calling.
Also, you can't use reference in the way you're trying
<reference name="newParent"/>
The <reference/> tag is only valid as an immediate ancestor of the top level layout handles (any other behavior caused by using reference in another location is an unintended implementation side effect, and I'd be wary of relying on said behavior)
Finally, you may be able to achieve what you want by using the "helper parameter" feature of the layout system.
<action method="setParentBlock">
<block helper="core/data/someMethod" arg1="foo" ...>
</action>
if you can find a helper class
Mage::helper('core/data');
Where someMethod returns the block you're after
Mage::helper('core/data')->someMethod(parameters,from,attributes,above);
but changing a block's parent mid-render is sort of abusing the layout system, and I'd advise against it (unless there's no other way to get at what you want).
You needn't worry about this at all, as the inverse is typically the way associations are built, but if you want to set a parent you can use the parent block attribute:
<block type="..." name="..." parent="parentblockname" ... />
I have a custom info.phtml file which is already working if placed at app\design\frontend\default\default\template\sales\order\info.phtml.
However, I do not want to use this approach since this is part of a module, so if later someone else overrides this file it'll brake the functionality. I've read about using custom blocks but I tried many different approaches without success.
Actually, the path you cite will only work when the theme package is set to default. The intended fallback theme for all files since CE1.4 is base/default. There's quite a lot to consider here, so let's break it down. Of course, being the end implementer of a Magento instance is luxurious because you can use any number of customization options without worrying about how to support other developers' needs. However, when developing a third party module for release to/consumption by others (as you are, it seems), then you have some tough decisions to make if you wish to ensure that output is modified in the way which your module intends/needs. Let's look at the template you mention, which is part of the means by which output is generated. This example contains several of the factors involved in the generation of output.
app/design/frontend/base/default/template/sales/order/info.phtml:
<?php $_order = $this->getOrder() ?>
<?php echo $this->getMessagesBlock()->getGroupedHtml() ?>
<div class="page-title title-buttons">
<h1><?php echo $this->__('Order #%s - %s', $_order->getRealOrderId(), $_order->getStatusLabel()) ?></h1>
<?php echo $this->getChildHtml('buttons') ?>
</div>
Are you in the right place?
The first question to ask is, "What do I need to change, and where?" If the answer lies in the output of a child block (e.g. the output of $this->getChildHtml('buttons')), then customizing the output means specifying an alternate child (with multiple possibilities for customization). If not, then the change is likely local to the template/block environment.
Is the change entity-related?
For third-party devs, the ideal solution is to avoid theme-dependent output whenever possible. For example, if you needed to change or add to the data available directly from one of the constituent objects, it is possible to modify that object or its behavior via Magento's configuration XML using configuration-based rewrites or via the event-observer architecture. In the current example, $this->getOrder() is an instance of Mage_Sales_Model_Order, $this is an instance of Mage_Sales_Block_Order_Info, and it is possible to rewrite either of these to a different class. Also, the sales_order_load_after method can be observed to modify properties of the order object.
Can translations be used?
If there is a need to modify just a string, it is often possible to effect this change via Magento's translation functionality. In a template, any string rendered via the __() method is passed through translation. This is very easy to modify for the end-implementer in a theme-specific translate.csv file. For third-party developers a little configuration XML allows to specify an additional translation CSV file, even for a core module.
Am I stuck with markup?
If there is a need to change the view markup being presented, it's necessary to modify the source of the markup, which most often is a template file. This can be effected in a couple of ways. In the case of the order info block, the sales/order/info.phtml template is defined in the Magento constructor:
class Mage_Sales_Block_Order_Info extends Mage_Core_Block_Template
{
//snip...
protected function _construct()
{
parent::_construct();
$this->setTemplate('sales/order/info.phtml');
}
//snip...
}
This usually means that there is no specified template in layout XML. The next step is to determine if the block is in fact instantiated via layout XML or in the controller. In the case of the latter, there's no way to manipulate the block via layout XML, so you'll need to use one of many possible Magento/PHP options to change the _template property. If the block is instantiated via layout XML, great - it's easy to specify some custom layout XML to point to an alternate template which will not be present in any theme; you just need to know the handle(s) and names which the block has been given, which can be determined by searching for the block instantiation markup (e.g. search *app/design/frontend/* for type="sales/order_info". This will lead you to *.../base/default/layout/sales.xml*:
<sales_order_view translate="label">
<label>Customer My Account Order View</label>
<update handle="customer_account"/>
<reference name="my.account.wrapper">
<block type="sales/order_info" as="info" name="sales.order.info">
<block type="sales/order_info_buttons" as="buttons" name="sales.order.info.buttons" />
</block>
<!-- etc. -->
It's possible to then use the handles and names to update the block's _template property in your module's custom layout update XML file, e.g.:
<sales_order_view>
<reference name="sales.order.info">
<action method="setTemplate"><tpl>my/custom/template.phtml</...>
<!--
Instead of <reference> you can use the 'block' attribute:
<action method="setTemplate" block="sales.order.info"><tpl>my/custom/template.phtml</...>
-->
This would allow you to put your custom template in the base/default template directory, where it belongs. However, you'll notice that this block is instantiated in a number of handle:
You might want to use a utility handle and <update /> directive to encapsulate the instructions into one place and provide it to all of the stock handles.
Nothing's foolproof!
This is one of several approaches, but it's not foolproof. Depending on your extension consumer audience, you might want to scan layout XML and template directories for customizations of/changes from the stock template and provide a notice to the admin.
If you need your custom info.phtml as part of a module, then declare it in module's xml. Then you can place your custom phtml file in app\design\frontend\whatever\somethingelse\template\sales\order and it will override it. For example, see this thread about how to override existing template file.
This is done from layout xml of your module.
Just put the code below in your layout xml with needed changes as per your namespace, module and controller action which call that file:
<checkout_onepage_index>//Use correct Controller action
<reference name="checkout.onepage.payment">//Change reference name as per your need
<action method="setTemplate">
<template>giftcard/checkout/onepage/payment.phtml</template>//Path of phtml file in yourmodule template folder
</action>
</reference>
</checkout_onepage_index>
I currently have this:
<block type="core/text" name="top.address" as="topAddress">
<action method="addText"><text>PO BOX 1124, Rockdale, Sydney, NSW 2216, Australia</text></action>
</block>
But, when I need to update address, I have to do it manually here in the layout file. I want to pull address from store config ( general/store_information/address ) so, I can update everywhere on the site from one location easily.
I guess it can be done directly on the template like this:
<?php echo Mage::getStoreConfig('general/store_information/address') ?>
But I want to try with layout, is it possible?
Thanks.
My answer might be to oudated, but I faced with such problem just now I found an alternative way to solve it:
In layout you can specify core/text block and set its text via helper. You can use any suitable reference.
<reference name="before_body_end">
<block type="core/text" name="some.config">
<action method="addText">
<text helper="module_name/data/getSomeConfig" />
</action>
</block>
</reference>
Declare getSomeConfig function in the helper:
public function getSomeConfig()
{
return Mage::getStoreConfig('your_config_path');
}
In a such way you can even pass some dynamic data into javascript code.
Short answer - no. There is no functionality for it. That's not to say that it couldn't be done. There is an attribute that you can use on an action tag - ifconfig. It looks to see if a system config flag is set, and if it returns true, then it will proceed with the action. You could override or extend Mage/Core/Model/Layout.php to add that functionality.
There are a number of options to this problem, though.
You can use templates, like you mentioned.
If you are wanting to avoid a template, you can create a block that extends Mage_Core_Block_Text and specify the _toHtml method, with the code that you provided.
The best: I would see creating a generic block in a generic module that is used to pull system config requests and output them as text. You could either have it be a custom action/method, or send along an attribute value, which will end up in the data array for the block, which you could then lookup in _toHtml.
I am in a situation where I need to have a different layout for a specific Product Type.
It'll be okay if I could load a different CSS for this Product Type.
Is anything like this possible through code/xml?
I am able to identify the product type in catalog/product/view.phtml, will it be helpful?
I also noticed a node in catalog.xml named <PRODUCT_TYPE_grouped> or <PRODUCT_TYPE_configurable> etc.
will something like this help!?
Your idea is a valid one, something like this should be possible:
<PRODUCT_TYPE_grouped>
<reference name="head">
<action method="addCss"><stylesheet>css/your_file.css</stylesheet></action>
</reference>
</PRODUCT_TYPE_grouped>
Take a look at page.xml to see what other sorts of tricks the same block can perform.
You want to look at catalog.xml not page.xml for product type.
This is if you are using magento 1.7 and up.