PHP Beer Server – Part Two

Part One

Today, we’ll be fleshing out the method bodies of our PHP Beer Service.

First, I’m going to fix the getMethods function.

Old:

/*
 * getMethods returns a Vector of methods accessable to a client
 */
function getMethods( $params ) {
    $methods = array(
                     'getPrice(String beer)',
                     'setPrice(String beer, Double price)',
                     'getBeers()',
                     'getCheapest()',
                     'getCostliest()'
                     );
    return new xmlrpcresp( new xmlrpcval($methods), "vector");
}

New:

/*
 * getMethods returns an Array of methods accessable to a client
 */
function getMethods( $params ) {
    $methods = array(
                     new xmlrpcval('getPrice(String beer)', 'string'),
                     new xmlrpcval('setPrice(String beer, Double price)', 'boolean'),
                     new xmlrpcval('getBeers()', 'array'),
                     new xmlrpcval('getCheapest()', 'string'),
                     new xmlrpcval('getCostliest()', 'string')
                     );
    return new xmlrpcresp( new xmlrpcval($methods), "array" );
}

Here, I’ve updated the getMethods function a bit. I found out that to correctly return the array, it needs to be an array of xmlrpcval objects. So, easy fix.

I made an associative array to hold the beer data. Keeping it simple.

$beersArray = array(
                    'Bud' => '6.99',
                    'Coors' => '8.49',
                    'Corona' => '13.99',
                    'Genesee' => '4.99',
                    'Guinness Draught' => '12.99',
                    'Labatt' => '7.99',
                    'Sam Adams' => '12.49'
                    );

Now that we have our array, we can start on the getPrice function. Thanks to PHP’s enormous amount of well-built methods, it’s as simple as using Array Search.

/*
 * getPrice returns a Double price for a beer
 */
function getPrice( $beer ){
    return new xmlrpcval(array_search( $beer, $beersArray ), 'double');
}

Now, I’m going to move on to function getBeers. I have a feeling I might run into an issue here, since PHP doesn’t support Vectors like Java – I’m passing back an array. We’ll see how this goes in testing.

/*
 * getBeers accepts no parameters, returns a Vector of beers and their prices
 */
function getBeers() {
    return new xmlrpcval($beersArray, 'array');
}

For our next function, getCheapest, it was a little harder to think of a solution, since we have to look through the array, find the minimum value, and then find its corresponding key. Thankfully, a quick Google search led me to StackOverflow, which had the exact solution.

/*
 * getCheapest accepts no parameters, returns a String of the cheapest beer on tap
 */
function getCheapest(){
    return new xmlrpcval(array_keys($beersArray, min($beersArray)), 'string');
}

GetCostliest was ultra easy, since it’s just one function change, from min to max.

/*
 * getCostliest accepts no parameters, returns a String of the costliest beer on tap
 */
function getCostliest(){
    return new xmlrpcval(array_keys($beersArray, max($beersArray)), 'string');
}

SetPrice function wasn’t too difficult, either. Here’s what I came up with.

/*
 * setPrice returns true if set correctly; sets the price for a beer given
 * String $beer
 * Double $price
 */
