I'm attempting to subscribe to the sales_order_place_after observer in Magento so that I can output order data. Unfortunately, anytime I try to log output to Mage::log, print_r, or even var_dump, I receive a User Error: Some transactions have not been committed or rolled back error.
It's probably also important to note that I'm using one step checkout and the checkout usually hangs (confirmation email still comes through). Shortly after the hang, I receive a PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 234881024 bytes) error. Can't seem to track down where or what is causing the memory leak... but it this error is only spit out if I am trying to output data.
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<InfinitelyWhite_OrderEvent>
<version>0.0.1</version>
</InfinitelyWhite_OrderEvent>
</modules>
<!-- Configure our module's behavior in the global scope -->
<global>
<!-- Defining models -->
<models>
<!--
Unique identifier in the model's node.
By convention, we put the module's name in lowercase.
-->
<infinitelywhite_orderevent>
<!--
The path to our models directory, with directory
separators replaced by underscores
-->
<class>InfinitelyWhite_OrderEvent_Model</class>
</infinitelywhite_orderevent>
</models>
<events>
<sales_order_place_after>
<observers>
<infinitelywhite_orderevent>
<class>infinitelywhite_orderevent/observer</class>
<method>exportNewOrder</method>
<type>singleton</type>
</infinitelywhite_orderevent >
</observers>
</sales_order_place_after>
</events>
</global>
Observer.php
class InfinitelyWhite_OrderEvent_Model_Observer
{
/**
* Magento passes a Varien_Event_Observer object as
* the first parameter of dispatched events.
*/
public function exportNewOrder(Varien_Event_Observer $observer)
{
Mage::log('reached export_new_order');
$order = $observer->getOrder();
Mage::log($order->getData());
return $this;
}
}
Any help would be greatly appreciated.
Nick Parson .....please removed singleton
<type>singleton</type>
Turns out the error was coming up because I was attempting to pull order information with $observer->getOrder(), when the correct approach is to use $observer->getEvent()->getOrder(). I was simply missing the getEvent().
I'm now able to extract data like so:
$order = $observer->getEvent()->getOrder();
$billing = $order->getBillingAddress();
$shipping = $order->getShippingAddress();
$customer = $order->getCustomer();
And then I can do something like:
$order->getData() or $customer->getData()
It's also important to note that because I switched up my observer event and I'm not using sales_model_service_quote_submit_success which stopped the nasty User Error: Some transactions have not been committed or rolled back error.
Can't comment yet, but what happens if you disable your observer? (Just to make sure it is the observer causing this issue).
If it is the observer, you could try replacing $order->getData() with $order->debug() and see if that helps
Related
I know there are lot of posts with this problem, but I guess each of them is with different roots of it (at least from what I checked - nothing helped me).
I am trying to fire the event upon click on a button from the user, but I get the upper mentioned exception in a browser alert Mage registry key "_singleton/" already exists.
The part of the config.xml:
.....
<models>
<packagecustomernumber>
<class>Package_CustomerNumber_Model</class>
</packagecustomernumber>
</models>
</global>
<frontend>
<events>
<checkout_type_onepage_save_order>
<observers>
<type>singleton</type>
<class>packageName/customernumber/observer</class>
<method>setCustomerNumber</method>
</observers>
</checkout_type_onepage_save_order>
</events>
</frontend>
And the class itself:
class Package_CustomerNumber_Model_Observer
{
public function setCutomerNumber($observer)
{
die('setCutomerNumber');
}
}
The button which should fire the even it checking out/saving the order, so the event should be correct.
Any suggestions ?
The first thing that pops out is this
<class>packageName/customernumber/observer</class>
That's invalid. This is the node where you're telling Magento what class to use for your observer. As such, the <class/> node should be either the full PHP class name of your observer
<class>Package_CustomerNumber_Model_Observer</class>
Or a class aliases for the model
<class>packagecustomernumber/observer</class>
Also, before running your observer, it helps to make sure you can instantiate your model class. Try running the following code in a Magento loaded environment (script, controller action, phtml template, etc.)
$model = new Package_CustomerNumber_Model_Observer;
var_dump(get_class($model));
$model = Mage::getModel('packagecustomernumber/observer');
var_dump(get_class($model));
If you can't instantiate the class, then Magento won't be able to either (and it's easier to test this first before running through some steps to trigger your observer).
Yes, the "packageName/customernumber/observer" is the source of the problem.
while this class reference is completely incorrect in its structure, the problem actually comes up when your class reference does not match up with your global/models/modulename definition. even when the reference "looks" correct.
The config :
<config>
<global>
<models>
<mymodule>
<class>My_Module_Model</class>
</mymodule>
</models>
<events>
<some_event_tag>
<observers>
<my_event_observer_method>
<class>my_module/observer</class>
<method>myEventObserverMethod</method>
</my_event_observer_method>
</observers>
</some_event_tag>
</events>
</global>
</config>
Will have the same result because "my_module/observer" is not found, since the "my_module" class group node is not configured. The correct use for this sample would have been "mymodule/observer".
So if you run across this error, re-read your config.xml.
Make sure that your config.xml models section contains
<!-- This says that string 'company_module' corresponds to Company_Module_Model pseudo-namespace in getModel() and getSingleton() calls. -->
<company_module>
<class>Company_Module_Model</class>
</company_module>
Otherwise you won't be able to make new model instance.
I've been looking for a while at other threads, several have been close to what I need but not quite.
When a guest or customer sign up for our newsletter the field change_status_at populates with a timestamp.
However, if the guest or customer unsubscribe, we would like the the change_status_at field to pickup the current timestamp.
This is important to use because we do not use the newsletter "feature" of Magento 1.7.0.2 CE. Rather we export the newletter_subscriber list to a company to send an email.
Thank you,
Dan
I just ran into this too. A customer wanted to know the date when a subscribe/unsubscribe action occurred. Looking around the internets I've found a few others who have run into this and some claim that it used to work in very old version of Magento.
I think what happened is that the field definition for change_status_at used to be setup to auto update as a default timestamp (i.e. ON UPDATE CURRENT_TIMESTAMP), but that was lost in some update. So there's no Magento code that writes to the field, because MySql is supposed to magically maintain it.
You could try updating the table definition to add the ON UPDATE CURRENT_TIMESTAMP back in (but I'm not in favor changing default model tables) or adding another field to be the default timestamp field.
Or a better solution would be to create a module with an observer to just add the date in when the subscription changes. Here's that might look like (warning - this code is just an example, there may be some syntax errors due to expunging my module's info) -
app/code/local/Myco/MyMod/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Myco_MyMod>
<version>1.0.0</version>
</Myco_MyMod>>
</modules>
<global>
<models>
<myco>
<class>Myco_MyMod_Model</class>
</myco>
</models>
<events>
<newsletter_subscriber_save_before>
<observers>
<mycomymod_observer_subscriber>
<type>singleton</type>
<class>Myco_MyMod_Model_Observer</class>
<method>setUpdateDate</method>
</mycomymod_observer_subscriber>
</observers>
</newsletter_subscriber_save_before>
</events>
</global>
</config>
===========
in app/code/local/Myco/MyMod/Modules/Observer.php
<?php
class Myco_MyMod_Model_Observer
{
public function setUpdateDate(Varien_Event_Observer $observer) {
$subscriber = $observer->getSubscriber();
$subscriber['change_status_at'] = (date("Y-m-d H:i:s", time()));
}
}
I have wrote 2 Magento observers and they both do exactly what I want with the exception that they end on the wrong page. In other words, they write the log files, modify the databases, and talk with other servers, but they modify the page to page routing. For example, I have an observer that I used at login that modifies a database, writes a cookie, and writes to a log, but it changes the post log-in page to
http://www.my-web-site.com/index.php/customer/login/post/
and then gives me a 404 error. If I hit "Ctrl" + 'r' then I am logged in at
http://www.my-web-site.com/index.php/customer/account/index/
which is correct. If I change, app/code/local/my_module/my_model/etc/config.xml to
app/code/local/my_module/my_model/etc/config.xml.1 (in other words take out the observer), then Magento routes to the correct page,
I'm thinking that I need router information in config.xml. My current config.xml is:
<?xml version="1.0" encoding="UTF-8"?>
<!-- The root node for Magento module configuration -->
<config>
<!-- The module's node contains basic information about each Magento module -->
<modules>
<!-- This must exactly match the namespace and module's folder
names, with directory separators replaced by underscores -->
<MyCompany_LogIn>
<!-- The version of our module, starting at 0.0.0 -->
<version>0.0.0</version>
</MyCompany_LogIn>
</modules>
<!-- Configure our module's behavior in the global scope -->
<global>
<!-- Defining models -->
<models>
<!-- Unique identifier in the model's node.
By convention, we put the module's name in lowercase. -->
<mycompany_login>
<!-- The path to our models directory,
with directory separators replaced by underscores -->
<class>MyCompany_LogIn_Model</class>
</mycompany_login>
</models>
</global>
<frontend>
<!-- Defining an event observer -->
<events>
<!-- The code of the event we want to observe -->
<customer_login>
<!-- Defining an observer for this event -->
<observers>
<!-- Unique identifier within the catalog_product_save_after node.
By convention, we write the module's name in lowercase. -->
<mycompany_login>
<!-- The model to be instantiated -->
<class>mycompany_login/observer</class>
<!-- The method of the class to be called -->
<method>wrtLogInCookie</method>
<!-- The type of class to instantiate -->
<type>singleton</type>
</mycompany_login>
</observers>
</customer_login>
</events>
</frontend>
</config>
I'm guessing that the login inside Magento uses an observer, and I'm interfering with it.
Besides the , I'm guessing that I could also accomplish a similar thing in the PHP Observer code. My observer is:
<?php
/**
* Our class name should follow the directory structure of
* our Observer.php model, starting from the namespace,
* replacing directory separators with underscores.
* i.e. /www/app/code/local/MyCompany/LogIn/Model/Observer.php
*/
class MyCompany_LogIn_Model_Observer extends Varien_Event_Observer
{
/**
* Magento passes a Varien_Event_Observer object as
* the first parameter of dispatched events.
*/
public function wrtLogInCookie(Varien_Event_Observer $observer)
{
// Retrieve the product being updated from the event observer
$customer = $observer->getEvent()->getCustomer();
$email = $customer->getEmail();
Mage::log('The E-mail is: ' . $email);
$ran_nmbr = rand();
Mage::log('The random number is: ' . $ran_nmbr);
$crnt_dat = date("m-d-Y::H:i:s");
Mage::log('The date is: ' . $crnt_dat);
return $this;
}
}
?>
I have read about routers, but the articles discussed it in terms of landing on some page before the extension is executed. As you can see, I need to land on the right page after the extension is executed.
Inside the PHP observer, I also tried redirects. For example,
Mage::app()->getResponse()->setRedirect(Mage::getUrl('customer/account/login'));
Maybe I need a full URL address or something. I'm sure this is easy to fix, but my ignorance seems to be following me around. Please help if you know something about this.
Unfortunately, it's not quite as simple as an easy fix. You see, if we look at app/code/core/Mage/Customer/controllers/AccountController.php, in the loginPostAction(), we see that the customer/session singleton triggers the customer_login call. However, what is causing the trip-up here is that after that is called, back in the controller, the controller calls $this->_loginPostRedirect(), so all of your rerouting work that you did is overwritten.
How to fix:
After saying it isn't all that simple, I did happen to see a cheat that we can take advantage of:
$session = Mage::getSingleton('customer/session');
$session->setBeforeAuthUrl($forwardToUrl);
You're partially right. The problem you're running into is Magento's redirect mechanism works with a "last one to say something" wins philosophy. If you look at the standard login code in
app/code/core/Mage/Customer/controllers/AccountController.php
You'll see the loginPostAction method ends with a call to
$this->_loginPostRedirect();
which (ultimately) ends up calling some code that looks like this
$this->getResponse()->setRedirect($url);
This may be the code that's causing you a problem, or it may be something else. The general problem is the final call to the response object's setRedirect method will win.
My usual solution to this is setting some sort of global flag (static variable on a class, a flag set with Mage::register) when I want to perform a redirect, and then creating an additional observer for controller_action_postdispatch. In this observer I look for the global flag I set, and if I find it, set the redirect there.
This handled 99% of redirect situations, and should handle yours. The times this won't work are with some admin login cases, as well as URL rewrites.
The admin login contains some redirect code that doesn't use Magento response object
#File: app/code/core/Mage/Admin/Model/Session.php
...
header('Location: ' . $requestUri);
exit;
...
If this redirect is causing you a problem, create a listener for admin_session_user_login_success that uses PHP header redirects before Magento's does.
Similarly, if Magento's using a rewrite object, the following code might run
#File: app/code/core/Mage/Core/Model/Url/Rewrite.php
if ($isPermanent) {
header('HTTP/1.1 301 Moved Permanently');
}
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
header('Location: ' . $url);
exit;
(that said, the rewrite code will rarely be your problem, as in standard Magento operation is runs before controller dispatch)
Think of a small and basic affiliate system. I want an URL like
www.myshop.com/mynewproduct.html?afid=123
Every time afid is found in the URL, a method should be called (basically to save "afid" in the session and when the customer buys stuff, I want to track it).
You don't need a router for this. You'll want to setup an event listener that fires for every page load, and then access the variables in the request collection. The controller_front_init_routers event should do.
So, setup your module's config with the following
<global>
<events>
<controller_front_init_routers>
<observers>
<packagename_modulename_observer>
<type>singleton</type>
<class>Packagename_Modulename_Model_Observer</class>
<method>interceptMethod</method>
</packagename_modulename_observer>
</observers>
</controller_front_init_routers>
</events>
</global>
And then create the following class
app/code/local/Packagename/Modulename/Model/Observer.php
class Packagename_Modulename_Model_Observer {
public function interceptMethod($observer) {
$request = $observer->getEvent()->getData('front')->getRequest();
$afid = $request->afid;
//do whatever you want with your variable here
}
}
The interceptMethod can be named whatever you want.
I know this is a very old answer, but it is valid to mention we shouldn't use the controller_front_init_routers event if we intend to store those parameters in session, which is the scenario for the original question. For example, if you instantiate customer/session at this point you won't be able to perform a customer login anymore. Alan pointed this himself in http://alanstorm.com/magento_sessions_early. BTW, thanks Alan for this great article.
How can I set different email templates for Customer Order Confirm email and Admin copy of the same.
I need to add some extra content for the Admin email copy.
Thanks
I am assuming that you are currently using the "copy" feature to send the admin email. Let me know if that's not the case. Because the same email is currently being sent to multiple recipients, it would be difficult to change the content for each recipient. You could send multiple emails with a little bit of code, though, which would allow you to use a different email template for each. This could be achieved by creating a new class:
class MyModule_Model_Sales_Order extends Mage_Sales_Model_Order {
/**
* Sending email with order data
*
* #return Mage_Sales_Model_Order
*/
public function sendNewOrderEmail() {
parent::sendNewOrderEmail();
/**
* Your admin email sending code here. Copy it out of the sendNewOrderEmail
* function in Sales_Order.
*/
return $this;
}
}
And then telling Magento to override the core class inside your module config:
<config>
<global>
<models>
<mymodule>
<class>MyModule_Model</class>
</mymodule>
<sales>
<rewrite>
<order>MyModule_Model_Sales_Order</order>
</rewrite>
</sales>
</models>
</global>
</config>
You'll need to create the template you want and be sure that your overridden model uses that template instead.
Change tag catalog to sales and add namespace with class name, Eg: if namespace is Custom then add Custom_MyModule_Model and Custom_MyModule_Model_Sales_Order, so the resulting XML file would be:
<config>
<global>
<models>
<mymodule>
<class>Custom_MyModule_Model</class>
</mymodule>
<sales>
<rewrite>
<order>Custom_MyModule_Model_Sales_Order</order>
</rewrite>
</sales>
</models>
</global>
</config>
you can check and edit the HTML files in app > locale > en_US > templates > email
Please keep in mind that you are overruling the Sales and not the Catalog.
So to prevent searching why the above code is not working use "sales" instead of "catalog" in the config.xml.
This extension does it: http://codecanyon.net/item/send-new-order-email-to-admin/3198802
Totally worth the $10 given all the time I wasted trying to get the other answer here working. I had just finished making my own custom module for the contact form, but the other solutions here didn't work.