We recently integrated a feature that would prevent users from creating an order with a PO Box in Australia. Since we are already familiar with Australia Post shipping integration (and heavy modifications of it, I might add :)), the first thing that came to mind is to have a look if there’s some API or web interface that can be used to filter out post codes that are linked to PO Boxes. Many high volume addresses and/or PO Boxes in Australia have their own post code, so it looked like an obvious solution. It turns out that it’s not that simple or logical, since there are many edge cases where you can not reliably tell if you should filter the code out or not. There is a guide made by Fontis that shows how to validate the input fields in order to filter out PO Boxes. However, in our case we had some dynamic content loading with Temando shipping method integrated. This guide is intended as a more general solution that is compatible with Checkout page’s dynamic content and third party Shipping methods integration like Temando.
Compatibility issues
The first step we took to add this functionality is to add a regular expression validation check in the Customer/Model/Address/Abstract.php (like explained in the Fontis’ post). However, Temando’s dynamic content on the checkout refused to update itself and was throwing strange validation errors. We have identified that the source of the compatibility problem is not the regular expression itself, but rather its location and triggering method.
Solution
The solution is based on pretty much the same logic – regular expression check to filter out addresses that have PO boxes in them (not a prefect solution, but at the moment, that’s the only tool developers for Australian market have). The difference is in the approach. We will create an AJAX called server side validation that hooks only on the needed fields without interference (overriding) with Magento’s validation logic or any other custom modification, dynamically generated data or a third party shipping module. So, let’s start.
The first thing we’ll need is a module. You can make a blank module or use a “wrapper” kind of module that holds all those minor changes and fixes for the project you are working on (like I do). Define a new router in your module’s config.xml file:
<frontend>
<routers>
<poboxes>
<use>standard</use>
<args>
<module>Namespace_Yourmodulename</module>
<frontName>poboxes</frontName>
</args>
</poboxes>
</routers>
</frontend>
This router is linked with the controller action responsible for validation, so your module will need a PoboxesController.php file as well:
class Namespace_Yourmodulename_PoboxesController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
$this->loadLayout();
$this->renderLayout();
}
public function checkpoboxAction()
{
$street1 = $this->getRequest()->getParam('street1');
$street2 = $this->getRequest()->getParam('street2');
$response = '';
if (preg_match("/p.* *o.* *box/i", street1) || preg_match("/p.* *o.* *box/i", $street2))
{
$response = Mage::helper('yourmodulehelper')->__('We do not ship to PO boxes.');
}
echo $response;
}
}
This controller action, when called will read two parameters: street1 and street2. Those are your – street1 and street2 form elements on the checkout page :). The $response variable contains the HTML response: it’s empty if the validation passed and contains the error message if the validation fails. One important thing to remember here is to echo the response instead of using return. If you use the “return”, you’ll end up with a system.log full of “Headers already sent” errors.
We now need to connect the logic with the page itself. I suggest you to go to Magento’s admin area, turn the template path hints on and go to the Checkout page. eWave’s Temando module, for example, overrides the default .phtml files and uses its own. Depending on your configuration and/or other third party shipping modules, Magento may not use the default shipping.phtml and billing.phtml files on Checkout page and we need to edit those.
We have two options on the billing information step. One is to invalidate the PO box input directly, another one is to avoid the validation if customer decides to use different shipping address. In the second case, the validation will be done on the shipping step. I’ll post the second approach just as a showcase.
Find the “Continue” button in the billing.phtml file and notice that its onclick action is to call billing.save() function. We’ll inject our code here and use custom_billing() function instead of a default one. Your “Continue” button should look like this:
<button type="button" title="<?php echo $this->__('Continue') ?>" class="button" onclick="custom_billing()"><span><span><?php echo $this->__('Continue') ?></span></span></button>
Create the custom_billing() javaScript function at the bottom of your billing.phtml file:
function custom_billing()
{
var poboxcheck = '<?php echo $this->getUrl("poboxes/poboxes/checkpobox")?>street1/'
+ document.getElementById('billing:street1').value + '/street2/'
+ document.getElementById('billing:street2').value;
if (document.getElementById('billing:use_for_shipping_yes').checked == true)
{
new Ajax.Request(poboxcheck,
{method:'get',
onSuccess: function (data){
var response = data.responseText;
response = response.strip();
if (response !== '')
{
alert(response);
}
else
{
billing.save();
}
}
}
);
}
else
{
billing.save();
}
}
Hold on… AJAX?! In my PHP? Well, I’d never… 🙂
Don’t be scared. It’s fairly simple, really. The first thing we’ve done is formed an URL to the validation controller action that we have already made. That URL is placed inside the “poboxcheck” variable. We have added the billing:street1 and billing:street2 field’s values to the URL and they will be passed to the controller action in a GET request. You can add any number of parameters to a controller action call using this method. The URL format is something like:
your_contoller_action_URL/parameter_1_name/parameter_1_value/parameter_2_name/parameter_2_value/...
and so on. The AJAX request itself calls the provided URL from the “poboxcheck” variable and fetches response from the controller action. It is generally wise to strip() the response, since Magento sometimes adds empty spaces on the start of the response for some weird reason. If the response is empty, we will just call the default billing.save() function. If the response contains the validation error message, we will notify the customer by using alert() (or render some fancy lightbox message, or whatever your designer wants to implement :)). Since we have echoed the response in our controller, the AJAX request will get just a response message. If we returned the response instead, the function would send HTTP headers as well (which should be avoided).
The AJAX call is wrapped in an “if” statement that checks if the customer wants to use billing address as the shipping address. If the option is checked on the Checkout page, it will activate validation right away; if not, it will save the billing information and proceed to the shipping step. That is the only difference between the two approaches discussed earlier. If you want to trigger validation right away on the billing step, just remove this “if” statement and that’s it.
The exact same logic goes in the shipping.phtml file: replace the shipping.save() onclick method with custom_shipping() function, create the function, form an URL with parameters, call it with AJAX and process the response. It really looks the same as in billing step; you just have to make sure to pass the info to shipping.save() at the end (instead to billing.save()).
Clear the cache, including javaScrpt/CSS cache, from Magento’s admin area, and you now have an isolated validation method that will not clash with any third party module or dynamic data generated on the Checkout page.