Set position of block programmatically in Magento - php

I want to set the position of a block programmatically in Magento.
For example I wish that I can set the position of a block as:
Below 'content' of product view page
In the side bar (left/right)
Before/after any other block.
Please suggest the way to do it.

I have found a solution for this. You can set the block position dynamically using the observers.
First of all create the Observer.php in <Namespace>/<Module>/Model directory. Write following code in this file:
class <Namespace>_<Module>_Model_Observer
{
public function set_block($observer)
{
$action = $observer->getEvent()->getAction();
$fullActionName = $action->getFullActionName();
$position = 'right';
$sub_position = 'before="cart_sidebar"';
$myXml = '<reference name="'.$position.'">';
$myXml .= '<block type="obc/obc" name="obc" template="obc/obc.phtml" '.$sub_position.' />';
$myXml .= '</reference>';
$layout = $observer->getEvent()->getLayout();
if ($fullActionName=='catalog_product_view') { //set any action name here
$layout->getUpdate()->addUpdate($myXml);
$layout->generateXml();
}
}
}
Now in config.xml write following lines to call the observer:
<events>
<controller_action_layout_generate_blocks_before>
<observers>
<module_block_observer>
<type>singleton</type>
<class><Namespace>_<Module>_Model_Observer</class>
<method>set_block</method>
</module_block_observer>
</observers>
</controller_action_layout_generate_blocks_before>
</events>
Now you can set the position of your block anywhere on the page using observer.

Related

Magento class not found

I have written a payment plugin in magento and it works (almost) flawlessly. Frontend payments are getting through I thought everything is done. But now I stumbled upon an issue.
I am using own autoloader and the registering event is controller_front_init_before which works fine. When I create a script calling Mage::app(); and then when I try to edit the orders I get from Mage::getModel("sales/order")->getCollection(); I get an error that my classes aren't loaded. I tried changing the event even to resource_get_tablename but the error persists. The only way to fix this error was to register the same autoloader I use in my plugin in the small script and this is an issue for me. Script follows
require_once "app/Mage.php";
Mage::app();
$orders = Mage::getModel("sales/order")->getCollection();
foreach ($orders as $order) {
echo "ORDER id " . $order->getId() . " #" . $order->getIncrementId()."\n";
$order->setData('status',Mage_Sales_Model_Order::STATE_COMPLETE);
$order->save();
}
Is anybody here who knows maybe which event to use so that this script works without needing to use spl_autoload_register... method?
Thanks!
EDIT
code/communinty/Payment/Module/config.xml
<config>
<global>
<events>
<resource_get_tablename>
<observers>
<payment_module>
<class>Payment_Module_Model_Autoloader</class>
<method>addAutoloader</method>
<type>singleton</type>
</payment_module>
</observers>
</resource_get_tablename>
</events>
</global>
</config>
code/community/Payment/Module/Model/Autoloader.php
class Payment_Module_Model_Autoloader extends Mage_Core_Model_Observer
{
protected static $registered = false;
public function addAutoloader(Varien_Event_Observer $observer)
{
if (self::$registered) {
return;
}
spl_autoload_register(array($this, 'autoload'), false, true);
self::$registered = true;
}
public function autoload($class)
{
$classFile = COMPILER_INCLUDE_PATH . DIRECTORY_SEPARATOR . $class . '.php';
include $classFile;
}
}

Make routing to my custom module in magento