function setPrice( $beer, $price ){
    if($beersArray[$beer]){ //if the beer exists in the array
        $beersArray[$beer] = $price; //set its value to $price
        return new xmlrpcval(true, 'boolean');
    }
    else{ //we couldn't find that beer
        return new xmlrpcval(false, 'boolean'); //so don't update its price
    }

Great! So, looks like I have all my methods filled in now. Time to test!
Full PHP server file:

<?php
include "lib/xmlrpc.inc";
include "lib/xmlrpcs.inc";
include "lib/xmlrpc_wrappers.inc";

$beersArray = array(
                    'Bud' => 6.99,
                    'Coors' => 8.49,
                    'Corona' => 13.99,
                    'Genesee' => 4.99,
                    'Guinness Draught' => 12.99,
                    'Labatt' => 7.99,
                    'Sam Adams' => 12.49
                    );

/*
 * getMethods returns a Vector of methods accessable to a client
 */
function getMethods( $params ) {
    $methods = array(
                     new xmlrpcval('getPrice(String beer)', 'string'),
                     new xmlrpcval('setPrice(String beer, Double price)', 'boolean'),
                     new xmlrpcval('getBeers()', 'array'),
                     new xmlrpcval('getCheapest()', 'string'),
                     new xmlrpcval('getCostliest()', 'string')
                     );
    return new xmlrpcresp( new xmlrpcval($methods), "array" );
}

/*
 * getPrice returns a Double price for a beer
 */
function getPrice( $beer ){
    return new xmlrpcval(array_search( $beer, $beersArray ), 'double');
}

/*
 * setPrice returns true if set correctly; sets the price for a beer given
 * String $beer
 * Double $price
 */
function setPrice( $beer, $price ){
    if($beersArray[$beer]){ //if the beer exists in the array
        $beersArray[$beer] = $price; //set its value to $price
        return new xmlrpcval(true, 'boolean');
    }
    else{ //we couldn't find that beer
        return new xmlrpcval(false, 'boolean'); //so don't update its price
    }
}

/*
 * getBeers accepts no parameters, returns a Vector of beers and their prices
 */
function getBeers() {
    return new xmlrpcval($beersArray, 'array');
}

/*
 * getCheapest accepts no parameters, returns a String of the cheapest beer on tap
 */
function getCheapest(){
    return new xmlrpcval(array_keys($beersArray, min($beersArray)), 'string');
}

/*
 * getCostliest accepts no parameters, returns a String of the costliest beer on tap
 */
function getCostliest(){
    return new xmlrpcval(array_keys($beersArray, max($beersArray)), 'string');
}

//declare signature, provide documentation
//the php server supports remote introspection
$getMethods_sig = array(array($xmlrpcArray));
$getMethods_doc = "Returns a Vector of methods accessable to a client.";

$getPrice_sig = array(array($xmlrpcArray, $xmlrpcDouble));
$getPrice_doc = "Returns a Double price for a given beer.";

$setPrice_sig = array(array($xmlrpcArray, $xmlrpcString, $xmlrpcDouble));
$setPrice_doc = "Returns true if set correctly; sets the price for a beer given.";

$getBeers_sig = array(array($xmlrpcArray));
$getBeers_doc = "Returns a Vector of the beers on tap";

$getCheapest_sig = array(array($xmlrpcArray));
$getCheapest_doc = "Returns a String of the cheapest beer on tap";

$getCostliest_sig = array(array($xmlrpcArray));
$getCostliest_doc = "Returns a String of the costliest beer on tap";

new xmlrpc_server(array('beer.getMethods' =>
                        array(
                                'function' => 'getMethods',
                                'signature' => $getMethods_sig,
                                'docstring' => $getMethods_doc),
                        'beer.getPrice' =>
                              array(
                                      'function' => 'getPrice',
                                      'signature' => $getPrice_sig,
                                      'docstring' => $getPrice_doc),
                        'beer.setPrice' =>
                              array(
                                      'function' => 'setPrice',
                                      'signature' => $setPrice_sig,
                                      'docstring' => $setPrice_doc),
                        'beer.getBeers' =>
                              array(
                                      'function' => 'getBeers',
                                      'signature' => $getBeers_sig,
                                      'docstring' => $getBeers_doc),
                        'beer.getCheapest' =>
                              array(
                                      'function' => 'getCheapest',
                                      'signature' => $getCheapest_sig,
                                      'docstring' => $getCheapest_doc),
                        'beer.getCostliest' =>
                              array(
                                      'function' => 'getCostliest',
                                      'signature' => $getCostliest_sig,
                                      'docstring' => $getCostliest_doc)
                        ));


?>

PHP Beer Server – Part One

As a followup to my eCommerce site series, I’ll be writing up a project I’m starting for the same Server Programming class. It’s a XML-RPC PHP server that handles beer! Yum.

Beer.

The professor was nice enough to allow us to use a library that handles the nitty-gritty of XML-RPC, so I’ve started to stub in the necessary methods.

<?php
include "lib/xmlrpc.inc";
include "lib/xmlrpcs.inc";
include "lib/xmlrpc_wrappers.inc";

/*
 * getMethods returns a Vector of methods accessable to a client
 */
function getMethods( $params ) {
    $methods = array(
                     'getPrice(String beer)',
                     'setPrice(String beer, Double price)',
                     'getBeers()',
                     'getCheapest()',
                     'getCostliest()'
                     );
    return new xmlrpcresp( new xmlrpcval($methods), "vector");
}

/*
 * getPrice returns a Double price for a beer
 */
function getPrice( $beer ){
    
}

/*
 * setPrice returns true if set correctly; sets the price for a beer given
 * String $beer
 * Double $price
 */
function setPrice( $beer, $price ){
    
}

/*
 * getBeers accepts no parameters, returns a Vector of beers and their prices
 */
function getBeers() {
    
}

/*
 * getCheapest accepts no parameters, returns a String of the cheapest beer on tap
 */
function getCheapest(){
    
}

/*
 * getCostliest accepts no parameters, returns a String of the costliest beer on tap
 */
function getCostliest(){
    
}

//declare signature, provide documentation
//the php server supports remote introspection
$getMethods_sig = array(array($xmlrpcArray));
$getMethods_doc = "Returns a Vector of methods accessable to a client.";

$getPrice_sig = array(array($xmlrpcArray, $xmlrpcDouble));
$getPrice_doc = "Returns a Double price for a given beer.";

$setPrice_sig = array(array($xmlrpcArray, $xmlrpcString, $xmlrpcDouble));
$setPrice_doc = "Returns true if set correctly; sets the price for a beer given.";

$getBeers_sig = array(array($xmlrpcArray));
$getBeers_doc = "Returns a Vector of the beers on tap";

$getCheapest_sig = array(array($xmlrpcArray));
$getCheapest_doc = "Returns a String of the cheapest beer on tap";

$getCostliest_sig = array(array($xmlrpcArray));
$getCostliest_doc = "Returns a String of the costliest beer on tap";

new xmlrpc_server(array('beer.getMethods' =>
                        array(
                                'function' => 'getMethods',
                                'signature' => $getMethods_sig,
                                'docstring' => $getMethods_doc),
                        'beer.getPrice' =>
                              array(
                                      'function' => 'getPrice',
                                      'signature' => $getPrice_sig,
                                      'docstring' => $getPrice_doc),
                        'beer.setPrice' =>
                              array(
                                      'function' => 'setPrice',
                                      'signature' => $setPrice_sig,
                                      'docstring' => $setPrice_doc),
                        'beer.getBeers' =>
                              array(
                                      'function' => 'getBeers',
                                      'signature' => $getBeers_sig,
                                      'docstring' => $getBeers_doc),
                        'beer.getCheapest' =>
                              array(
                                      'function' => 'getCheapest',
                                      'signature' => $getCheapest_sig,
                                      'docstring' => $getCheapest_doc),
                        'beer.getCostliest' =>
                              array(
                                      'function' => 'getCostliest',
                                      'signature' => $getCostliest_sig,
                                      'docstring' => $getCostliest_doc)
                        ));


?>

So thanks to the awesomeness that is Edd Dumbill’s XML-RPC PHP library, that’s pretty much everything besides the body of each method. Tune in next time for part two!

eCommerce site – Conclusion

Part One
Part Two
Part Three
Part Four
Part Five
Part Six

Now that the eCommerce site’s been completed and handed to the professor, I await my grade breakdown. This was an enormous opportunity to learn, and my skills in PHP, jQuery, Javascript, and MySQL definitely grew.
My main takeaway from the project is that I needed to put more planning into my code structure. I hadn’t mapped out all the possibilities when I started typing, so I had to go back and rework some code. It ultimately ended up costing me time and energy I could have spent refining the site. I’m still pretty happy with how it turned out.

PHP eCommerce Site – Part Six

Part One
Part Two
Part Three
Part Four
Part Five

Tonight’s the night I turn in this project! I’m glad, because I’m definitely feeling a bit worn down. I’ve spent the past hour or so doing minor styling and bugfixes. However, the biggest problem I’ve run into is that the production server, RIT’s NOVA, is being generally terrible. So I’ve decided to host it on my samjhill.com Amazon Web Services cloud server. Hopefully, the professor won’t deduct too many points.

Demo Site

For some reason, after I transferred the database and code to my server, I’ve been having trouble adding products to my cart from the catalog. Turns out that I’ve been parsing the item id’s incorrectly this whole time, and I found a quick and simple fix.

$data = $_REQUEST['data']; //getting the data from the ajax call
$data = explode(',', $data); //convert it to an array

Since the project requirements call for at least 3 items on sale at a time, I have the admin page displaying this message:

if( sizeOf($saleProducts = getSaleProducts()) &lt; 3){ // too few, show an error
                    echo &quot;&lt;div class='alert alert-danger fade in' role='alert'&gt;You must put an additional &quot; . (intval(3) - intval($saleProducts)) .&quot; products on sale.&lt;button type='button' class='close' data-dismiss='alert'&gt;&lt;span aria-hidden='true'&gt;x&lt;/span&gt;&lt;span class='sr-only'&gt;Close&lt;/span&gt;&lt;/button&gt;&lt;/div&gt;&quot;;
                    }
                if( sizeOf($saleProducts = getSaleProducts()) &gt; 5){ //too many, show an error
                    echo &quot;&lt;div class='alert alert-danger fade in' role='alert'&gt;You have &quot; . (intval($saleProducts) - intval(5)) .&quot; too many products on sale.&lt;button type='button' class='close' data-dismiss='alert'&gt;&lt;span aria-hidden='true'&gt;x&lt;/span&gt;&lt;span class='sr-only'&gt;Close&lt;/span&gt;&lt;/button&gt;&lt;/div&gt;&quot;;
                    }
                ?&gt;

All that’s left to do is to password-protect the administration page. Simple enough.

PHP eCommerce Site – Part Five

Part One
Part Two
Part Three
Part Four

6:54pm
Alright! So most of the site is done, and I’m adding in the last few pieces, including a file uploader. Figure I’ll follow this tutorial.

9:35pm
The image uploader turned into a bit of a nightmare. I had to customize the script quite a bit for it to fit into my form correctly. I’ll be adding it to the repository so we can take a look at the changes I made. Now that it’s done, I’m looking through the grading rubric and taking a look at what I have left to complete.

1) There will be a minimum of 15 items total in the database
Okay, cool, I can do that when I move this onto the production server.

