Thursday, October 18, 2012

Customer First Order by customize magento event


Often you will stumble upon a case where Magento lacks certain events that you can easily observe. Various business cases can sometimes truly stretch the boundaries of even the best shopping carts like Magento. Luckily, creating or more properly said dispatching your own event in Magento is pretty straight forward task.
Imagine the following business case, where your client, the merchant says: I need to give some reward points to Customer A who invited Customer B. These reward points will be assigned one time only and at the moment when Customer B creates it’s first order in the system.
Ideal scenario would be if you had the built in Magento event like “customer_first_order” or if you need more finer tuning to target the very state of the order, for example “customer_first_order_that_reached_state_complete”. Following along with the client requirement above, I will show you how you can easily make additional events with just some basic thinking invested into the whole process.
So where do we start? I will start from the built in “sales_order_save_after” event. Logically I need to do something after the order is created. From there I will do the logic that checks if this is customers first order or not. If you are new to Magento and you do a lookup/search on entire Magento installation code you will not find the expression Mage::dispatchEvent(‘sales_order_save_after’, array(‘object’=>$this));anywhere.
However you will find the defined values for $_eventPrefix and $_eventObject properties under theMage_Sales_Model_Order class. Since Mage_Sales_Model_Order somewere down the line inherits fromMage_Core_Model_Abstract you can check it’s _afterSave() method and easily conclude thatsales_order_save_after event comes from the expression Mage::dispatchEvent($this->_eventPrefix.’_save_after’, $this->_getEventData());.
The most important thing here for us is to “catch” the parameters that are passed to event. Function call$this->_getEventData()) basically returns the array of array(‘data_object’ => $this, $this->_eventObject => $this);. As we mentioned previously $_eventObject property has the value of “order” set under theMage_Sales_Model_Order class. What this means that all we need to do in our “sales_order_save_after” event observer in order to grab the order passed to the event is an expression like $observer->getEvent()->Order();.
From there on, we will start implementing the code for our specific client requirement. Before we do so, here is the actual code you need in order to have your sales_order_save_after event observer functional.
config.xml from within your extension:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<config>
//...
<frontend>
<events>
<sales_order_save_after>
<observers>
<customer_first_order>
<class>myClassGroup/observer</class>
<method>handleCustomerFirstOrder</method>
</customer_first_order>
</observers>
</sales_order_save_after>
</events>
</frontend>
//...
</config>
Observer.php from within your extension:
1
2
3
4
5
6
7
8
9
<?php
class MyCompany_MyExtension_Model_Observer
{
public function handleCustomerFirstOrder($observer)
{
}
}
Now we need to add the necessary logic that implements client’s specific requirement, which transforms the above Observer.php into something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class MyCompany_MyExtension_Model_Observer
{
private static $_handleCustomerFirstOrderCounter = 1;
public function handleCustomerFirstOrder($observer)
{
$orders = Mage::getModel('sales/order')
->getCollection()
->addFieldToSelect('increment_id')
->addFieldToFilter('customer_id'array('eq' => $observer->getEvent()->getOrder()->getCustomerId()));
//$orders->getSelect()->limit(2);
//if ($orders->count() == 1) {
if ($orders->getSize() == 1) {
if (self::$_handleCustomerFirstOrderCounter > 1) {
return $this;
}
self::$_handleCustomerFirstOrderCounter++;
Mage::dispatchEvent('customer_first_order'array('order' =>$observer->getEvent()->getOrder()));
}
return $this;
}
}
Several things to explain here.
First the use of $_handleCustomerFirstOrderCounter. I have noticed that during the same page request on order after save action, depending on possible other existing event observers that might do order re-save action, etc. my code within handleCustomerFirstOrder($observer) method can get executed twice or more times, thus I used static variable in a form of counter to specifically allow it to execute only once.
Second, you will notice the limit I put on the colelction, $orders->getSelect()->limit(2);. Reason for this is the logic that says “check if this is customers first order”. In order to do that, I need to know if there are other order in the system for the same customer. Since there is no need to fetch/load or even query for all of the order’s of the same customers I merely said “tray to grab two orders from this customer” then with if ($orders->count() == 1) expression I merely said, if total count of grabbed orders is one then this is the first order by this customer. Now you will notice that I commented out the $orders->getSelect()->limit(2); and if ($orders->count() == 1) expressions at the end and went with the use of getSize() method on the collection. Looks nicer and does the same thing. Method getSize() comes from the Varien_Data_Collection_Db class and internally it actually calls the getSelectCountSql() method that specifically executes count on the database, $countSelect->columns(‘COUNT(*)’); ,and returns just the count value. Thus making it even faster.
And finally, in place of implementing my reward points logic code directly there, I decided to trigger thecustomer_first_order event for this case. Reason is simply, maybe other developers in the system would like do do something else on this event, so why not dispatch it.
To conclude the client’s request I simply implement one more observer that handles thecustomer_first_order event and within that observer I give customer A it’s earned reward points.

No comments:

Post a Comment