Accessing shipping cost in Magento's cart and/or checkout
First, try not to worry too much about performance until you see an actual bottleneck somewhere. Have faith in the multitude of caching systems. Put more cynically, Magento's already a bit of a SQL beast, so if you have a store tuned well a few extra queries won't hurt.
Second, the database hit might not even be a problem. The shipping/rate_request
Model doesn't appear to be backed by a database. If you look at the two times it's used in the core code
Mage_Shipping_Model_Shipping::collectRatesByAddress
Mage_Sales_Model_Quote_Address::requestShippingRates
you can see the shipping/rate_request
model is being instantiated, and then populated from already loaded fields. Additionally, all the models used in Mage_Shipping_Model_Carrier_Tablerate::collectRates
don't load anything from a database, they just do calculations.
It's admirable that you want to build something that's as performant as possible in the first go around, but there's too many complex interactions in a modern OO system to magically know the most performant way to do something. Do what you need to to get the information you need, and handle performance tuning (if needed) during a maintenance release (or if you're not lucky enough to have maintenance release, when someone with power in your organization grumbles about the speed somewhere)
Third, when the system doesn't provide access to something yo need, that's what the class override system is for. Something like
class Package_Module_Model_Carriertablerate extends
Mage_Shipping_Model_Carrier_Tablerate
{
public function getRate(Mage_Shipping_Model_Rate_Request $request)
{
$rate = parent::getRate($request);
Mage::register('package_module_carriertablerates', $rate);
return $rate;
}
}
...
//later, retrieve the rate
$rates = Mage::registry('package_module_carriertablerates');
You're basically calling the same code as before, but stowing the results away somewhere for later access. About as safe as an override can get.
Jonathan Day
Lover of patterns, hater of hacks, aficionado of elegant solutions. Navigating the Magento maze since 2008. twitter: http://twitter.com/#!/aligent
Updated on June 13, 2022Comments
-
Jonathan Day almost 2 years
Please note, this question is regarding the shipping cost, not price. There is an important difference, i.e. what $$ charge the shipping method incurs for the store owner, as opposed to what is $$ charge is paid by the customer.
The
shipping_tablerate
database table includes acost
field, which is populated in theMage_Shipping_Model_Carrier_Tablerate
object during thecollectRates
method. However, that field is not accessible anywhere else in the stack, e.g. from a quote's address.I need to access that value on the cart page, and I can't find anyway to achieve it, other than to instantiate a
Mage_Shipping_Model_Rate_Request
object to pass intocollectRates()
. That seems unnecessarily inefficient given that the data is already loaded from the table and should be accessible.I have tried Observing the
<shipping_carrier_tablerate_load/>
event, but it seems that the_load
event is not thrown for that model.I have also tried accessing the rate from the quote:
$quote = Mage::getSingleton('checkout/cart')->getQuote(); $address = $quote->getShippingAddress(); $rate = $address->getShippingRateByCode($code ='tablerate_bestway');
I can see the calculated
price
, howevercost
is not present in that model.At this stage, I'm running out of ideas. Any suggestions gratefully received!
Thanks, Jonathan
-
Jonathan Day over 13 yearsThanks Alan, great answer. My concern about going back to the database was because this model seems not to go through the standard autoload process, hence why the
$model_load
event is not thrown. I like the idea of using theMage::register
to store the value. DoesMage::register
store globally or just for thatcheckout/session
? IF it's global, then I'd need to check that the rate that is saved includes thedest_zip
used to calculate it. But that's an implementation detail. -
Jonathan Day over 13 yearsP.S. The database load for
tablerate
is performed inMage_Shipping_Model_Mysql4_Carrier_Tablerate_Collection::__construct()
and is reasonably complex as it involves three tables in the join. -
Alan Storm over 13 yearsAh, I misunderstood the question. I thought you wanted to grab it for use during the same request. I'm pretty sure Mage::register is a per request thing .. global variables without violating global scope. Also, FWIW, I just searched my local 1.4 and 1.4.1 codebase and didn't find the shipping_carrier_tablerate_load event anywhere.
-
Jonathan Day over 13 yearsYeah, I do want it on a per-request basis, so that's great. Re the
shipping_carrier_tablerate_load
event, I was referring to the very handy genericload_before
andload_after
events per Joseph's blog post (masteringmagento.com/2010/06/…)