2)There will be links that allow the user to go to the next or previous page of items as applicable with selected page number validation
Kind of strange thing to require for just 15 items; let’s table that one for now.

3)A form to put items on sale or remove them from being on sale. You must validate that there is a minimum of 3 items on sale at any one time with a maximum of 5 items on sale at one time.
Ah, crap. The second part of this sounds kind of difficult. I have the first part set, though.

4) A password field. No posting/update should happen without a match (or some sort of login with session management)
And I need to set up a login page for the administrator. I’ve only done this a few times before, but it shouldn’t be too hard.

5) All input will be validated and sanitized as appropriate based on the information in the field.
I have HTML5’s useful input type fields and PHP’s prepared statements to thank for this. Pretty much complete, just gotta check to make sure it’s 100% done.

Instead of doing any of this, I decided to add a little line into the navigation bar to show the count of items in the cart.

<?php if(sizeOf(getCart()) > 0){ 
echo "["; 
printf(sizeOf(getCart()), true); 
echo "]"; } 
?>

It’s getting late and I’m exhausted, so I think I’ll save the rest for tomorrow night.

PHP eCommerce Site – Part Four

Part One
Part Two
Part Three

11:13am
I’ve got a few functions of the administration dashboard set up now! Because I built the PHP functions to be so abstract, it was easy to set up an insertProduct and updateProduct function for use with the dashboard. I call the functions with AJAX.

