Patrick64 / reefine

ExpressionEngine addon that filters and searches entries

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reefine - ExpressionEngine addon

Reefine is an ExpressionEngine add-on that allows the user to search, filter and refine channel entries (eg products) on your site. It is aimed at ecommerce sites but can be used for sorting any kind of data stored in channel entries.

Sponsors

Support the development of this project by becoming a sponsor or hire me for your software project

Table of Contents

Installation

Make sure your system meets the minimum requirements:

First download and extract the Reefine ZIP file. There are folders, ee2, ee3, ee4 and ee5.

Install Reefine 1.x (ExpressionEngine 2)

  1. Download and extract the ee2 folder in the Reefine ZIP file.
  2. Upload the third_party/reefine folder to system/expressionengine/third_party/
  3. Upload the themes/third_party/reefine folder to themes/third_party/
  4. Install the module in Add-Ons → Modules
  5. Check the ee2/docs folder for more instructions on Reefine 1.x

Install Reefine 2.x or 3.x (ExpressionEngine 3, 4 & 5)

  1. Download and extract the Reefine ZIP file. Go into the ee3/ee4/ee5 folder depending on your ExpressionEngine version
  2. Upload the /system/user/addons/reefine folder to /system/user/addons/reefine
  3. Upload the /themes/user/reefine folder to /themes/user/reefine
  4. Click Developer → Add-on Manager. Scroll down to "Third Party Add-Ons" and click install next to Reefine.

Hello world

This is a small tutorial to create a clothing shop filter. View a live demo.

  1. Create a Channel called "clothes"
  2. Create a Field group called "clothes"
  3. Add a field with short name "size" with the type "select dropdown" and add values Small, Medium, and Large.
  4. Add three more fields with short names "product_type", "colour" and "price" with the type "text"
  5. Add another field with short name "product_image" with type "file".
  6. Associate the field group with the channel you created.
  7. Add some entries to your channel, ensure that you add an image to product_image and the price field contains just a number, the other fields can be anything you want.
  8. Create a new template in a template group. (eg shop/search )
  9. Paste following code into template (Github Gist). Then go to your template and see the results.

Parameters

parse

This must be set to inward for the {entries} tag works correctly.

parse="inward"

Important: This is required for Reefine to work properly

channel

Specify a list of channels to search through, seperated by pipe |.

channel="food|clothes|cutlery"

url

This specifies the structure of your url. It must show where each filter group goes in the url. The filter group is specified in curly braces. Each filter group must be seperated by characters that are unlikely to appear in the filter.

This example will produce an url of mypage followed by a search for title and then colour. For example if the user filters by title mugs and colour red it will produce /mypage/mugs/red

fields="title|colour" url="mypage/{title}/{colour}"

If the user does not specify a title it will replace it with the text "any". You can override this behaviour by specifying the any text after a pipe. For example this will produce mypage/any-title/any-colour if no filters are selected.

url="mypage/{title|any-title}/{colour|any-colour}"

If the user searches by multiple filters for a filter group you can also specify the text that will join them. By default it is "-or-" (for join="or" filter groups), or "-and-" (for join="and" filter groups). This applies to filter groups of type text only. For example this will product mypage/mugs/red-otherwise-green/ if the user filters by title mugs and colour red or green.

url="mypage/{title|any-title}/{colour|any-colour|-otherwise-}"

For filters of the type number_range the url by default is "any" for no filter, "-to-" as a seperator between the range of values, "at-least-" if only the min value is specified and "at-most-" if only the max value is specified. You can override these using these pipe seperated values:

url="mypage/{title}/{colour}/{price|any|-to-|at-least-|at-most-}"

You don't need to seperate filter groups by a slash you can use any text you want. This will produce urls such as buy-some-delightful-things-today or buy-some-green-mugs-today

url="buy-some-{colour|delightful}-{title|things}-today"

Important:  If you are using method="url" (default) then this is required and all filter groups must be specified in the url.

url_output

Default: same as "url" parameter

Use this if you need to specify a different url for output to links. This is useful if you want to link to a new page on filtering or if you're using a language indicator in the first segment that isn't sent through to expressionengine. Reefine will still use the "url" parameter to get the current filter values.

url="/{colour}/{price}" url_output="/shop/{colour}/{price}"

theme

You can specify a theme to use which will output the filters selectors to save you having to create them yourself. Themes are found in the /themes/third_party/reefine folder. Reefine comes with the themes "shop" and "form". The "form" theme is compatible with all methods whereas the "shop" theme is compatible with all methods except for method="post".