I have made a custom module with layout in magento. its shows customer's information of a particular customer group.
its url is
www.mysite.com/profile/index/index/mayank
Where profile is my modulename, index is controller name, 2nd index is index controller's method and mayank is customer's username (a unique field or attribute).
What i need is.
Make this url like this:-
www.mysite.com/mayank
Firstly, Magento URLs tend to put the field name before the value. So I expect the long URL to actually look like this:
www.mysite.com/profile/index/index/name/mayank
This would allow for a neat retrieval in the controller,
public function indexAction() {
$name = $this->getRequest()->get('name');
}
“Is there anyway we can do like 'if no route found then magento calls our module's controller..?”
I've wrestled with routers before and in my opinion they are unnecessary unless absolutely necessary. When the number of possible URLs is finite then it is easier to save a rewrite in advance. The best time to do this is whenever a username changes.
To listen for a change add this to your module's config.xml:
<global>
<events>
<customer_save_after>
<observers>
<mayank_profile>
<type>singleton</type>
<class>mayank_profile/observer</class>
<method>customerSaveAfter</method>
</mayank_profile>
</observers>
</customer_save_after>
<customer_delete_after>
<observers>
<mayank_profile>
<type>singleton</type>
<class>mayank_profile/observer</class>
<method>customerDeleteAfter</method>
</mayank_profile>
</observers>
</customer_delete_after>
</events>
</global>
In your module's Model folder create Observer.php:
class Mayank_Profile_Model_Observer {
public function customerSaveAfter(Varien_Event_Observer $observer) {
$customer = $observer->getCustomer();
// username is not a standard attribute, hopefully you have created it
if ($customer->dataHasChangedFor('username')) {
$this->remove($customer->getOrigData('username'));
$this->add($customer->getData('username'));
}
}
public function customerDeleteAfter(Varien_Event_Observer $observer) {
$customer = $observer->getCustomer();
$this->remove($customer->getOrigData('username'));
}
protected function remove($username) {
// this does nothing if rewrite doesn't exist
Mage::getModel('core/url_rewrite')
->loadByIdPath('profile/'.$username);
->delete();
}
protected function add($username) {
if ($this->isValidUsername($username)) {
$storeId = Mage::app()->getDefaultStoreView()->getId();
Mage::getModel('core/url_rewrite')
->setIsSystem(0)
->setStoreId($storeId)
// IdPath is used by remove() function
->setIdPath('profile/'.$username)
// TargetPath is your controller/action
->setTargetPath('profile/index/index/name/'.$username)
// RequestPath is what the browser sees
->setRequestPath($username)
->save();
}
}
protected function isValidUsername($username) {
// feel free to add more checks here
// start with existing rewrites
$rewrite = Mage::getModel('core/url_rewrite')
->loadByRequestPath($username);
if (!$rewrite->isObjectNew()) {
return false;
}
// scan known frontNames
$frontNames = Mage::getConfig()->getXPath('*/routers/*/args/frontName');
foreach ($frontNames as $name) {
if ($username == (string) $name) {
return false;
}
}
return true;
}
}
It's quite a bit of work but the advantage here is the following code correctly looks up the path earlier saved as "RequestPath":
echo Mage::getUrl('profile', array(
'name' => $username,
'_use_rewrite' => true
));
Here is a much simpler way which only works for URLs that cannot clash with other modules. Add this to your config.xml:
<global>
<rewrite>
<mayank_profile>
<from><![CDATA[#^/profile/#]]></from>
<to><![CDATA[/profile/index/index/name/]]></to>
<complete>1</complete>
</mayank_profile>
</rewrite>
</global>
When outputting an URL you need to concatenate the path yourself. e.g.
echo Mage::getUrl('', array(
// Use _direct here to avoid a trailing solidus
'_direct' => 'profile/' . $username
));
You can try to solve this with a htaccess rewriterule
Or
create an observer on controller_action_layout_load_before and fetch the REQUEST_URI and use it for your case.
Good luck

Magento - custom module structure

Heyo, been working on a magento theme for a little while now (... and my understanding of the system is slowly but steadily growing). I need to start working on some custom modules soon, and so I'm starting with this little project to get my bearings:
I found a little bit of code to manage a specific task that I have been copying and pasting into template files:
<?php
$ids = $_product->getCategoryIds();
$cat = Mage::getModel('catalog/category')->load($ids[0]);
$isFF = false;
foreach ($ids as $key=>$val) {
$cat = Mage::getModel('catalog/category')->load($val);
$name = $cat->getName();
if ( $name === 'Fast Fulfillment' ) {
$isFF = true;
}
}
if ($isFF) { echo '<span class="ff-logo"></span>'; }
?>
Very simple. I'm just checking to see if a product is in a specific category, and producing an element if it is. (I know there are about 5 solid ways of doing this... this is the one I went with).
I need to test this every time a product block is displayed, and have, up till now, been replicating this code to make it work. It does work, but is backasswards (I shouldn't be putting logic into the view layer).
Ok - so, lets make a simple module to share the functionality:
local/WACI/ProductFlag/etc/config.xml
<config>
<modules>
<WACI_ProductFlag>
<version>0.1.0</version>
</WACI_ProductFlag>
</modules>
<global>
<blocks>
<WACI_ProductFlag>
<class>WACI_ProductFlag_Block_View</class>
</WACI_ProductFlag>
</blocks>
</global>
</config>
etc/modules/WACI_All.xml
<config>
<modules>
<WACI_CustomPageLayouts>
<codePool>local</codePool>
<active>true</active>
</WACI_CustomPageLayouts>
</modules>
<modules>
<WACI_ProductFlag>
<codePool>local</codePool>
<active>true</active>
</WACI_ProductFlag>
</modules>
</config>
Now, for the Class ... I'm not really sure if a Block or a Helper is appropriate. I went with Block, but - idk... I'm probably wrong (tutorials for this stuff vary wildly).
local/WACI/ProductFlag/Block/View.php
<?php
/**
* WACI
*
* #codepool Local
* #category View
* #package WACI
* #module ProductFlag
*/
class WACI_ProductFlag_Block_View extends Mage_Core_Block_Template
{
private $_focus;
private $_isFF = false;
public function getIsFF( $product ){
$this->_focus = "FF";
$isFF = false;
$ids = $product->getCategoryIds();
$cat = Mage::getModel('catalog/category')->load($ids[0]);
foreach ($ids as $key=>$val) {
$cat = Mage::getModel('catalog/category')->load($val);
$name = $cat->getName();
if ( $name === 'Fast Fulfillment' ) {
$isFF = true;
}
}
}
protected function _toHtml(){
$html = parent::_toHtml();
if ($this->_focus === "FF") {
if ($this->_isFF){
$html .= '<span class="ff-logo"></span>';
}
}
return $html;
}
}
?>
Hopefully its clear that I only want to output a string based on the input of any given product. Should I be overriding the _toHtml() to deliver the string? Again. Probably not...
in my local.xml I reference the block:
<catalog_product_view>
<reference name="content">
<reference name="product.info">
<block type="WACI/ProductFlag" name="product.flag" as="productFlag" output="toHtml" />...
... I'm not clear if this instantiates this class? I don't think so. I'm not really sure how to address it in my product/view/media.phtml anyway, given that I need to call a method with a parameter.
I don't think I need a template file, given that I'm just outputting a string, but I don't think I've seen block modules without an associated template. Maybe a helper class is appropriate then?
Whatever the case, it ain't workin.
I either get a fatal error saying my media class has no getIsFF() method (not surprising), or just nothing shows up. My config files are correct, but that's about it, I think.
Jebus. I'm all turned around.
Can someone with Mage skillz clarify this simple issue and point me in the right direction?
Cheers!
No advice? Well = I sussed out this slightly modified and working solution:
local/WACI/ProductFlag/etc/config.xml
<config>
<modules>
<WACI_ProductFlag>
<version>0.1.0</version>
</WACI_ProductFlag>
</modules>
<global>
<blocks>
<productflag>
<class>WACI_ProductFlag_Block</class>
</productflag>
</blocks>
<helpers>
<productflag>
<class>WACI_ProductFlag_Helper</class>
</productflag>
</helpers>
</global>
</config>
local/WACI/ProductFlag/Helper/Flag.php
class WACI_ProductFlag_Helper_Flag extends Mage_Core_Helper_Abstract
{
private $_isFF = false;
public function getIsFF( $product ){
$html = '';
$ids = $product->getCategoryIds();
$cat = Mage::getModel('catalog/category')->load($ids[0]);
foreach ($ids as $key=>$val) {
$cat = Mage::getModel('catalog/category')->load($val);
$name = $cat->getName();
if ( $name === 'Fast Fulfillment' ) {
$this->_isFF = true;
}
}
if($this->_isFF) {
$html = '<span class="ff-logo"></span>';
}
return $html;
}
}
and call it up in any template file via the simple:
<?php echo $this->helper('productflag/flag')->getIsFF($_product); ?>
I'm still not sure if this is exactly appropriate to the magento way of doing things - ie the model calls, I think, should be relegated to their own class and dropped in a Model folder.
Whatever the case - and for anyone else trying to figure this stuff out - as I monkeyed around with it I slowly realized the intent of the config.xml file is to add available factory classes to the blocks/helpers/model pools - and that the path is to the containing directory. The helper call in the template file, then, identifies the "short name" of the directory and then the actual class name.
ie - You could have multiple classes of helpers:
<?php echo $this->helper('productflag/class_one')->someMethod($_product); ?>
<?php echo $this->helper('productflag/class_two')->someOtherMethod($_product); ?>
<?php echo $this->helper('productflag/class_three')->yetAnotherMethod($_product); ?>
So... One step closer.

Event observer not firing from custom module when coupon is applied on checkout page

I want to do something after a coupon is applied during the checkout process. I used the 'catalogrule_after_apply' event along with the corresponding function in Observer.php. The coupon is still being applied without hitting my observer method. I have other events firing, so invalid xml wouldn't be the case. The issue might be the event i am using, but i cant find any other event that would yield the same effect. Suggestions?
etc/config.xml
<config>
<events>
<catalogrule_after_apply>
<observers>
<promosource>
<type>singleton</type>
<class>promosource/observer</class>
<method>addSourceIdToSessionFromCoupon</method>
</promosource>
</observers>
</catalogrule_after_apply>
</events>
</config>
Helper/Observer.php
<?php
class Russ_Promosource_Model_Observer
{
public function __construct()
{
}
public function addSourceIdToSessionFromCoupon($observer) {
die ('event observer worked');
}
}
Thank you in advance.
the event you're observing is triggered when you click on the "Apply Rule", in the admin.
For what you wanna do, I'd use the salesrule_validator_process event. You're observer could then access the applied rule:
public function couponApplied($observer)
{
$rule = $observer->getEvent()->getRule();
$couponCode = $rule->getCouponCode();
$ruleId = $rule->getRuleId();
}
HTH

magento sales_order_place_after observer

I'm trying to write an observer that will export order data when an order is placed. I haven't written any modules before. Basing my implementation on this article: http://www.magentocommerce.com/wiki/5_-_modules_and_development/0_-_module_development_in_magento/customizing_magento_using_event-observer_method
so far I'm just trying to trigger some dummy code to write to a file. I'm not getting anything showing in my log, and the file's not being modified. The apache user has permission for the directory. I've disabled configuration caching in the Magento settings. I'm a little confused on some of the naming conventions; I just tried to follow the example. Anyone know where I'm going wrong?
in magento/app/etc/modules/Feed.xml:
<?xml version="1.0"?>
<config>
<modules>
<Feed_Sales>
<codePool>local</codePool>
<active>true</active>
</Feed_Sales>
</modules>
</config>
in magento/app/code/local/Feed/Sales/etc/config.xml:
<?xml version="1.0"?>
<config>
<global>
<models>
<feedsales>
<class>Feed_Sales_Model</class>
</feedsales>
</models>
<events>
<sales_order_place_after>
<observers>
<feed_sales_order_observer>
<type>singleton</type>
<class>sales/order_observer</class><!-- I've also tried Feed_Sales_Model_Order_Observer here -->
<method>export_new_order</method>
</feed_sales_order_observer>
</observers>
</sales_order_place_after>
</events>
</global>
</config>
in magento/app/code/local/Feed/Sales/Model/Order/Observer.php:
<?php
class Feed_Sales_Model_Order_Observer
{
public function __contruct()
{
}
/**
* Exports new orders to an xml file
* #param Varien_Event_Observer $observer
* #return Feed_Sales_Model_Order_Observer
*/
public function export_new_order($observer)
{
Mage::log("reached export_new_order");
try
{
$dumpFile = fopen('/home/jorelli/new_orders/testdump', 'w+');
fwrite($dumpFile, 'this is a test!');
}
catch (Exception $e)
{
Mage::log("order export failed.\n");
}
return $this;
}
}
?>
Magento 1.4 on Debian Lenny with Apache2 if it should matter for any reason.
Read my articles, they'll help you understand what's going on from a naming convention standpoint and get you grounded in some of Magento's conventions/assumptions.
Looking at the samples above, you have a few things not quite right.
First, your file in the etc folder is named wrong
magento/app/etc/modules/Feed.xml
This file needs to be named Packagename_Modulename, so you probably want
magento/app/etc/modules/Feed_Sales.xml
Look at System -> Configuration -> Advanced to see if your module shows up. If it does, you'll have named this file correctly. Without this, the module you created isn't being loaded into the system, and your code never has a chance to run.
Next, you're specifying the class incorrectly. You say
sales/order_observer
but that first part of the URI (sales) is incorrect. You defined your models section as
<models>
<feedsales> <!-- this is your model part -->
<class>Feed_Sales_Model</class>
</feedsales>
</models>
which means you want
feedsales/order_observer
Checkout the Class/URI tab of Commerce Bug and try entering some URIs (like sales/order) to get a better idea of what's going on here.
Another quick tip, when you're trying to get your handler setup, do it for an event that fires on every page load. Then, once you have your method being called, you can switch it to the specific event you want and not have to go through the entire purchase process.
Finally, and I realize you were copying examples, consider putting your module in a folder named something other than Sales. I find mimicking the names of Magento core folders only add an extra layer of confusion, which is not what you need while you're learning the system.
The problem seems to be with your observer declaration. Give this a try:
<events>
<sales_order_place_after>
<observers>
<feed_sales_order_observer>
<type>singleton</type>
<class>feedsales/order_observer</class>
<method>export_new_order</method>
</feed_sales_order_observer>
</observers>
</sales_order_place_after>
</events>
Holy crap. I was stupid. I tested it out with a command line script, like I do with a lot of things, but that particular file was only writeable by the command line interpreter, not the Apache interpreter. I should have left the office earlier last night.
Well for the record, this is how you trigger an action on order placement. I'll leave it up for posterity.
better use this event instead of sales_order_save_after and checkout_onepage_controller_success_action
checkout_submit_all_after
it will work even site not redirected to success page because of some errors like internet issue and all.
try to use below code my requirement also same
namespace Custom\Checkout\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Simplexml\Element as SimpleXMLElement;
class Afterplaceorder implements ObserverInterface
{
protected $_request;
protected $_layout;
protected $_dir;
protected $jsonHelper;
protected $timezone;
protected $_io;
protected $_RandomBytes;
public function __construct(
\Magento\Framework\View\Element\Context $context,
\Magento\Framework\Filesystem\DirectoryList $dir,
\Magento\Framework\Json\Helper\Data $jsonHelper,
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone,
\Magento\Framework\Filesystem\Io\File $io,
\Magento\Framework\Math\Random $RandomBytes
){
$this->_layout = $context->getLayout();
$this->_request = $context->getRequest();
$this->_dir = $dir;
$this->jsonHelper = $jsonHelper;
$this->timezone = $timezone;
$this->_io = $io;
$this->_RandomBytes = $RandomBytes;
}
/**
* #param \Magento\Framework\Event\Observer $observer
* #return void
*/
public function execute(EventObserver $observer)
{
$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/orderdata.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$order = $observer->getData('order');
$quote = $observer->getQuote();
try{
// function defination to convert array to xml
function array_to_xml( $data, &$xml_data ) {
foreach( $data as $key => $value ) {
if( is_numeric($key) ){
$key = 'item'.$key; //dealing with <0/>..<n/> issues
}
if( is_array($value) ) {
$subnode = $xml_data->addChild($key);
array_to_xml($value, $subnode);
} else {
$xml_data->addChild("$key",htmlspecialchars("$value"));
}
}
}
$data = array('total_stud' => 500);
// creating object of SimpleXMLElement
$xml_data = new SimpleXMLElement('<?xml version="1.0"?><order></order>');
// function call to convert array to xml
array_to_xml($orderDeatails,$xml_data);
//saving generated xml file;
if ( ! file_exists($this->_dir->getPath('var').'/api/order')) {
$this->_io->mkdir($this->_dir->getPath('var').'/api/order', 0775);
}
$result = $xml_data->asXML($this->_dir->getRoot().'/var/api/order/order_'.$order->getIncrementId().'.xml');
}catch(\Exception $e){
$logger->info(print_r('error-> '.$e->getMessage(),true));
}
}
}
hope it will help you!
Happy Coding!!

Categories