In the Pioneer Cycling store, we used filtering to allow the user to limit the types of parts that are displayed on category_parts.jsp
. The filtering functionality allows the user to filter parts by manufacturer, by frame types that the parts should fit, and by the part’s general price category.
Filtering functionality is composed of three pieces:
The Java Server Page where the filter’s criteria are specified (
search/filtering.jsp
).The
PartsFilterFormHandler
, which accepts the criteria and generates theRuleBasedRepositoryItemGroup
targeting object.The Parts Java Server Page that uses the
RuleBasedRepositoryItemGroupFilter
to display the filtered parts.
The Filtering Page
The filtering page initializes the PartsFilterFormHandler
with filtering criteria including a list of manufacturers, types of frames the parts fit, and general price categories for the parts.
filtering.jsp
:
<dsp:importbean bean="/atg/commerce/catalog/FilterCreator"/> <dsp:importbean bean="/atg/commerce/catalog/RepositoryValues"/> <dsp:importbean bean="/atg/dynamo/droplet/ForEach"/> <dsp:include page="../common/HeadBody.jsp" flush="true"></dsp:include> <dsp:include page="../common/StoreBrand.jsp" flush="true"></dsp:include> <dsp:include page="../catalog/common/breadcrumbs.jsp" flush="true"> <dsp:param name="displaybreadcrumbs" value="true"/> <dsp:param name="no_new_crumb" value="true"/></dsp:include> </span> <p> <span class=storebig>Shop by Filtering</span><br> <span class=help>Filtering allows you to see only parts in the catalog that meet the criteria that you indicate here.</span> <p> <dsp:form action="../catalog/category_parts.jsp" method="POST"> <table width=100% cellpadding=0 cellspacing=0 border=0> <tr> <td colspan=3 class=box-top-store>Filter Catalog</td> </tr> <tr valign=top><td class=box> <dsp:input bean="FilterCreator.searchAllManufacturers" type="radio" value="true"/> Show products from all manufacturers<br> <dsp:input bean="FilterCreator.searchAllManufacturers" type="radio" value="false"/> Show products from only these manufacturers<br> <dsp:select multiple="<%=true%>" bean="FilterCreator.manufacturers" size="4"> <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="product"/> <dsp:param name="propertyName" value="manufacturer"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:param name="sortProperties" value="+id"/> <dsp:oparam name="output"> <dsp:getvalueof id="option76" param="element.id" idtype="java.lang.String"> <dsp:option value="<%=option76%>"/> </dsp:getvalueof><dsp:valueof param="element.id"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <p> <dsp:input bean="FilterCreator.anyFrame" type="radio" value="true"/> Show products that fit any frame<br> <dsp:input bean="FilterCreator.anyFrame" type="radio" value="false"/> Show only products that fit this frame<br> <dsp:select bean="FilterCreator.frameType"> <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="frame-product"/> <dsp:param name="propertyName" value="frameType"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:oparam name="output"> <dsp:getvalueof id="option116" param="element" idtype="java.lang.String"> <dsp:option value="<%=option116%>"/> </dsp:getvalueof><dsp:valueof param="element"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <br> <br> </td> <td class=box> </td> <td class=box> <dsp:input bean="FilterCreator.priceSelectionType" type="radio" value="allPrices"/> Show products of all prices<br> <dsp:input bean="FilterCreator.priceSelectionType" type="radio" value="priceCategory"/> Show products that are priced<br> <dsp:select bean="FilterCreator.priceCategory" size="4"> <dsp:droplet name="RepositoryValues"> <dsp:param name="itemDescriptorName" value="product"/> <dsp:param name="propertyName" value="priceRange"/> <dsp:oparam name="output"> <dsp:droplet name="ForEach"> <dsp:param name="array" param="values"/> <dsp:oparam name="output"> <dsp:getvalueof id="option166" param="element" idtype="java.lang.String"> <dsp:option value="<%=option166%>"/> </dsp:getvalueof><dsp:valueof param="element"/> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> </dsp:select> <br> <P> <%/* Set these hidden params to cause the action to return to the * previous page */%> <input name="navCount" type="hidden" value='<dsp:valueof bean="/atg/commerce/catalog/CatalogNavHistory.navCount"/>'> <input type="hidden" name="navAction" value="jump"> <input type="hidden" name="id" value="cat10005"> <dsp:input bean="FilterCreator.submit" type="submit" value=" Filter Catalog "/> </td> </tr> </table> <p> </dsp:form> </td> </tr> </table> <p> <dsp:include page="../common/StoreFooter.jsp" flush="true"></dsp:include> <dsp:include page="../common/Copyright.jsp" flush="true"></dsp:include> </BODY> </HTML>
filtering.jsp
imports and uses two Nucleus components, FilterCreator
and RepositoryValues
. The RepositoryValues
configuration file sets a single property, the catalog for which products will be filtered.
RepositoryValues
component:
$class=atg.repository.servlet.PossibleValues repository^=CatalogTools.catalog
The FilterCreator
component configures the PartsFilterFormHandler
class’s repository property with the store catalog and sets the itemDescriptor
to product, since parts are products.
FilterCreator
component
$class=atg.projects.b2cstore.PartsFilterFormHandler $scope=session loggingIdentifier=Parts Filter Form Handler repository^=CatalogTools.catalog itemDescriptor=product
The PartsFilterFormHandler
The PartsFilterFormHandler
accepts manufacturer names, frame types, and price categories and generates an atg.targeting.RuleBasedRepositoryItemGroup
, which is a targeting rule set made up of SGML Content Targeting Rules.
The PartsFilterFormHandler
overrides the getRuleRepresentation()
methods of its parent class. This allows it to specify an SGML targeting rule.
Generating the Targeting Rules
The targeting rules are SGML rules that specify logical operations such as “and”, “or”, or “includes” to accept or reject repository items. See the Creating Rules for Targeting Content chapter of the ATG Personalization Programming Guide for more information on targeting rules.
If no filtering criteria have been entered by the user, getRuleRepresentation()
returns a rule that accepts all items:
<ruleset> <accepts> <rule op=any> </rule> </accepts> </ruleset>
When one or more conditions are specified via filtering.jsp
, the PartsFilterFormHandler
generates an “and” rule within the <accepts>
rule that contains one or more child rules that specify the actual filtering criteria:
<ruleset> <accepts> <rule op=and> <rule op=isoneof> <valueof target="manufacturer.id"> <valueof constant="[BatGoose, DZ Supply]"> </rule> <rule op=eq> <valueof target="priceRange"> <valueof constant="EXPENSIVE"> </rule> </rule> </accepts> </ruleset>
The top level <rule op=and>
tag within the <accepts>
tag is necessary because the filtering conditions must be grouped together with an “and” rule. In the above case, the manufacturer.id
should be one of the listed constants and the price range should equal EXPENSIVE
. Without the parent “and” rule, the filtering conditions are grouped together with an “or” rule.
Creating the RuleBasedRepositoryItemGroup
The RuleBasedRepositoryItemGroup
is a Java Targeting object created from the SGML rules that were generated by the PartsFilterFormHandler
. It is later used by the RuleBasedRepositoryItemGroupFilter
to filter an array of parts repository items.
We created the RuleBasedRepositoryItemGroup
within FilterFormHandler
. Within its handleSubmit()
method, the parent class, FilterFormHandler
, retrieves the SGML rules from PartsFilterFormHandler
via the overridden getRuleRepresentation()
method and generates a RuleBasedRepositoryItemGroup
object from those rules. The RuleBasedRepositoryItemGroup
object is available via the getRepositoryItemGroup()
method of the FilterFormHandler
or, in JSP, via the repositoryItemGroup
property.
You can duplicate the functionality of FilterFormHandler
in your own code. The handleSubmit()
method of FilterFormHandler
simply calls the TargeterUtils.createGroup()
method to create the RuleBasedRepositoryItemGroup
.
Filtering and Displaying Parts Repository Items
The RuleBasedRepositoryItemGroupFilter
component takes the RepositoryItemGroup
created by FilterCreator
and an array of repository items and outputs a subset of the input array that contains only those repository items that pass the filter.
ProductsLineItem.jsp Fragment
<dsp:importbean bean="/atg/dynamo/droplet/ForEach"/> <dsp:importbean bean="/atg/commerce/catalog/FilterCreator"/> <dsp:importbean bean="/atg/targeting/RuleBasedRepositoryItemGroupFilter"/> <declareparam name="array" class="java.lang.Object[]" description="An array of repository items"> <%/* Filter the array of repository items in the page param, "array" */%> <dsp:droplet name="RuleBasedRepositoryItemGroupFilter"> <dsp:param name="inputItems" param="array"/> <dsp:param bean="FilterCreator.repositoryItemGroup" name="repositoryItemGroup"/> <dsp:oparam name="output"> <%/* Display each filtered item */%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="filteredItems"/> <dsp:param name="elementName" value="childProduct"/> <dsp:oparam name="outputStart"> <table cellspacing=3 cellpadding=0 border=0> </dsp:oparam> <dsp:oparam name="output"> <tr valign=top> <td> <dsp:valueof param="childProduct.manufacturer.displayName"/> </td> <td> </td> <td colspan=3> <b> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="childProduct"/> <dsp:param name="Image" param="childProduct.thumbnailImage"/> <dsp:param name="DisplayText" param="childProduct.displayName"/> <dsp:param name="navAction" value="push"/> </dsp:include> </b> </td> </tr> <%/* List all child SKUs of the product */%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="childProduct.childSKUs"/> <dsp:param name="elementName" value="childSku"/> <dsp:oparam name="output"> <tr valign=top> <td colspan=2></td> <td> <dsp:valueof param="childSku.displayName"/> </td> <td> </td> <td align=right> <dsp:include page="../common/DisplaySkuPrice.jsp" flush="true"> <dsp:param name="Sku" param="childSku"/> <dsp:param name="Image" param="childSku.thumbnailImage"/> <dsp:param name="navAction" value="push"/> <dsp:param name="DisplayText" param="childSku.displayName"/> </dsp:include> </td> </tr> </dsp:oparam> </dsp:droplet> <tr> <td colspan=5><hr size=0></td> </tr> </dsp:oparam> <dsp:oparam name="outputEnd"> </table> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet>
The ProductsLineItem.jsp
fragment imports the FilterCreator
component to access the parts RuleBasedRepositoryItemGroup
from its repositoryItemGroup
property, and imports the RuleBasedRepositoryItemGroupFilter
servlet bean in order to apply the RuleBasedRepositoryItemGroup
to each repository item. ProductsLineItem.jsp
also declares a page parameter called array
that is set by the caller to the array of parts repository items to display. All the actual filtering work is done up front in the first invocation, RuleBasedRepositoryItemGroupFilter
. As parameters, the array that’s been passed to this page fragment is passed on to the inputItems
parameter in the filter servlet bean, and the RuleBasedRepositoryItemGroup
created by the PartsFilterFormHandler
(described above) is passed in as the repositoryItemGroup
parameter. In the output open parameter, the ForEach
servlet bean iterates the RuleBasedRepositoryItemGroupFilter's
output parameter filteredItems
that contains the list of repository items that have passed the filter.