theme="shop"

You can create your own themes by copying the theme from the /themes/third_party/reefine folder, and then reference your new theme using the directory name. For example /themes/third_party/reefine/mytheme would be:

theme="mytheme"

The theme parameter is optional - you can specify all html & css in the template itself. See the code samples for an idea of how this works.

method

Default: url

This is the method by which the user can interact with Reefine. The supported methods are url, ajax, get and post. The method must be supported by your theme / template code. The existing themes "shop" and "form" will read the method used and adapt accordingly. If you are using your own theme or template code you should note the following:

  • method="url" (default) allows the user to filter by using links. You must provide the url parameter for this to be used. You can use links with filter url or a form. If you are using a form it will automatically redirect to the correct URL when the form is submitted, you will also need to add <input type="hidden" name="form\_post" value="yes"/> which fixes a problem where the user can submit a form with no filters selected.
  • method="ajax" will support returning Reefine as an AJAX request. You can use a form or filter the url links. If you add ajax_request=1 to the get/post request it will return just the form on it's own without the rest of the template. If you are using just template code to or making your own theme from scratch you will need to use javascript / jQuery to handle the AJAX request. Refer to themes/third_party/reefine/shop/ajax.js or themes/third_party/reefine/form/ajax.js for examples.
  • method="get" will allow for submitting the request via the GET method (so it appears in the URL eg http://www.example.com/products?colour=red ). You can use a form <form method="get" ...> or by using filter the url links.
  • method="post" will allow for submitting the request via POST eg <form methd="POST" ... >. Unless you have disabled secure forms you will need to add to your form as well. This method does not support filter the url links, you will need to use a form.

author_id

Pipe separate list of author IDs. This parameter can restrict the entries shown to just these author IDs.

author_id="1|3"

disable_search

Default: no

You can add the parameter disable_search="yes" which will make Reefine ignore any filter values in the URL or post. This is useful for showing all filter options on a subpage. For example if you are on the product's page (http://www.example.com/products/items/red-trousers) you don't want Reefine to pick up the segments "items/red-trousers" so you add disable_search="yes" to ignore them:

<nav> {exp:reefine channel="clothes" parse="inward" theme="shop" disable_search="yes"  
filter:fields="title|product_type|size|colour|price" filter:price:type="number_range"  
url="/products/{product_type}/{size}/{colour}/{price}/{title}"}  
{/exp:reefine} </nav>  
  
<article>{exp:channel:entries channel="clothes"} <h1>{title}</h1> <p>{content}</p> {/exp:channel:entries}</article>

disable_multiple_selected_groups

Default: no

Make it so users can only select one filter group at a time (useful for performance problems).

disable_multiple_selected_groups="yes"

fix_pagination

Default: no

If you're using Structure and/or freebie you may find the pagination doesn't work correctly. Set this to yes to try and fix this.

fix_pagination="yes"

site

Specify the site ID if using Multi site manager.

site="1"

search:field name

You can search by a particular field value or the title. This works in the same way as the channel:entries tag. Please refer to the documentation on search:field_name for channel entries in the ExpressionEngine documentation. Additionally you can use this to search the title with search:title="abc".

search:colour="red"

category

This parameter will limit all your entries to a particular category by it's category ID. You can include multiple categories seperated by pipe "|".

category="2|3"

You can also exclude categories using not:

category="not 2|3"

category_url

This parameter will limit all your entries to a particular category. This uses the category url title.

category_url="{segment_2}"

fixed_order

Pipe separated lists of entry ids that will allow you specify the exact order of the entry_ids go in. You will need to put this in the fixed_order for {exp:channel:entries} for example:

{exp:reefine fixed_order="1|2|3|4" ...snip...}  
{entries}  
{exp:channel:entries entry_id="{entry_ids}" fixed_order="{entry_ids}" dynamic="no" } ... snip ... {/exp:channel:entries}  
{/entries}  
{/exp:reefine}

show_expired

Default: no

Show expired entries. You will also need to add this to your {exp:channel:entries} tag.

show_expired="yes"

show_future_entries

Default: no

Show entries with an entry date that is in the future. You will also need to add this to your {exp:channel:entries} tag.

show_future_entries="yes"

status

Default: open

This will limit the entries by their status. Multiple statuses are seperated by a pipe character "|".

status="featured|on_offer"

Important: The containing exp:channel:entries tag will also need to have the same status tag, otherwise the filter and search results will differ.

filter:fields

Pipe | seperated list of fields to filter by. For each field it will assume some defaults, such as specifying "title" will assume type is "search", a checkboxes/multiselect field will assume delimiter="|". You can then change some of attributes of the filter by referencing filter:field name:... as below. Each field will then be a filter group.

filter:fields="title|product_type|colour"

filter:filter group name

You can specify extra information about filter groups by using a parameter in the format filter:filter group name:variable. Some examples:

filter:search:fields="title|product_type|colour"  
filter:search:category_group="3"  
filter:search:type="search"`

filter:price:fields="price"  
filter:price:type="number_range"

filter:colour:fields="colour"  
filter:colour:delimiter="|"  
filter:colour:label="Paint Colour"  
filter:colour:join="or"  
filter:colour:orderby="active"  

filter:filter group:type

Default: list

Specify the different filter group types which enables different features of Reefine. For example:

filter:price:type="number_range"  
filter:search:type="search"  
filter:colour:type="list"

Note:  Not related to the field types in ExpressionEngine.

filter:filter group:type="list"

This is the default method. It will show a list of all possible value of the fields specified with the quantity. Also see {list_groups}

filter:filter group:type="search"

Search is for text searches with a text box. It will search each word individually. Also see {search_groups}

filter:search:fields="title|product_type|colour"  
filter:search:type="search"  

filter:filter group:type="number_range"

The number range allows the user to specify a minimum and maximum value to filter by. This is useful for price ranges. It will output extra information in the filter variables pair and the tag {number_range_groups}

filter:price:fields="price"  
filter:price:type="number_range"  

filter:filter group:type="month_list"

This type will allow the user to filter the entries by a list of months. This example if for a channel with the Date fields event_from (the date the event starts) and event_to (the date the event ends, this is useful in case the event covers more than one month). Also notice the filter:month:where_after parameter will hide all months that have passed. You can also use where_before to hide months in the future. The fields entry_date and expiration_date are also supported.

filter:month:type="month_list"  
filter:month:fields="event_from|event_to"  
filter:month:where_after="{current_time}"  

filter:filter group:type="tree"

This type will show categories with subcategories in a tree format, much like how ExpressionEngine shows them in the control panel. Also see {tree_groups}

filter:genre:category_group="2"  
filter:genre:type="tree"  

filter:filter group:fields

Pipe | seperated list of fields to filter by. Also includes title, entry_date, expiration_date and status.

filter:search:fields="title|product_type|colour"

Important:  If one of the fields is of type checkboxes or multiselect list you must specify filter:filter group:delimiter="|"

filter:filter group:label

The label for the filter group.

filter:colour:label="Paint Colour"

filter:filter group:locale

Default: Server's default locale

Applies to number_range groups only. Locale in which the number would be formatted (locale name, e.g. en_CA). This effects how the number is parsed and displayed. This does not effect how the number appears in the URL.

filter:price:locale="de_DE"

filter:filter group:show_empty_filters

Whether to output filters that have no matches in the current search. Value can be yes or no, default is "no".

filter:colour:show_empty_filters="yes"

filter:filter group:custom_titles

filter:filter group:custom_values

For making a filter group with fixed values or adding some hard coded values to a filter group. This is usful for creating ways for the user to change orderby or number of pages. You can then use {active_filter_values} to access the selected value. Text is pipe separated and each item in custom_values has the repsective title in custom_titles, i.e. the first item in custom_values (eg title) has the title of the first item in custom_titles (eg Product Name) and so on.

filter:orderby:custom_titles="Product Name|The Price"  
filter:orderby:custom_values="title|price"  
filter:orderby:default="price"  
filter:orderby:show_separate_only="yes"  
filter:orderby:join="none"

filter:filter group:default

Default selected value for this filter group. If using categories this would be the category short name.

filter:postage:default="first_class"

filter:filter group:delimiter

This is the delimiter to use for fields that contain more than one value. By default ExpressionEngine uses the pipe character |. You should specify filter:filter group:delimiter="|" when the filter group contains a field that is a checkboxes or multiselect.

filter:store:delimiter="|"

filter:_filter group:_join

Possible options are:

  • or (default)
  • and
  • none

This allows you specify how multiple filters are treated. If the join is or reefine will show an entry if it matches at least one filter in the filter group. If the join is and reefine will show an entry if it matches all filters in the filter group. If the join is none reefine will only allow the user to select one filter for that filter group.

filter:colour:join="or"

filter:_filter group:_orderby

This specifies the order that the filters are displayed in the filter group. Possible values are:

  • value (default, order alphabetically by the text value of the filter)
  • quantity (order by the quantity of matching entries for filter, most to least)
  • active (put active filters at top and order the rest alphabetically)
  • active_quantity (put active filters at top and order the rest by quantity)

You can also specify a pipe "|" seperated list of filter values to set your own custom order.

filter:colour:orderby="active_quantity"  
filter:city:orderby="london|bristol|cheltenham|plymouth"

filter:_filter group:_sort

Default: asc

This specifies the direction of order that the filters are displayed in the filter group. It can be "asc" for ascending or "desc" for decending.

filter:colour:sort="desc"

filter:filter group:category_group

Filter by categories by specifying a category group. This is a pipe | seperated list of category IDs. In the same way the fields parameters works you can specify a list of categories. For example if I have a category group for departments with the ID 2 I can filter by the departments.

filter:department:category_group="2"

filter:filter group:show_separate_only

Default: no

Exclude this filter from the showing up in the tags for flexible filter groups, that combine filter groups (i.e. {list_groups}, {number_range_groups}, {search_groups}, {active_groups} and {filter_groups}). To show this filter you would need to specify it specifically, see Complete example using separate filters.

filter:department:show_separate_only="yes"

Single Variables

{total_active_filters}

Number of active filters across all filter groups.

{if total_active_filters > 0}You have selected a filter{/if}

{total_entries}

Total number of matching entries

{if total_entries == 0} No entries found. {/if}

{querystring}

Query string of current search, useful for pagination if you're using method="get" as ExpressionEngine does not add the querystring onto {auto_path} in pagination.

{paginate}  
{if next_page}  
   <a href="{auto_path}?{querystring}">Next page</a>  
{/if}  
{/paginate}

{entries} Variable Pair

Where the magic happens. This variable pair will contain your channel entries code to display the results

{entries} » {entry_ids}

A pipe | seperated list of entry IDs that match the current filters. You can use this in a channel entries tag to output your results. If no entries are found {entry_ids} will be -1.

{exp:channel:entries entry_id="{entry_ids}" dynamic="no" orderby="title" status="not closed" limit="10" paginate="yes"}  
    <frameset>  
    <img src="{product_image}" alt="{title}"/><br />  
    <a href="/products/{url_title}">{title}</a><br />  
    <strong>&pound;{price}</strong>  
    </frameset>  
    {paginate}<p class="paging">Page {current_page} of {total_pages} pages {pagination_links}</p>{/paginate}  
  {/exp:channel:entries}

{entries} » {total_entries}

Total number of matching entries

{if total_entries == 0} No entries found. {/if}

{list_groups} Variable Pair

This variable pair will contain your list of filters for all the filter groups of type "list". Here is an example from the "shop" theme:

{list_groups}  
<h3 class="group_{group_name}">{label}</h3>  
<ul>  
{filters}  
<li class="{filter_active_class}" aria-selected="{filter_active_boolean}">  
<a href="{url}">{filter_title} ({filter_quantity})</a>  
</li>  
{/filters}  
</ul>  
{/list_groups}  

{list_groups} » {group_name}

The filter group name that is derived from filter:group name tag or the EE field name. Can be used for css classes.

{list_groups}<div class="filter_{group_name}">...</div>{/list_groups}

{list_groups} » {label}

Filter group label to display to user.

{list_groups}<h2>{label}</h2>....{/list_groups}

{list_groups} » {type}

Filter group type (list, number_range, search)

{list_groups}{if type=="number_range"}...{if:else}....{/if}{/list_groups}

{list_groups} » {total_filters}

Total number of possible filters in filter group

{list_groups}{if total_filters > 0}...{/if}{/list_groups}

{list_groups} » {active_filters}

Total number of filters that have have been selected by the user.

{list_groups}Filtering by {active_filters} filters.{/list_groups}

{list_groups} » {matching_filters}

Total number of filters that have at least one matching entry.

{list_groups}{if matching_filters > 0}...{if:else}No filters match{/if}{/list_groups}

{list_groups} » {clear_url}

The url to remove all active filters from the filter group.

{list_groups}<a href="{clear_url}">Clear</a>....{/list_groups}

{list_groups} » {filters} Variable Pair

This variable pair will contain your list of possible filter values for the filter group. The standard ExpressionEngine variables {count} and {total_results} are also available.

{list_groups} » {filters} » {filter_id}

For categories this will be the category ID. For all other field types this is blank.

{list_groups}<ul>{filters}<li><a href="{url}">  
{exp:channel:categories id="{filter_id}"}{category_image}{/exp:channel:categories}  
</a></li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {filter_value}

This is the possible filter value, which is used in the URL. For categories this will be the category url title. For relationship fields this will be the url_title of the entry.

{list_groups}<ul>{filters}<li><a href="{url}">{filter_value}</a></li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {filter_title}

This is the possible filter title for displaying to the user.

{list_groups}<ul>{filters}<li><a href="{url}">**{filter_title}**</a></li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {url}

The URL for that filter. No need to put {site_url} before it as it should do this itself.

{list_groups}<ul>{filters}<li><a href="**{url}**">{filter_title}</a></li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {filter_quantity}

The number of matching entries for that filter.

{list_groups}<ul>{filters}<li><a href="{url}">{filter_title} (**{filter_quantity}**)</a></li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {filter_active}

True if the filter is active (ie has been selected by user)

{list_groups}<ul>{filters}<li class="{if **filter_active**}active{if:else}inactive{/if}" >...</li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {filter_active_class}

If the filter is active this returns "active", if not "inactive". Useful for CSS class names.

{list_groups}<ul>{filters}<li class="{filter_active_class}" >...</li>{/filters}</ul>{/list_groups}

{list_groups} » {filters} » {filter_active_boolean}

If the filter is active this returns "true", if not "false". Useful for specifying aria-selected for screen reader users.

{list_groups}<ul>{filters}<li aria-selected="{filter_active_boolean}">...</li>{/filters}</ul>{/list_groups}

{tree_groups} Variable Pair

This variable pair will contain all filter groups of the type "tree". It is used for showing categories with subcategories much like how ExpressionEngine shows them in the control panel. It contains all the same variables as the {list_groups} pair Each item in the {filters}...{/filters} pair represents a top most category. Additionally the {filters}...{/filters} variable pair also contains the variable pair {subfilters_1}...{/subfilters_1} which represents a subcategory which in turn has the variable pair {subfilters_2}...{/subfilters_2} which represents a sub-sub-category and so on. If you are using the shop theme then this will be taken care of for you. Here is an example taken from the "shop" theme:

{tree_groups} » {filters}

This represents a main category and has all the same variables as {list_groups} > {filters}

{tree_groups} » {filters} » {has_active_subfilters}

This is true if this current filter has at least one subfilter selected.

{if has_active_subfilters}...{/if}

{tree_groups} » {filters} » {has_active_subfilters_class}

The same as {tree_groups} » {filters} » {has_active_subfilters} but it outputs "has-active-subfilters" if true, and "no-active-subfilters" if false. This can be used for styling, for example if you want to hide subfilters until the user has selected the parent filter.

<li class="{filter_active_class} {has_active_subfilters_class}">

{tree_groups} » {filters} » {subfilters_1}

This represents a sub-category. It has all the same variables as {tree_groups} » {filters} BUT each variable is appended with the subfilter number. So {url} is {url_1}, {filter_title} is {filter_title_1} and so on.

{tree_groups} » {filters} » {subfilters_1} » {subfilters_2}

This represents a sub-sub-category. It has all the same variables as {tree_groups} » {filters} but each variable is appended with _2.

{tree_groups} » {filters} » {subfilters_1} » {subfilters_2} » {subfilters_3} ...

This represents a sub-sub-sub-category. It has all the same variables as {tree_groups} » {filters} but each variable is appended with _3 and so on, you get the idea :)

{number_range_groups} Variable Pair

This variable pair will contain all filter groups of the type "list". The {number_range_groups} variable pair contains all the same variables as the {list_groups} pair. It contains only one filter which has the minimum and maximum values the user has specified and the minimum and maximum values of the filter for the current search. Here is an example from the "shop" theme:

{number_range_groups}  
<h3 class="group_{group_name}">{label}</h3>  
<form method="post" class="number_range" >  
<input type="hidden" name="XID" value="{XID_HASH}" />  
{filters}  
<input type="text" name="{group_name}_min" id="{group_name}_min" placeholder="{filter_min}"  
value="{filter_min_value}" aria-label="Minimum value"/>  
&mdash; <input type="text" name="{group_name}_max" id="{group_name}_max" placeholder="{filter_max}"  
value="{filter_max_value}" aria-label="Maximum value"/>  
{/filters}  
<input type="submit" name="submit" value="Go" />  
</form>  
{/number_range_groups}  

{number_range_groups} » {filters} » {filter_min}

The minimum possible value of the filter. Applies to filter groups of type number_range only.

{number_range_groups}{filters}<p>Min: {filter_min}</p>...{/filters}{/number_range_groups}

{number_range_groups} » {filters} » {filter_max}

The maximum possible value of the filter. Applies to filter groups of type number_range only.

{number_range_groups}{filters}<p>Max: {filter_max}</p>...{/filters}{/number_range_groups}

{number_range_groups} » {filters} » {filter_min_value}

The minimum value to filter by as specified by the user. Applies to filter groups of type number_range only.

{number_range_groups}{filters}...<input name="{group_name}_min" value="{filter_min_value}" />...{/filters}{/number_range_groups}

{number_range_groups} » {filters} » {filter_max_value}

The maximum value to filter by as specified by the user. Applies to filter groups of type number_range only.

{number_range_groups}{filters}...<input name="{group_name}_max" value="{filter_max_value}" />...{/filters}{/number_range_groups}

{search_groups} Variable Pair

This variable pair will contain all filter groups of the type "search". It has all the same variables as {list_groups}. It contains just the one filter which has the {filter_title} variable. Here is an example from the default theme:

{search_groups}  
{filters}  
<form method="post" class="{filter_active_class}">  
<input type="hidden" name="XID" value="{XID_HASH}" />  
<label for="{group_name}">{label}</label>  
<input  
type="text" id="{group_name}" name="{group_name}" placeholder="search"  
value="{filter_title}" title="Search ({filter_quantity})" />  
<input type="submit" name="submit" value="Go" />  
</form>  
{/filters}  
{/search_groups}  

{active_groups} Variable Pair

This variable pair will contain all filter groups that have at least one active filter. This is useful for showing a list of selected criteria. It has all the same variables as {list_groups}. It will only output active filter groups and active filters. Here is an example from the default theme:

{if total_active_filters > 0}  
<div class="reefine_active_filters">  
<h3>Selected Criteria</h3>  
<ul>  
{active_groups}  
<li><strong><a class="remove-filter" href="{clear_url}">{label}:</a></strong></li>  
{filters}  
<li class="{filter_active_class}" aria-selected="{filter_active_boolean}">  
<a href="{url}">{filter_title}</a>  
</li>  
{/filters}  
{/active_groups}  
</ul>  
<p class="total_entries">{total_entries} items found</p>  
</div>  
{/if}  

{filter_groups} Variable Pair

This variable pair will output all filters regardless of type. The variables available depend on what type the group is.

Change Log

Version 3.3.0

Release 2022-11-23

  • Fix Issue #10 Large numbers with commas cut off at first comma in number_range
  • add parameter disable_multiple_selected_groups
  • #6 Month list: fix date overlap issue
  • fix PHP 7.4 warning, update demo url, remove old docs
  • #3 fix has_active_subfilters problem

Version 3.2.0

Release 2019-04-01

  • Added {querystring} tag
  • Big change to url encoding: Now uses --number-- encoding to fix disallowed character bug eg test@example becomes test--45--example
  • Improved category filter Performance
  • Bug fixes

Version 3.1.1

Released 2019-01-07

  • Bug fixes
  • EE5 compatible

Version 3.0.0

Release 3018-10-26

  • EE4 compatible

Version 2.1.2

Released 2018-01-16

  • fixed bug with site id not in where clauses
  • EE3: added fix for ajax loading template override error

Version 2.1

Release 2017-04-06

  • Bug fixes
  • Added author_id, fixed_order parameters
  • Added status as filter field

Version 2.0

Release 2016-10-20

  • Made EE3 compatible.

Version 1.6

Release 2015-09-28

  • Added new filter group type "tree" for showing categories and subcategories in a tree format.
  • Added fix_pagination to {exp:reefine}
  • Added show_separate_only, default, custom_values and custom_titles parameters to filter groups.

Version 1.5

Release 2015-01-05

  • Added url_output, category, filter:filter_group:sort, show_future_entries and show_expired parameters.
  • status parameter can now be pipe seperated
  • Added active_filters, matching_filters and filter_id tags
  • Added rel="nofollow" to links in "shop" theme
  • Fix for AJAX in "form" theme
  • Fix for special characters in URL.
  • Added custom ordering for filter:filter_group:orderby parameter.

Version 1.4

Release 2013-12-11

  • Added Grid, Matrix, Relationship & Playa compatibility
  • Added methods "get", "post", and "ajax"
  • Added "form" theme and made the "shop" theme compatible with get, post and ajax methods.
  • Added disable_search parameter
  • Added month_list filter group type.

Version 1.3

Released 2013-09-25

  • Added Store fieldtype compatibility
  • Made seperate tags for each filter type and an active_groups tag
  • Added filter:filter group:show_empty_filters parameter
  • Modified themes to be more portable
  • Made EE 2.7 compatible
  • Fixed bug with {count} tag
  • Modified default shop theme for better performance (by avoiding conditionals)
  • seperate_filters parameter removed, reefine now automatically detects seperate filter tags.
  • Improved accessability of default theme for screen readers.

Version 1.2

Release 2013-09-05

Bug Fixes

Version 1.1

Released 2013-08-09

Breaking Changes

  • The {filter_value} parameter is now used as the value for url title for categories. Please change {filter_value} to {filter_title} in your code.

Other changes

  • Added filter_title field
  • Added fix for filter groups with multiple categories bug
  • Fixed number_range problem with large numbers
  • Fix paging problem when there is no filter
  • Added category_url parameter

Version 1.0

First version of Reefine.

Compatibility

Categories

Reefine can filter by categories, see filter:filter group:category_group

Selects / Checkboxes / Multiselects

Reefine can filter by select dropdowns and multiselects. If you are using the filter:fields parameter it will automatically pick this up. If you using filter:field group:fields you will have to add delimiter="|" see delimiter parameter.

Structure

Reefine is compatible with Structure. However you will need to add the Freebie add-on so that the page will still display. You will need to add your page's segments to Freebie so Structure displays the correct page. Reefine will modify the page's URI string back to how it was before Freebie after processing to ensure EE's native paging still works.

Expresso Store

Reefine is compatible with the Expresso Store fieldtype. You can access the details of a product by specify the field name in the same way as in the template eg if your field of fieldtype Store is called "product" and you wanted to get the price you would use filter:filter group:fields="product:price". Please note it can only retrive the values from the exp_store_products database table so it will not be able to add tax or shipping. To filter by what's on sale use filter:filter group:fields="product:on_sale".

Relationship & Playa fieldtypes

Reefine is compatible with the Relationship fieldtype and Pixel and Tonic's Playa fieldtype. Both work in the same way. Just enter the field name and the filter will show the related entry's title and use the url_title for the url. For example:

channel="films"  
filter:actors:fields="actors"  
url="/films/{actors}"  

You can also use a particular field within the related entry as the filter by appending a colon (:) and the field name of the related entry you want to filter. This example will show a filter for actor's nationality, so the user can see a list of all films that have French actors, for instance.

channel="films"  
filter:actor_nationality:fields="actors:nationality"  
url="/films/{actor_nationality}"  

Grid & Matrix fieldtypes

Reefine is compatible with the Grid fieldtype and Pixel and Tonic's Matrix fieldtype. Both will work in the same way. Just enter the field name of the Grid/Matrix field followed by a colon (:) and the field name within the grid/matrix. Please note that Reefine will only return the matching entries and will not be able to show filtered results of the grid rows. Here's an example of a clothes store that has clothes in the channel "clothes" and a Grid field "sizes" with columns "size" and "price". The price of an item of clothing is dependant on the size, the following example will allow the user to filter by both size and price:

channel="clothes"  
filter:size:fields="sizes:size"  
filter:price:fields="sizes:price"  
filter:price:type="number_range"  
url="/store/{size}/{price}"  

Date fieldtype

The date fieldtype is supported with filter:filter group:type="month_list". You can also use the list and number_range filter group types, however it will be output as a number (unix time) which you'll have to convert to a date in the template .

Other Custom Fieldtypes

Reefine may not be able to filter by custom fieldtypes not listed above. Although you can still display the content of custom fieldtypes in the search results as Reefine just relies on the exp:channel:entries tag.

About Reefine

License

View License

Support

If you have an issue then you will need to first use the process of elimination to pinpoint exactly what is causing the issue. First create a copy of your template and start removing everything but the filters or parts that are causing the issue. For example if you have 4 filters try removing all but the first filter then if the bug isn't appearing remove all but the second filter and so on until you find the filter or combination of filters that are causing the issue. Then check with the documentation that your syntax is correct.

Community support is available at the following places:

If you wish to hire me for ExpressionEngine work checkout my website:

Tips and Tricks

SEO

Reefine allows you to make super SEO and user friendly URLs. However, you may want to limit the number of pages search engines index as you can be penalised for duplicate content. To do this you can add the rel="nofollow" parameter to all tags. If you're using a theme the nofollow tag is already there.

<a href="{url}" aria-selected="{filter_active_boolean}" rel="nofollow">{filter_title}</a>

This will prevent search engines from following the filter links, however the search pages may still be indexed if there are direct links going there. You may wish to use a canonical link in this case so hopefully the search engine will attribute the page rank to your main search page rather than spread out over different filters. You can do this by putting in a conditional in your head tag to make your search page the canonical link, for example:

{if segment_1=="my_search_page" && segment_2!=""}<link rel="canonical" href="{site_url}/my_search_page" />{/if}

Warning: I'm not an SEO expert so take care when using these.

Performance

Reefine can be resources heavy if searching large amounts of filters. Try to avoid using categories and any fields with multiple values (checkboxes, multiselects) to speed up search times. Ensure you're using a decent host. Try to keep the number of conditionals (especially advanced conditionals) in the filter tags to a minimum. You should also consider using a cacheing addon such as CE Cache or you can use ExpressionEngine's native cacheing as follows:

Add this to the {exp:reefine} tag:

cache="yes" refresh="120" cache_buster="{segment_1}/{segment_2}/{segment_3}/{segment_4}/{segment_5}/{segment_6}/{segment_7}/{segment_8}/"

Add this to the {exp:channel:entries} tag

cache="yes" refresh="120"

You can also get some good performance improvements by using indexes in MySQL.

Themes and customisation

If you would like to customise the default "shop" theme you can either remove the theme="shop" parameter and add your own filter tags as above or you can create your own theme. To create your own theme make a copy of the themes/third_party/reefine/shop folder eg themes/third_party/reefine/my_theme and change the theme="shop" to whatever you named the new folder eg theme="my_theme". You can then customise the css and html in that folder.

Seperate filter groups

Rather than using {list_groups}, {number_range_groups} etc as above you can specify each filter group individually. Just use the filter group name. Check the code example below to get a better idea.

{colour}  
<h3>Colour</h3>  
<ul>  
{filters}  
<li class="{filter_active_class}" style="color:{filter_value};">  
<a href="{url}">{filter_title} ({filter_quantity})</a>  
</li>  
{/filters}  
</ul>  
{/colour}

Sorting entries

Sorting can be done using a filter group with hardcoded titles and values and then plugging the selected value into the exp:channel:entries orderby parameter. Here is an example with the relevant parts in bold. You can also use the same principle for other parameter in channel:entries such as limit and sort.

{exp:reefine channel="clothes" parse="inward" theme="shop"  
filter:fields="price|colour"  
filter:orderby:custom_titles="Product name|Price"  
filter:orderby:custom_values="title|price"  
filter:orderby:default="price"  
filter:orderby:join="none"  
url="/{segment_1}/{segment_2}/{price}/{colour}/{orderby}"}  
{entries}  
  
{exp:channel:entries entry_id="{entry_ids}"  
disable="categories|category_fields|member_data" dynamic="no"  
orderby="{orderby}{active_filter_values}{value}{/active_filter_values}{/orderby}" 
sort="asc" status="not closed" limit="8" paginate="yes"}  
....  
{/exp:channel:entries}  
{/entries}  
{/exp:reefine}  

Known Issues

Reefine not processing some tags in filters

This may occur if you are running ExpressionEngine version 2.5.2 or earlier. This is a bug in ExpressionEngine which was fixed in later releases. In the meantime you will need to specify all tags in all situations. This may mean you have to put the tags in comments as follows. The default theme has a few of these tags for compatibility which you may remove if you're using EE 2.5.5 and above.

<!--{url}{filter_value}{filter_title}{filter_quantity}{filter_min_value}{filter_max_value}{filter_min}{filter_max}-->

Code Samples

Complete example using separate filters

This example outputs each filter group in a seperate tag pair rather than using the {list_groups}, {number_groups} etc. This is useful for customising the order, position and formatting of different filters. Github Gist

Complete example using flexible filter groups

This example uses the {number_range_groups} {list_groups} {search_groups} variable pairs and selects the correct html based on the group type. You can add extra fields to this example and they should show up automatically. Github Gist

Example with form and month_list

This example uses a form to submit two dropdowns which can filter a list of events. When the user click the submit button it will redirect the url to include the filter parameters. Also notice it uses the month_list group type. Github Gist


Happy Filtering

The End

About

ExpressionEngine addon that filters and searches entries

License:MIT License


Languages

Language:PHP 88.2%Language:CSS 4.5%Language:JavaScript 3.9%Language:HTML 3.3%