The insertProduct is incredibly simple, thankfully.

function insertProduct ($product ){
        $productValues = array(
            'name' => $product['name'],
            'description' => $product['description'],
            'price' => $product['price'],
            'quantity' => $product['quantity'],
            'image' => $product['image'],
            'sale' => $product['sale'],
            'id' => $product['id']
        );
        insert('products',$productValues);
    }

Next up, the updateProduct function:

function updateProduct( $product ){
        $productValues = array(
            'name' => $product['name'],
            'description' => $product['description'],
            'price' => $product['price'],
            'quantity' => $product['quantity'],
            'image' => $product['image'],
            'sale' => $product['sale'],
            'id' => $product['id']
        );
        update('products',$productValues);
    }

The next thing I’ll be adding is the deleteProduct AJAX call – should be easy enough, since I already have the PHP deleteProduct method complete.

11:45am
Woohoo! The admin page can now add, update and delete products from the products table.
Here’s what the stack looks like.

Each product has a delete button, which has an onclick event of deleteProduct($product[6]); where $product[6] is the item ID.

deleteProduct jQuery

function deleteProduct( id ){
    console.log('deleting product ' + id);
    ajax('POST', id, 'assets/php/formsubmit.php?method=deleteProduct');
}

This then calls formsubmit.php and passes the parameter deleteProduct, which triggers a switch inside and calls the PHP function deleteProduct(id) in the main library.

