Custom Magento Events: Customer First Order

Featured Image

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 the Mage_Sales_Model_Order class. Since Mage_Sales_Model_Order somewere down the line inherits from Mage_Core_Model_Abstract you can check it’s _afterSave() method and easily conclude that sales_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 the Mage_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:

<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:

<?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:

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 the customer_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 the customer_first_order event and within that observer I give customer A it’s earned reward points.

Hopefully this article was helpful for some.


7 comments

  1. Hi Branko,
    I am trying to make an extension which work is that when create our new account that time after account confirmation after will go to customer including promotional code. please suggest me what can i do? give me some reference.

  2. Hi Branko,

    I am trying to change Magento default order place functionality. What I am thinking is whatever values that goes in database after order is placed (or pressing Place Order tab), I want to save that all data in one Custom Table in Magento…(I have created Custom Table in Magento database).
    Now how can I save that data in Custom table…as someone suggest me to use event/observer.
    So how can I use event/observer in above scenario…can u plz guide me or explain me how can I achieve the same or any other idea U can privide me…
    If u have any sample code for the same that would be really helpful…

    Thanks..

  3. Hi,

    Thanks for this article.

    It would be interesting to create the “customer_first_order_that_reached_state_complete” event, as you said,
    What would be the best solution for that, maybe it should be based on a “order status changed” event ?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.