This design will attempt to accomodate implementation in any environment, but PHP will be the reference implementation. A Java Servlets version is planned.
The reference database is MySQL, which has proven to be a superior technology. This does imply that commercial use of FreeTrade will require purchasing a license. If developers step forward to create an implementation using another database, Working Dogs may treat it as a branch of the entire project, but we are not interested in abstracting all the database code to allow for plugging in database modules. We feel this is a bad trade of speed for compatibility.
This design strives to keep all functionality within the code and out of the database. That is to say, the system will not rely upon stored procedures or custom functions embedded in the database. MySQL has less support for this than other databases anyway.
After performing common initiation code, the assembler determines if an action needs to be performed. The assembler links in the module for performing that action. The results of this action are stored in a global memory space so that the layout module may inform the user of action results.
Next, the assembler determines which page layout module to use based on browser and user preferences. Specifically, that is the type of computer they are using, the browser brand and version, and a set of preferences the may administer. Navigator 3 and Internet Explorer 3 are the minimum, but the system will degrade to be useful on older browsers. The site may take advantage of features of the latest browsers.
The layout module chooses a navigation module based on preferences, which layout it has chosen and the screen to be displayed.
Based on form variables, the assembler determines what screen in the site to serve. Each screen may depend on other form variables in order to determine exactly what content to display. For example, the "project details screen" requires a project number.
Each screen may have some dependency on functional elements common to several pages. An example would be a set of form inputs for getting a date from a user.
FreeTrade
Each module must choose a screen module and a navigation module. The output of these modules is placed in the appropriate position on the page according to the previously discussed page layout designs.
about_us
Input: None
Description: This screen lists all of the relevant information about
your company that the user would like to divulge.
add_attribute
Input:
Description: This screen allows the admin to add an attribute to the specified project. The name, graphic, and display precedence are entered here. User must have administration rights. Others are given an error message.
add_department
Input: Department
Description: This screen allows the admin to create a department. User must have administration rights.
add_item
Input: Department
Description: This form will enable an administrator to enter in all of the
relevant information for an item. User must have administration rights.
add_sku
Input: Item
Description: This form will enable the administrator to enter in all of the
relevant information for a sku. User must have administration rights.
add_variation
Input:
Description: This form enables administrators to create a variation. User must have administration rights.
admin_attribute
Input:
Description: This form enables administrators to set attributes for administration. User must have administration rights.
admin_departments
Input: Department
Description: This screen allows the admin to edit this particular department ($department), including items and subdepartments. It also gives the admin links to move to the parent department, or any of the children departments. Think recursively.
admin_invoice
Input:
Description: This form allows the admin to edit invoice(s). User must have administration rights.
admin_item
Input:
Description: Everything (hopefully) the admin ever wanted for administrating items (including access to the skus). User must have administration rights.
admin_menu
Input: None
Description: Gives administrator a menu of the forms.
admin_tax_rates
Input:
Description: This is a screen that allows the admin to edit the tax rates for each state. User must have administration rights.
contents
Input:
Description: Enables shopper to review contents of basket and provides a link to completely remove an order. Gives the user a link to return to shopping.
create_account
Input:
Login and password.
Description: Allows a shopper to create an account.
department
Input:
Description: This screen queries the database to show departments and items contained in them.
edit_attribute
Input: Item information
Description: This screen allows the administrator to edit an attribute. User must have administration rights.
edit_department
Input: Department information
Description: This screen provides a form for updating a department. User must have administration rights.
edit_ibid
Input: Invoice information
Description: (In invoice_sku, the thing that separates the records is the ID, which has been named ibid "in-basket ID"). This page allows a user to edit that particular record based on the ibid. The In Basket ID represents one sku that will have at least one and possibly more than one quantities in a basket. User must have administration rights.
edit_invoice
Input: Notes regarding invoice
Description: This page allows company personnel to edit invoices. User must have administration rights.
edit_sku
Input: Item information
Description: This is a form for updating a sku. User must have administration rights.
edit_user
Input: User information
Description: This screen allows admin and shopper to edit the information about a particular user. Admins can use this script to edit anyone, user can only use this script to edit information about self.
edit_users
Input: None
Description: This page, in spreadsheet format, allows the admin to see the list of users (ie. people who sign up to buy stuff). The list will have links to edit each user. User must have administration rights.
edit_variation
Input: Item information
Description: This page allows the admin to edit a variation. User must have administration rights.
item
Input:
Description: This page enables the shopper to view details about an item and to add it to the basket.
item_details
Input:
Description: This page enables the shopper to view a large picture of an item in a new browser window.
login
Input: Name and password
Description: This page enables the shopper to log in by form with name and password. The page provides a link for users who haven't registered yet.
order_address
Input: Address
Description: This page enables the shopper to choose a different shipping address item by item.
order_billing
Input: Credit card information, address
Description: This page enables the shopper to input billing information.
order_confirm
Input:
Description: This page shows the final order information and asks for confirmation from the shopper.
order_history
Input:
Description: This page enables the shopper to view the invoices for this user. Each invoice should have a link to allow the user to view that particular invoice. Notice, this file is 'conspicuously' similar to admin_invoice, which is the Admin version of this script.
order_info
Input: Shipping information
Description: This page enables the shopper to view the basket and input shipping information.
order_thankyou
Input: None
Description: Gives a thank you message for placing an order.
order_view_ibid
Input:
Description: (In invoice_sku, the thing that separates the records is the ID, which has been named ibid "in-basket ID"). The user can view this screen during the order confirmation process (for instance, he may want to check to see that a particular item is being shipped to the correct place).
relate_item
Input:
Description: This page allows an administrator to relate an item to other items. User must have administration rights.
report_inventory
Input:
Description: This page gives the admin an inventory report. User must have administration rights.
search_form
Input: Description or keyword
Description: This page enables the shopper to perform a search by item or over the entire site.
search_results
Input:
Description: This page enables the shopper to view the results of a search.
view_ibid
Input:
Description: (In invoice_sku, the thing that separates the records is the ID, which has been named ibid "in-basket ID"). This page allows a user to view that particular record based on the ibid.
view_invoice
Input:
Description: This page enables the shopper to view one of his past invoices. Checks to make sure userID corresponds to this invoice's userID.
welcome
Input:
Description: This is the default screen for a FreeTrade site.
wishlist
Input:
Description: This page enables the shopper to view the wishlist and move items to the basket for checkout.
The titles in this section are the exact names of both the actions and their filenames. A short description is followed by a list of expected input fields. Security for these actions needs to match the checks made on the screens they are called by. In most cases the actions simply perform a database function like adding or updating a row.
ADD_ATTRIBUTE
Add an attribute to the database. Only an administrator may add an attribute.
ADD_BASKET
This action adds an item to the basket.
ADD_DEPARTMENT
This action adds a department.
ADD_FROM_WISHLIST
This action adds a sku from the wishlist, then removes it from
wishlistinput_division_name. This is accomplished by adding the product to a
basket through a query which will input the Session, SKU, Quantity, and Notes.
Once the query is executed, the item(s) will then be added to the basket, and the
second query, which will remove the item from the wishlist, begins. The query
simply deletes the relevent data from the user_sku table.
ADD_ITEM
This action adds an item to the database. Only administrators may add items. When adding
an item there are a couple of things to consider: The user must select a
department and give the item a name. Any other entries are optional. Then a query
is run to determine if there are any duplicate items. The item must
then be added to the item record.
ADD_SKU
This action adds a SKU to an item. Only administrators may add skus.
ADD_STATUS
This action allows company personnel to add records to the invoice_status table in connection with a particular invoice.
ADD_USER
This action adds a user to the system.
ADD_VARIATION
This action adds a variation to the database. The user must have admin rights.
CLEAN_INVOICE
This action removes shipping/billing records for active invoice. This is done through a
series of steps that are executed in various queries: get the ID of the
incomplete invoice for this user, get shipping IDs for the invoice, delete
addresses, delete shipping, delete billing, and update shipping in invoice_sku.
CLEAN_INVOICE_FULL
This action removes shipping/billing/fees for active invoice. Similar to the clean_invoice
module, this is done through a series of steps that are executed in various
queries: get the ID of the incomplete invoice for this user, erase previous
fees, get shipping IDs for the invoice, delete addresses, delete shipping,
delete billing, and update shipping in invoice_sku.
CREATE_INVOICE
This action will create a blank invoice, with no SKUs, and set to inactive. The user
will create an invoice row that will enable the user to enter relevant information.
DELETE_ATTRIBUTE
This action removes an attribute from the database. In order to completely remove the
attribute it has to be removed from the attribute table along with the
sku_variation table.
DELETE_DEPARTMENT
This action removes a department and all items/SKUs in the department. When first running
this module, it will determine whether the department variable is set. Once it
is determined, it will check to see whether or not the department exists and
whether or not there are items in the department. If there are items, then it
will not continue and will display the appropriate message. If there are no
items, the department will be deleted.
DELETE_ITEM
This action Removes an item and all SKUs that are related to the respective item. A series
of queries are executed to ensure that all relevant SKUs are deleted to its
corresponding item.
DELETE_SKU
Removes a SKU from both the SKU table and the inventory table.
DELETE_VARIATION
This action Removes a variation from the variation and sku_variation tables respectively by deleting the attribute and the sku_variation.
GENERATE_SID
This action creates a sid and creates a row in the session table. The sid is
randomly generated from a defined charpool.
LOGIN_USER
This action will verify the user and create a session. To verify the user it
will then check whether the user is allowed to login and whether the password
given is correct.
LOGOUT_USER
This action will remove the user from the session table.
PURGE_INVOICE
This action will delete a user's incomplete invoice, all SKUs, and all address
for SKUs. This does not require admin rights.
REMOVE_FROM_WISHLIST
This action will remove a SKU from the users wishlist.
SAVE_BASKET
This action saves all SKUs in the basket to the wishlist.
SET_SHIPPING
This action sets shipping address for given SKUs on the incomplete invoice.
SET_SHIPPING_ALL
This action sets shipping address for all SKUs on the incomplete invoice. There will be
three main functions executed: createAddress, createShipping, and ShippingByTotal.
UPDATE_VARIATION
This action updates information about a variation.
UPDATE_TAX_RATES
This action allows the admin to update the tax rates for all of the states. Expected Input: Array, Rates, TaxShipping
UPDATE_SKU
This action updates a SKU. The following valid data is required:
UPDATE_SHIPPING
This action updates the contents of one record from the "shipping" table.
Expected input:
UPDATE_LOGIN_PASSWORD
This action updates the login and password in the "user" table.
Expected input:
UPDATE_ITEM
This action updates information about an item.
UPDATE_INVOICE_SKU
This action updates quantity and giftwrap for items on an invoice.
UPDATE_IBID
This action updates the contents of one of the records in the "invoice_sku" table.
Expected input:
UPDATE_DEPARTMENT
This action updates information about a department.
Expected input:
UPDATE_BASKET
This action updates the quantities for basket.
UPDATE_ATTRIBUTE
This action updates information about an attribute
Expected input:
UPDATE_ADDRESS
This action updates an address record in the "address" table.
Expected input:
SUBMIT_ORDER
This action creates an invoice. Here the invoice is set active as the lists of respective
SKUs are retrieved. At the end of the function a confirmation email is then
sent.
VALIDATE_ORDER
This action checks the billing info for validity, then adds it to the invoice. Here, we're only
checking that the credit card number is valid, not authorizing the charge. The
three main functions that will be executed are the "createAddress", "get credit
card validation", and "Is everything okay to proceed" function.
CREATE TABLE attribute ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), Graphic varchar(255), DisplayPrecedence int(11), PRIMARY KEY(ID) ); CREATE TABLE billing_method ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), DisplayPrecedence int(11), PRIMARY KEY(ID) ); INSERT INTO billing_method VALUES (1,'Visa',1); INSERT INTO billing_method VALUES (2,'Mastercard',2); INSERT INTO billing_method VALUES (3,'American Express',3); CREATE TABLE department ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), Graphic varchar(255), Parent int(11), DisplayPrecedence int(11), PRIMARY KEY(ID) ); #test data INSERT INTO department VALUES(1, 'Books', 'books.gif', 0, 1); INSERT INTO department VALUES(2, 'Music', 'music.gif', 0, 2); INSERT INTO department VALUES(3, 'Videos', 'video.gif', 0, 3); INSERT INTO department VALUES(4, 'Hardcover', 'hardcover.gif', 1, 1); INSERT INTO department VALUES(5, 'Softcover', 'softcover.gif', 1, 2); INSERT INTO department VALUES(6, 'VHS', 'vhs.gif', 3, 1); INSERT INTO department VALUES(7, 'DVD', 'dvd.gif', 3, 2); INSERT INTO department VALUES(8, 'CD', 'cd.gif', 2, 1); INSERT INTO department VALUES(9, 'Vinyl', 'vinyl.gif', 2, 2); INSERT INTO department VALUES(10, 'Cassette', 'cassette.gif', 2, 3); CREATE TABLE department_item ( Department int(11), Item int(11) ); CREATE TABLE invoice ( //This is hopelessly out of date. ID int(11) NOT NULL AUTO_INCREMENT, Bill_FirstName varchar(255), Bill_LastName varchar(255), Bill_Company varchar(255), Bill_Address1 varchar(255), Bill_Address2 varchar(255), Bill_City varchar(255), Bill_State varchar(255), Bill_Country varchar(255), Bill_ZIP varchar(255), Bill_Method int(11), Bill_CreditCardNumber varchar(255), Bill_CreditCardExpiration varchar(255), Order_ItemCost float(11,2), Order_Tax float(11,2), Order_Shipping float(11,2), Order_Created datetime, Order_Shipped datetime, Order_OnHold datetime, Order_OnHoldComment blob, Ship_FirstName varchar(255), Ship_LastName varchar(255), Ship_Company varchar(255), Ship_Address1 varchar(255), Ship_Address2 varchar(255), Ship_City varchar(255), Ship_State varchar(255), Ship_Country varchar(255), Ship_ZIP varchar(255), Ship_Method int(11), User int(11), PRIMARY KEY(ID) ); CREATE TABLE invoice_sku ( Invoice int(11), SKU int(11), Quantity int(11), ExternalSKU varchar(255), Name varchar(255), ListPrice float(11,2), SalePrice float(11,2), Shipping float(11,2) ); CREATE TABLE item ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), Description blob, Keywords blob, Thumbnail varchar(255), Graphic varchar(255), LargeGraphic varchar(255), DisplayPrecedence int(11), Active char(1), PRIMARY KEY(ID) ); CREATE TABLE item_item ( Item int(11), Related_Item int(11), Relationship int(11) ); CREATE TABLE relationship ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), DisplayPrecedence int(11), PRIMARY KEY(ID) ); CREATE TABLE sku ( ID int(11) NOT NULL AUTO_INCREMENT, Item int(11), ExternalSKU varchar(255), Name varchar(255), ListPrice float(11,2), SalePrice float(11,2), Shipping float(11,2), DisplayPrecedence int(11), Active char(1), PRIMARY KEY(ID) ); CREATE TABLE sku_variation ( SKU int(11), Variation int(11) ); CREATE TABLE seed ( seed int(11) ); INSERT INTO seed VALUES (123123123); CREATE TABLE session ( ID varchar(24) NOT NULL, User int(11), LastAction datetime, PRIMARY KEY(ID) ); CREATE TABLE session_sku ( Session varchar(24), SKU int(11), Quantity int(11) ); CREATE TABLE shipping_method ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), DisplayPrecedence int(11), PRIMARY KEY(ID) ); INSERT INTO shipping_method VALUES (1,'USPS',1); INSERT INTO shipping_method VALUES (2,'UPS',2); INSERT INTO shipping_method VALUES (3,'FedEx',3); CREATE TABLE user ( ID int(11) NOT NULL AUTO_INCREMENT, FirstName varchar(255), LastName varchar(255), Login varchar(255), Password varchar(255), Email varchar(255), Company varchar(255), AddressLine1 varchar(255), AddressLine2 varchar(255), City varchar(255), State varchar(255), Country varchar(255), ZIP varchar(255), Phone varchar(255), Fax varchar(255), PRIMARY KEY(ID) ); CREATE TABLE variation ( ID int(11) NOT NULL AUTO_INCREMENT, Name varchar(255), Attribute int(11), Description varchar(255), Graphic varchar(255), DisplayPrecedence int(11), PRIMARY KEY(ID) ); CREATE TABLE permission ( ID INT NOT NULL AUTO_INCREMENT, Name VARCHAR(255), PRIMARY KEY(ID) ); INSERT INTO permission VALUES (1, 'Login'); INSERT INTO permission VALUES (2, 'Administrate'); CREATE TABLE user_permission ( User INT, Permission INT );
/* ** File: test ** Description: This is a test program ** Version: 1.0 ** Created: 1/1/2000 ** Author: Leon Atkinson ** Email: leon@clearink.com ** ** Copyright (c) 1999 Working Dogs. All rights reserved. */
Every function should have a block comment specifying name, input/output, and what the function does.
/*
** Function: doAdd
** Input: INTEGER a, INTEGER b
** Output: INTEGER
** Description: Adds two integers
*/
function doAdd($a, $b)
{
return(a+b);
}
Ideally, every while, if, for or similar
block of code should be preceded by a comment explaining what happens in the block.
Sometimes this is unnecessary.
// get input from user char by char
while(getInput($inputChar))
{
storeChar($inputChar);
}
Explain sections of code that aren't obvious.
//TAB is ASCII 9
define(TAB, 9);
// change tabs to spaces in userName
while($index=0; $index < count($userName); $index++)
{
$userName[$index] = ereg_replace(TAB, " ", $userName[$index]);
}
Function Declarations
As previously stated, functions should have a comment block explaining what they
do and their input/output. The function block should align starting at one tab
from the left margin unless the function is part of a class definition.
Opening and closing braces should also be one tab from the left margin. The
body of the function should be indented two tabs.
<?php
/*
** doAdd
** Adds two integers
** Input: $a, $b
** Output: sum of $a and $b
*/
function doAdd($a, $b)
{
return(a+b);
}
?>
Compound Statements
Flow control primitives (if, while, for, ...) should be compound statements,
even if they only contain one instruction. Like functions, compound statements
should have opening braces that start at column zero relative to scope.
Code within the braces forms a new scope and should be indented.
// tell the user if a is equal to ten
if($a==10)
{
printf("a is ten.\n");
}
else
{
printf("a is not ten.\n");
}
Naming
The names of variables, constants, and functions are to begin with a lowercase
letter. In names which consist of more than one word, the words are written
together and each word starts with an uppercase letter.
define("EMAIL_FROM", "Webmaster <webmaster@$SERVER_NAME>");
//text with which to label the field
define("ADDR_LABEL", 0);
//name of the form field (sans prefix of course)
define("ADDR_VAR", 1);
//error message to display for missing fields
define("ADDR_ERROR", 2);
/*
** Function getAddressFromEnvironment
** Input: $Prefix - prefix used to generate address form
** Return: array suitable for addressFields
*/
function getAddressFromEnvironment($Prefix)
{
global $AddressInfo;
//get list of all address fields
//from the AddressInfo array
reset($AddressInfo);
while(list($field, $info) = each($AddressInfo))
{
$ReturnValue[$field] = trim($GLOBALS[($Prefix . $info[ADDR_VAR])]);
}
return($ReturnValue);
}
$AddressInfo = array(...); for($i=0; $i < count($list); $i++)
Constants
Values that are treated as constants, that is, not changed by the program, should
be declared in the beginning of the scope in which they are used. In PHP this
is done with the define function. Each of these constants should be paired with
a comment that explains use. They should be named exclusively with uppercase
letters, with underscores to separate words. You should use constants in place of
any arbitrary values to improve readability.
// maximum length of a name to accept
define("MAX_NAME_LENGTH", 32);
print("Maximum name length is " . MAX_NAME_LENGTH);
Variables
Variables are to be declared with the smallest possible scope. This means using
function parameters when it's appropriate.
Long Lines
Lines should not exceed 78 characters. Break long lines at common separators
and align the fragments in an indented block.
if(($size < 0) OR
($size > max_size) OR
(isSizeInvalid($size)))
{
print("Invalid size");
}
Expressionsif..elseif..else is usually more readable.
Looping over associative arrays
//print all address fields
reset($AddressInfo);
while(list($key, $value) = each($AddressInfo))
{
print("$key is $value<br>\n");
}
Efficient Database Queries
The mysql_result function should not be used due to it's inefficiency.
The mysql_fetch_row has been used extensively, but mysql_fetch_object
may be the preferred method going forward.