//deleteProduct
function deleteProduct ( $id ){
        error_log("deleting product $id");
        return delete('products', $id ); 
}

//main delete function, if you remember from part two
function delete( $tableName, $value ){
        switch($tableName) //switch tablename so we can pick products or cart table
        {
            case "products":
                $tbl = 'products';
                break;
            case "cart":
                $tbl = 'cart';
                break;
        }
        error_log($value);
        //no switch for property here. We only want to delete by ID for safety
        $db = dbConnect(); //connect to our database
        if( strcmp($value, '') != 0 ){
            error_log("deleting $value from cart");
            $query = "DELETE FROM $tbl WHERE id=:value";
            $stmt = $db->prepare($query);
            //bind params
            $stmt->bindValue(':value', $value);
        }
        else{ //$value is empty; therefore we clear the table
            error_log("clearing the cart");
            $query = "DELETE FROM $tbl";
            $stmt = $db->prepare($query);
        }
        //execute
        try{
            $stmt->execute();
            $result = $stmt->fetchAll();
            return $result;
        }
        catch(Exception $e){
            return $e;
        }
}

Next steps: I realized I should add a boolean onSale column in the products table to determine if a product is on sale. Hopefully, this won’t screw with my existing code too much.

Also, I added a cool code syntax highlighter to my WordPress installation called SyntaxHighlighter Evolved! It’s pretty great. I’ll be going back and highlighting the code in my posts once I’m done.

12:17pm
It wasn’t too difficult to add that onSale boolean, thankfully. For the administration dashboard, I’ve added these cool toggle switches.

2:26pm
I’ve set the homepage to automatically display the items that are on sale. Now, I’m going to be adding MixItUp to my catalog page to allow for quick and pretty item sorting. I’ve used it before, and really enjoy the results.

3:45pm
I’ve set up the admin dashboard page to be a bit prettier thanks to Bootstrap’s tab-panes class. Also, I added a little styling to the footer (thanks, StackOverflow!).

footer {
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 60px;
  background-color: #f5f5f5;
}

PHP eCommerce Site – Part Three

Part One
Park Two

11:34am
I’ve spent the last few days getting my prepared statements together, and I have the select and delete functions all set.

5:11pm
It seems I finally have all the functions together for adding and correctly removing items from cart and products table.

