Grocery List Program

Haha, yes .. I wanted an excuse to learn a bit more about writing software like this. It's powered by PHP and the data is stored in a MySQL database. The layout is XHTML and the design CSS. Take a looksy at the screenshot to get an idea, while I explain what it does, despite it's simplistic look.

The situation is that once in a while my parents are about to do some grocery shopping and call me, asking me if I need something. Well .. yes, please. However, I am awake at nightly hours and it's a bit of a thing to get out of bed again and instantly try to think of stuff to get. This script allows me to add items when I think about them, and at any time my parents can go to the site and print out a simple and easy to read list.

Procrastinating - I have it down. :rolleyes:


Here is what it does, how it works, and all that. And where I can I will post some of the code.

The setup is an index.php file, that requires global.php and links to a .css file.

The .css file obviously just holds all the stylesheet code.

The global.php file has some $this = ''; to deal with PHP notices and to get the NOW time, in Dutch.


setlocale(LC_ALL, 'nl_NL');
$nu = strftime("%A, %d %B %G, %R");


$nu = Dutch for now by the way .. and allows me to update the database to 'list last edited on $somedate', each time the list is updated.

The reason for this is that when say my parents look at the list and see it's over 10 days old or whatever, that it hasn't been updated and that I don't need anything new. No need to buy the same list twice .. The 'list last edited' shows on the right of the top menu.  Talk about great use of whitespace!

Furthermore the global.php has a function and some XHTML code I re-use and didn't need to pollute the index.php with.

The function is just a whichOptions(); that displays the same code for each possible list, based on 0,1,2,3,etc so the basic list won't let you unread items, since they're already unread. And the other ways around. So it let's me add the list below the checkboxes with checked / disabled for the option tags, where appropriate.

The index.php file has the PHP code in the top, and the XHTML in the bottom. The XHTML has a few [php]<?=$something?>[/php] inside it, but that's it.

The PHP at the top are the if conditionals that check against $_POST or $_REQUEST, etc. To handle the actions by the user using index.php. It are all individual if conditionals, and I am sure it can be optimized. But since this is a personal script for a few people, and not something I am distributing or a lot of people are using, it will not get those hours of optimizing, rewriting, tweaking, etc. It's not needed.

One thing I needed help with was how to process selection of individual items. I had the code to mark a single product as read/unread, and a trigger to mark everything in one go as read/unread. But I didn't remember how to handle dealing with selecting say three individual items from a list of a lot. David helped me by giving some guidance. Which made things fall in place in my head against and I ended up with <input> fields with box[] and their values, so when checked and submitted, would be processed by this code:


if ($_REQUEST['boxes'] == 'boxes') {
$boxes = $_POST['box']; // array time
$allBoxes = ""; // start clean
foreach ($boxes as $box) { $allBoxes .= $box . ","; }
$allBoxes = substr($allBoxes, 0, -1); // fix last comma
UPDATE product SET status=0 WHERE id IN ($allBoxes)
") or die(mysql_error()); // query time
echo "<div align=\"center\"><div class=\"content\"><h1 class=\"nobrdr\"><span style=\"color:green\">Selectie Aangepast <strong>(EVEN WACHTEN..)</strong></span></h1></div></div>"; // throw done msg
header("Refresh: 2; url=index.php?do=bijwerken"); // refresh page and trigger time update event
} //end if


A few things come to mind here, I need to make a function to replace the echo at the end, and that refresh, so I can do doneWithThat("Aangepast", "bijwerken"); and have it just echo it out and refresh.  Code redundancy much Floris? hehe. And the other thing is the mysql_query(); should be $this->query(); if not for consistency than for security. But again, it's a small script and only a few people will be using it.

Ok, the page is two sections, the default list, which is the full admin manage part, and the end user part- which is just the simple list for printing. The admin section links to the simple list, and the simple list to the admin section.

The admin section is three parts, the header, the public list and the old list (history/private).

The simple section won't allow any actions, it's just the active items that still need to be purchased, and easy to read/print.

The admin section has the same list, but with inline management tools, such as 'mark all as read', and checkboxes to select one or more products, and a mass button at the bottom to mark as read. And the old list is the same, but swapped, it allows to mark unread (to make a product active again) or to delete an item.

The top menu has a quick reload this page link for convenience, and the link to show simple list. But more importantly it has add product and the timestamp.

The add product form won't show unless selected, this is done on purpose to deal with whitespace. One can select from a radio box selection list 1 to 12 amounts of x product. And below it an input field where a product description can be entered. The result will be a say '1 bread' or '6 apples' in the product list.

When a product gets added into the database, the timestamp gets updated.

The timestamp in the menu on the right, displays 'the last time this list has been updated', this is to give an indication if the items on the list are recent or if the list is old and not yet updated.

The timestamp also gets updated when an items gets marked as read or unread.

The active list gets all the items from the database with status 1 (for active) and the old list gets all items marked 0 for status (inactive/old). This allows me to have two lists. One for the list of items that need to be purchased. And an old list, which is handy because I might need bread every week .. I don't have to 'add product' every time, I can just mark the product unread from the old list again.

To avoid mistakes I only allow deletion of items from the old list. Deleted items can't be undone.

Each list has checkboxes, and a dropdown below it. This allows me to select one or more items and mark them read/unread/delete. Saving time and repeatability actions.

Each list also have a mass action link above the checkboxes. Clicking it will mark a complete list as read or unread. This is so when I know it was printed and purchased and delivered, that list is old and I can mark it as read with a single click.

Oh by the way, when the list is 'empty', it won't error, but I actually bothered to count the resulting rows and print out ~empty~ when it's 0.


$lijst = ''; // let's start empty
$lijstje = mysql_query("
SELECT * FROM `product`
WHERE status='1'
"); // get what we need
$num_rows = mysql_num_rows($lijstje); // how many rows
if ($num_rows == "0") {
$lijst .= "<li><em>-leeg-</em></li>"; // when empty dont error but say its empty
} else {
while ($result = mysql_fetch_array($lijstje)) {
$lijst .= "<li>"; // lets build the list if we have results
if (empty($hide)) { $lijst .= '<span class="checky"><input name="box[]" value="'.$result['id'].'" type="checkbox"><br /></span>'; }
$lijst .= $result['aantal']. " " .$result['product'] ."</li>\n"; // done with list
} // end while
} // end else


Again, quite dirty, but .. it works. Also, something tells me I should spend 5 minutes later to make sure no 'text' is 'parsed' and nothing can be exploited. Security is not important, but ignoring it isn't smart either.

As far as security goes, it will get limited to my IP address, so from my system here I get the manage tools, and everybody else just the simple list. My IP is quite static.

If anybody has any comments, code improvements, suggestions, etc, please feel free to comment to this blog entry. But please remember, this is more a PHP/MySQL/XHTML/CSS practice than anything. And nothing too serious.

And thank you David for your insight, and Chi for beta testing and finding two bugs. And for the feature suggestion.

[edit] Please realize that $lijst .= "&lt;li&gt;";  by the stupid php bbcode script is done by that stupid script. Plugin is gonna get replaced as soon as I find a replacement. Just pretend I actually wrote: $lijst .= "<li>;";