Mission: Find all the items in a users shopping cart that expired 3 minutes ago.
If you want to cheat just scroll to the last code block. Now for the setup.
This is a common query for any ecommerce website that wants to keep things “fresh” or run some analytics. Private sales sites are fond of this because an item in a shopping cart means that a customer is holding up inventory. That of course is a bad thing. This is just one of MANY reason why searching by date range is important.
So how do we handle this with Lithium our RAD PHP 5.3+ framework and MongoDB? To answer this lets use the mission above as our basis and spit out some code. Since you didn’t skip ahead lets build up our example just a little.
Let’s start in a BaseModel that will hold our core code. We will first assume that we want to query for some standard time values. To get things started we can setup a human readable protected array:
protected $_dates = array( 'now' => 0, '-1min' => -60, '-3min' => -180, '-5min' => -300, '15min' => 900 );
public static function dates($name) { return new MongoDate(time() + static::_object()->_dates[$name]); }
public static function addDates($product, array $options = array()) { $product->expires = static::dates('15min'); $product->created = static::dates('now'); return static::_object()->save($product); }
Note: The MongoDate object will look something like “Thu Aug 05 2010 01:32:41 GMT-0400 (EDT)”. This throws a few folks off thinking its just a string.
Now for our mission. Don’t worry I’ll repeat it here to save you from looking: Find all the items in a users shopping cart that expired 3 minutes ago. How would we do that in the CartsController? All we need to do is call a static method in the CartModel that does the search and let it know what range we are looking for:
$cart = Cart::search(array('time' => '-3min'));
We’re going to let our model do the heavy lifting to get the data.
public static function search($params = null) { $time = (!empty($params['time'])) ? $params['time'] : 'now'; $user = Session::read('userLogin'); return static::all(array( 'conditions' => array( 'session' => Session::key(), 'expires' => array('$gte' => static::dates($time)), 'user' => $user['_id'])), 'order' => array('expires' => 'ASC') )); }
There is a bit going on here so lets break it down. First we are checking to make sure that the time is there and set it if it’s not. Then for the sake of finding a specific users cart item we doing some ID grabbing from the PHP Session. Next is the query part: Give me all the user cart items that are greater than X date.
Notice in the code above that we are calling static::dates($time) to fetch our MongoDate. This will let MongoDB in turn know that we are searching for a date and query for it appropriately. This helps to cut out all the epoch timestamp manipulation in our code to properly search for values.
Whew! Now you are an expert and have enough ammunition to use MongoDates to the full.