function select( $tableName, $property, $value ){
        switch($tableName) //switch tablename so we can pick products or cart table
        {
            case "products":
                $tbl = 'products';
                break;
            case "cart":
                $tbl = 'cart';
                break;
        }
        
        $db = dbConnect(); //connect to our database
        if( strcmp($value, '') != 0 ){
            //error_log("looking by property $property");
            switch($property){ //switch property so we can do lookups by ID or name
                case "id":
                    $query = "SELECT * FROM $tbl WHERE id = :value";
                    break;
                case "name":
                    $query = "SELECT * FROM $tbl WHERE name = :value";
                    break;
            }
            $stmt = $db->prepare($query);
            
            //bind params
            $stmt->bindValue(':value', $value);
            
        }
        else{ //$value is empty; therefore we just want a general list of rows
            $query = "SELECT * FROM $tbl";
            $stmt = $db->prepare($query);
        }
        //execute
        try{
            $stmt->execute();
            $result = $stmt->fetchAll();
            return $result;
        }
        catch(Exception $e){
            return $e;
        }
    }
    
    function delete( $tableName, $value ){
        switch($tableName) //switch tablename so we can pick products or cart table
        {
            case "products":
                $tbl = 'products';
                break;
            case "cart":
                $tbl = 'cart';
                break;
        }
        error_log($value);
        //no switch for property here. We only want to delete by ID for safety
        $db = dbConnect(); //connect to our database
        if( strcmp($value, '') != 0 ){
            error_log("deleting $value from cart");
            $query = "DELETE FROM $tbl WHERE id=:value";
            $stmt = $db->prepare($query);
            //bind params
            $stmt->bindValue(':value', $value);
        }
        else{ //$value is empty; therefore we clear the table
            error_log("clearing the cart");
            $query = "DELETE FROM $tbl";
            $stmt = $db->prepare($query);
        }
        //execute
        try{
            $stmt->execute();
            $result = $stmt->fetchAll();
            return $result;
        }
        catch(Exception $e){
            return $e;
        }
    }
    
    function update( $tableName, $values){
        $db = dbConnect(); //connect to our database
        error_log("update called");
        switch($tableName) //switch tablename so we can pick products or cart table
        {
            case "products":
                $query = "UPDATE products SET name=?, description=?, price=?, quantity=?, image=?, sale=? WHERE id=?";
                $stmt = $db->prepare($query);
                $stmt->execute(array($values['name'], $values['description'], $values['price'], $values['quantity'], $values['image'], $values['sale'], $values['id']));
                break;
            case "cart":
                $query = "UPDATE cart SET name=?, description=?, quantity=?, price=? WHERE id=?";
                
                $stmt = $db->prepare($query);
                $stmt->execute(array($values['name'], $values['description'], $values['quantity'], $values['price'], $values['id']));
                break;
        }
    }
    
    function insert( $tableName, $values){
        $db = dbConnect(); //connect to our database
        switch($tableName) //switch tablename so we can pick products or cart table
        {
            case "products":
                $tbl = 'products';
                $query = "INSERT INTO $tbl(name, description, price, quantity, image, sale) VALUES(:name, :description, :price, :quantity, :image, :sale)";
                $stmt = $db->prepare($query);
                $stmt -> bindValue(':name', $values['name']);
                $stmt -> bindValue(':description', $values['description']);
                $stmt -> bindValue(':price', $values['price']);
                $stmt -> bindValue(':quantity', intval($values['quantity']));
                $stmt -> bindValue(':image', $values['image']);
                $stmt -> bindValue(':sale', intval($values['sale']));
                
                break;
            case "cart":
                $tbl = 'cart';
                $query = "INSERT INTO $tbl(name, description, quantity, price, id) VALUES(:name, :description, :quantity, :price, :id)";
                $stmt = $db->prepare($query);
                $stmt -> bindValue(':name', $values['name']);
                $stmt -> bindValue(':description', $values['description']);
                $stmt -> bindValue(':quantity', intval($values['quantity']));
                $stmt -> bindValue(':price', intval($values['price']));
                $stmt -> bindValue(':id', intval($values['id']));
               
                break;
        }
        
        //execute
        try{
            //error_log(print_r($stmt));
            $stmt->execute();
        }
        catch(Exception $e){
            error_log( $e );
        }
    }

PHP eCommerce Site – Part Two

So begins part two of my eCommerce site build. Here’s part one.

2:08pm
At this point, my AJAX calls are passing data correctly, but my PHP script is failing when I’m trying to insert a new row into the Cart table. So I’m having the script log the MySQL statement for testing using this log function I extended from PHP.net’s example.

/* 
     * function writes writes a given string to an error file
     * $text - string, text to be written to file
     * $filename - string, without extension
     * $rewrite - boolean, whether to clear the file
     */
    function writeFile( $text, $filename, $rewrite ){
        $file = $filename . '.txt';
        // Open the file to get existing content
        $current = file_get_contents($file);
        // Append the string to the file
        if($rewrite == true){ //empty the file
            $current = $text;
        }
        else{ //append text
            $current .= $text;
        }
        $current .= "\n --------------- \n";
        // Write the contents back to the file
        file_put_contents($file, $current);
    }
</code>

