Chapter 5 [ 123 ] Let's look at this as a step-by-step process: 1. We query the database for attribute types. 2. We cache the results of this query. 3. The cache is associated with a template tag. (This allows the template engine to generate a list of attribute types, and for each attribute type, it can build an empty list, surrounded by template tags, which will eventually contain the attribute values.) 4. We query the database for all attribute types, ordering by their own order. (Although the order is their order within their group, this does not matter, as we lter them out.) 5. We iterate through the results, putting each value into an array for its corresponding attribute type. 6. For each attribute type, we cache the array, and assign it to a template tag, allowing each group of values to populate the appropriate list for the attribute type. Our modied controller now looks like this, with our aforementioned six steps commented in for reference: private function generateFilterOptions() { // 1. Query the database for attribute types $attrTypesSQL = "SELECT reference, name FROM product_filter_attribute_types"; $this->registry->getObject('db')->executeQuery( $attrTypesSQL ); if( $this->registry->getObject('db')->numRows() != 0 ) { $attributeValues = array(); $attributeTypes = array(); while( $attributeTypeData = $this->registry-> getObject('db')->getRows() ) { $attributeValues[ $attributeTypeData['reference'] ] = array(); $attributeTypes[] = array( 'filter_attr_reference' => $attributeTypeData['reference'], 'filter_attr_name' => $attributeTypeData['name'] ); } // 2. cache the results of this query $attributeTypesCache = $this->registry->getObject('db')-> cacheData( $attributeTypes ); // 3. The cache is associated with a template tag $this->registry->getObject('template')->getPage()-> This material is copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Enhancing the User Experience [ 124 ] addTag( 'filter_attribute_types', array( 'DATA', $attributeTypesCache ) ); // 4. We query the database for all attribute types, // ordering by their own order $attrValuesSQL = "SELECT v.name AS attrName, t.reference AS attrType, v.ID AS attrID FROM product_filter_attribute_values v, product_filter_attribute_types t WHERE t.ID=v.attributeType ORDER BY v.order ASC"; $this->registry->getObject('db')->executeQuery( $attrValuesSQL ); if( $this->registry->getObject('db')->numRows() != 0 ) { // 5. We iterate through the results, putting each value into // an array for its corresponding attribute type. while( $attributeValueData = $this->registry->getObject('db')-> getRows() ) { $data = array(); $data['attribute_value'] = $attributeValueData['attrName']; $data['attribute_URL_extra'] = 'filter/' . $attributeValueData['attrType'] . '/' . $attributeValueData['attrID']; $attributeValues[ $attributeValueData['attrType'] ][] = $data; } } // 6. For each attribute type, we cache the array, and assign it // to a template tag, allowing each group of values to // populate the appropriate list for the attribute type. foreach( $attributeValues as $type => $data ) { //echo '<pre>' . print_r( $attributeValues, true ) . '</pre>'; $cache = $this->registry->getObject('db')->cacheData( $data ); $this->registry->getObject('template')->getPage()-> addPPTag( 'attribute_values_' . $type, array( 'DATA', $cache ) ); } } } This material is copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Chapter 5 [ 125 ] Processing filter requests With the relevant database structure in place, and functionality available for our customers to select attributes for which they wish to lter their product viewings, we need a method to process the request and actually lter the products listing. This involves iterating through the bits within the URL, and for every instance of filter found, storing the following two values. Once all bits of the URL have been processed, the saved bits should be processed to build a suitable query to lter the products. We will need some variables within our controller to store some of the data we will be processing. These would include: An array containing the lter attribute types, so we can pass the components of the URL to it in order to determine if the attribute value is from part of the products table itself, or if it is from an attribute association An array containing the lter attribute values, so when we nd an attribute type that refers to the products table, we can get the upper- and lower-bound values for this An array of pieces of SQL to search for attribute associations An array of pieces of SQL to search for attribute values within the products table A counter for the number of lters by association, as we will group this part of the search into a subquery, returning the results of a count, and we will know if we have a match if the count matches the number of conditions to the subquery These variables are displayed as follows: // Filter count: to count how many attributes by association // must match private $filterCount=0; // SQL statement parts where products are associated with // attributes private $filterAssociations = array(); // SQL statement parts where products are filtered by their own // direct properties i.e. price, weight. private $filterDirect = array(); // Array of filter attribute types private $filterTypes = array(); // Array of filter attribute values private $filterValues = array(); // our SQL statement for filtered products private $filterSQL = ''; • • • • • This material is copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Enhancing the User Experience [ 126 ] We now need a function to search through the URL and another function to add query pieces to our various arrays when it is passed the lter type and lter value once an occurrence of the word filter is found in the URL. So, rstly we'll see a function to go through the URL. /** * Generate an SQL statement for filtering products, based on URL * paramaters * @param array $bits the bits contained within the URL * @return void */ We rst get all of the attribute types available, and then we get all of the attribute values. private function filterProducts( $bits ) { // get our attribute types $attributeTypesSQL = "SELECT ID, reference, name, ProductContainedAttribute FROM product_filter_attribute_types "; $this->registry->getObject('db')->executeQuery( $attributeTypesSQL ); while( $type = $this->registry->getObject('db')->getRows() ) { $this->filterTypes[ $type['reference'] ] = array( 'ID' => $type['ID'], 'reference'=>$type['reference'], 'ProductContainedAttribute'=> $type['ProductContainedAttribute'] ); } // get our attribute values $attributeValuesSQL = "SELECT ID, name, lowerValue, upperValue FROM product_filter_attribute_values"; $this->registry->getObject('db')-> executeQuery( $attributeValuesSQL ); while( $value = $this->registry->getObject('db')->getRows() ) { $this->filterValues[ $value['ID'] ] = array( 'ID' => $value['ID'], 'name' => $value['name'], 'lowerValue' => $value['lowerValue'], 'upperValue' => $value['upperValue'] ); } This material is copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Chapter 5 [ 127 ] For each part of the URL, we go through and nd anything that relates to the lter functionality, which is of the format filter/attribute-type/attribute-value. // process the URL foreach( $bits as $position => $bit ) { // if we find filter in the URL if( $bit == 'filter' ) { // send the next two bits to the addToFilter method $this->addToFilter( $bits[ $position+1], $bits[ $position+2] ); } } We assume there are no lter requests being made, and set the basic lter query. Then we check if we have any lters that are not based on the product table values; if there are, we set the somethingToFilter variable, then we do the same for the lters based on the product table values. Each lter found adds additional restrictions to the basic lter SQL query. // assume no filter requests $somethingToFilter = false; // basic filter query $sql = "SELECT p.price AS product_price, v.name AS product_name, c.path AS product_path FROM content c, content_types t, content_versions v, content_types_products p WHERE v.ID=c.current_revision AND c.active=1 AND p.content_version=v.ID AND t.reference='product' AND c.type=t.ID "; if( !empty( $this->filterAssociations ) ) { // we have some filter requests $somethingToFilter = true; // build the query $sqla = " AND ( SELECT COUNT( * ) FROM product_filter_attribute_associations pfaa WHERE ( "; $assocs = implode( " AND ", $this->filterAssociations ); $sqla .= $assocs; $sqla .= " )AND pfaa.product = c.ID )={$this->filterCount}"; $sql .= $sqla; } if( !empty( $this->filterDirect ) ) { // we have some filter requests This material is copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 . and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Chapter 5 [ 1 25 ] Processing filter requests With the relevant database structure in place,. copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Chapter 5 [ 127 ] For each part of the URL, we go through and nd anything that relates. material is copyright and is licensed for the sole use by jackie tracey on 23rd February 2010 953 Quincy Drive, , Brick, , 08724 Enhancing the User Experience [ 124 ] addTag( 'filter_attribute_types',