MongoDB Tutorial Part 5: Sorting Data

Last week we put together the main page of Treasure Bag and managed to get it to successfully list all the treasure in our database. You might also remember that we included some useless sorting buttons. Well this week we’re going to get those buttons working!

 

Sorting With MongoDB in PHP

 

Unsurprisingly, you sort using a function named sort. The only tricky part is knowing where to use it and what arguments to pass it. MongoDB’s sort function belongs to the cursor class. That’s the same cursor class that is returned by the find function. Remember this line of code?

 

$cursor = $connection->treasurebag->treasure->find();

 

That line gave us a cursor pointing at all our treasure. Now we just have to tell the cursor that we want the data sorted. Here’s a quick example of how to sort our data based off of treasure name.

 

$cursor->sort(array('Name'=>1));

 

Now let’s take this apart. $cursor->sort tells us that we’re calling the sort function built into the cursor class. We then tell the sort function how to sort by giving it an array holding a key value pair where the key is the attribute we want to sort by and the value is positive 1 for sorting forwards and -1 for sorting in reverse.

 

Did you catch all that? Here’s a quick question to check. How would you sort a cursor based off of price, starting with the most expensive item?

 

Do you have your answer ready? Let’s see if you got it right:

 

$cursor->sort(array('Price'=>-1));

 

Feel fry to try this out by adding a sort line into your Tresure Bag index.php file. Just make sure that you put it after the call to find (or else you won’t have a cursor to sort) but before the getNext while loop (or else you’ll print the data before sorting it).

 

Letting PHP Know Which Sort of Sort We Want

 

Hard coding a sort is OK if you always want the list to show up in the same order. But we want to be able to switch our sort order between name and price. That means we’re going to need some way to mark which sort we want to happen.

 

The easiest way is probably to just include the kind of sort we want as a variable in the URL and then grab it using the PHP $_GET array. So if we want to sort by name we would change the URL to this:

 

index.php?sortby=Name

 

And if we wanted to sort by price we would use:

 

index.php?sortby=Price

 

Now instead of hard coding our sort we can change our sort based on the URL. I’m sure you’ve all seen this PHP pattern before:

 

if(isset($_GET['sortby'])){

    if($_GET['sortby']=='Price'){
        $cursor->sort(array('Price'=>1));
    }
    elseif($_GET['sortby']=='Name'){
        $cursor->sort(array('Name'=>1));
    }
}

 

Nothing too amazing here. We check whether or not the user gave us a “sortby” variable and then based off of that we choose which kind of sort to run on our cursor. If the user didn’t ask for a sort or asked for a sort we don’t support (sortby=something_else) then we don’t call sort at all and just display the treasure in whatever order the database feels like giving them to us in.

 

You can test this out by hand typing in the following urls:

 

No sorting: index.php

Sort by name: index.php?sortby=Name

Sort by price: index.php?sortby=Price

 

Once you’ve made sure that you can successfully control your sorts by passing URL variables we’re ready for the last step.

 

Making The Buttons Useful

 

No MongoDB here. Just a tiny bit of Javascript. We’re going to add an “onclick” event to our buttons that will navigate the user to a URL with the desired sort parameters. Just find the button printing code in index.php and replace them with this:

 

echo '<button onclick="window.location.href=\'index.php?sortby=Name\'">
                           Sort By Name</button>';
echo '<button onclick="window.location.href=\'index.php?sortby=Price\'">
                           Sort By Price</button>';

 

If everything worked out you should be able to click on the two “Sort By” buttons to switch between Name order and Price order. If you want to see the treasure in their original non-sorted order just click on the “Inventory” link in the top menu.

 

All our treasure, sorted by name

All our treasure, sorted by name

All our treasure, cheapest to most expensive

All our treasure, cheapest to most expensive

Complete Updated Index.php

 <?php
include('header.php');

$connection = new Mongo();
$cursor = $connection->treasurebag->treasure->find();
if(isset($_GET['sortby'])){

    if($_GET['sortby']=='Price'){
        $cursor->sort(array('Price'=>1));
    }
    elseif($_GET['sortby']=='Name'){
        $cursor->sort(array('Name'=>1));
    }
}

echo '<h2>Inventory</h2>';
echo '<button onclick="window.location.href=\'index.php?sortby=Name\'">
                           Sort By Name</button>';
echo '<button onclick="window.location.href=\'index.php?sortby=Price\'">
                           Sort By Price</button>';
echo '<table><tr><td>Name</td><td>Price</td><td>Type</td></tr>';
while($document = $cursor->getNext()){

    $treasureID = $document['_id'];
    $name = $document['Name'];
    $price = $document['Price'];
    $type = $document['Type'];

    echo "<tr><td>$name</td>
                <td>$price</td>
                <td>$type</td></tr>";

}
echo '</table>';

include('footer.php');
?>

That’s it for sorting. A quick short lesson. Join me next week as we add a whole new page that shows detailed information on a single treasure.