When called, I have it log the statement, and lo and behold, I have the answer to my question.  
<code>
INSERT INTO cart('name', 'description', 'quantity', 'cost', 'id') VALUES('Floating Platform Bed','This handmade platform bed is both sturdy and beautiful. It's guaranteed to last for years to come. ','1','500','1')
 --------------- 

The description string is being terminated early by my “It’s” having an apostrophe. I’ll have to work some htmlspecialchars() magic on it.

PHP eCommerce Site – Part One

Outside on a beautiful day at RIT
We’re lucky enough to have another beautiful day in Rochester! It’s 80 degrees and feels like Summer still, so I’ll be working outside for a while today.

For my Server Programming course, I’ve started to put together a basic eCommerce website that I’ll be developing in PHP. Here is the requirements document. As I work through it, I’ll be writing my progress and linking to relevant materials for future reference. Here goes everything! The GitHub project is located here.

Part One
2:11pm
So far, I’ve begun the boilerplate for the front end and set up a LAMP server for testing. That tutorial is ultra helpful for making sure there aren’t any quirks in my LAMP setup.

2:25pm
After messing with this blog post for a bit, I’m going to start planning the structure of my PHP classes and database tables. Thankfully, the requirements document is descriptive and helpful, so I shouldn’t have much of a hard time with this.

2:38pm
I have two tables set up – Cart and Products. Cart has four columns – name (VARCHAR), description (VARCHAR), quantity (INT), and price (VARCHAR), as per requirements. Products has more – it includes the previous four columns, plus an image link (VARCHAR) and a sale price (VARCHAR). PHPMyAdmin is my tool of choice to set up these tables. Now, I’m going to populate the products table with some fake products for testing. That reminds me – I guess I should figure out what I’m going to pretend to sell!

2:41pm
I’ve decided I’ll pretend to sell wood furniture. I’ve developed a passion for woodwork lately, and have some pictures of my work on this blog, so it shouldn’t be too difficult. Plus, if I get some more good pictures, it’ll look great contrasted with a sleek, minimalist website.

2:50pm
Okay, I have three dummy items inserted into the products database – it’s tough to come up with convincing product descriptions! I’m heading home now.

4:56pm
I’ve gotten a few PHP functions written. Now, I’m able to list all my products and look up a product by ID. I’m using parameterized queries and OOP methodology to avoid SQL injections. At first, I got thrown by PHP’s bind_result function, but now it’s all working. Looking at the next steps, I’m just going to leave the client-side formatting pretty plain until I get everything planned out.

7:49pm
I finished the products listing, and then realized my database connect function wasn’t flexible enough to read the cart page, too. So I had to rewrite a bit.

Old:

/* function connectWithStatement
     * currently BROKEN
     * Accepts: String $statement, int $numColumns
     * Returns: Array of Products from database
     */
    function connectWithStatement( $statement ) {
         // Create connection
        $con=mysqli_connect("localhost","root","mypasswordhere","ecom");
        
        if (mysqli_connect_errno()) {
            printf("Connect failed: %s\n", mysqli_connect_error());
            exit();
        }
        
        /* prepare statement */
        if ($stmt = $con->prepare( $statement )) {
            $stmt->execute();
            
            
            /* bind variables to prepared statement */
            $stmt->bind_result($col1, $col2, $col3, $col4, $col5, $col6, $col7);
            
            $arr = array();
            /* fetch values */
            while ($stmt->fetch()) {
                array_push($arr, array( $col1, $col2, $col3, $col4, $col5, $col6, $col7 ));
            }
            return $arr;
        
            /* close statement */
            $stmt->close();
        }
        /* close connection */
        $con->close();
    }

The new way is much easier, and less lines of code. I’ll just use the prepared statements for insert functions, at least for now.
New:

/*
     * function dbConnect
     * 
     */
    function dbConnect( $statement ) {
        $mysqli = mysqli_connect("localhost","root","mypasswordhere","ecom");
        $res = mysqli_query($mysqli,$statement);
        $row = mysqli_fetch_all($res);
        return $row;
    }

Finishing up, here’s the commit!

Spring semester ’14 wrap-up

I learned a lot this semester.

Ben Valentino and I made an Android application called MiddleMan!

I started a summer internship at Catertrax doing front-end web development! I’ll be spending the summer in Rochester. It’s going to be good one.

I’ll leave you with Timescapes, a little side-project I’m working on.