Scopes and Product Groups
This section explains Spree’s facilities for selecting lists of products by various criteria.
After reading it you should know:
- the scopes provided for filtering lists of products
- about the concept of product groups
- how to use product groups.
- how to configure and use the search API.
In various applications, we need to create lists of products according to various criteria, e.g.
all products in taxon X, or all products costing less than $20, or composite criteria like all
products costing more than $100 that have brand Y.
This chapter first covers the available named scopes in the Spree core which provide filtering
by various aspects of the basic data, some of which can be chained or composed together
for more complex filters.
The second part explains the facilities for defining and naming groups of products for
various purposes in an application. For example, you can define a group called Ruby products
which contains everything with Ruby in the product name, or another group called Ruby clothing
which is the sub-list of the above limited to items in the Clothing taxon. Product group definitions
can also take price ranges, product properties, and variant options into account. It is possible
to add your own filters by defining new named scopes too.
This chapter is mainly aimed at developers, who will usually create and hard-code definitions
and access to certain groups, but administrators will sometimes want to edit the criteria for
these existing groups. The admin interface editing facilities is
Primitive selection scopes
- – this is the key scope for selecting listable products, and is the combination of
(the product’s availability date is not later than the given date or the default current date) and (not marked as deleted in the database)
- – defaults to SQL text search in the product names and descriptions, though you can modify or extend this via the Searcher API
- , , – testing for any of a list of strings passed as a space or comma separated string, appearing in the product database
- – selects for a master price being in the limits (using SQL’s operator)
- and – selects products in the given taxon(s) and their descendants. The arguments can be taxon ids, taxon objects, names of taxons, or (suffixes of) permalinks.
- having a product property () or option type () associated with the product – but not checking for actual values, where can be a string, id, or property/option_type object
- – testing for a product having the given value. is as above, and is a string (or convertible to a string). Note that this can’t test for a product lacking a property
(ie no database row), but can test for a property that is explicitly set to NULL.
- – selects for a product having a variant with the given value for the option (but does not consider whether the variant is deleted or not)
- – selects products having either a property value or having a variant with an option value which is equal to the given value
You can also use the scopes available via SearchLogic. These follow the pattern of field plus a test, e.g. or (note the easy access across associations).
Primitive ordering scopes
SearchLogic is used to produce most of the commonly used ordering scopes, e.g. .
There is a scope which orders by the number of orders a product appears in.
Chaining scopes together
The above scopes can be used like any other scopes, and thus be chained arbitrarily. However, due
to the type of filters used and (in some cases) the implementation, certain combinations may “not
work” or return an empty list:
- most of the primitive scopes reduce (or sometimes, preserve) the number of products available, and sometimes the conditions have no solution amongst available products
- some combinations of scopes are of course contradictory, e.g. selecting scope price less than $10 and price greater than $15
- for efficiency reasons (balanced against typical use), it is not possible to filter by more than one property test or by more than one option value at a time (the subsequent tests would clash with the first one)
Of course, developers are free to add more complex scopes should they wish to undertake more complex chaining.
So called “order scopes” should not change the number of items displayed, just the order in which
items appear in the list.
NOTE: Product Groups is longer a part of Spree core, it has been extracted to
a standalone extension, which can be found at spree_product_groups
Product groups are named lists of products which are selected via a defined combination of criteria.
They can be created, edited, destroyed by administrators; and viewed by the user via appropriate
permalinks or developers can write code to display them as they wish.
In general, product groups are a composition of scopes with an optional ordering constraint (which is
also a scope).
Extensive low level documentation can be found in source code of the
INFO: product groups do not automatically include the scope, so may return out of date products.
Listing of product groups
The index page allows quick inspection of available groups – primarily to check for expected
cardinality, look up the permalink, or get quick access to the preview. You can also edit
or delete groups from this page.
Creating a new scope
When creating a new product group, you need:
- name of the group – it should be descriptive, as it’ll be reflected in both admin UI and product group url.
- order by which results will be displayed
- parameters for zero or more of the available scopes
A scope is activated by checking its tick box, and (where appropriate) supplying a parameter of
appropriate type, eg. a string or numeric value. The form provides hints for most of the scopes.
After you select conditions, you can preview products that will be selected by your conditions and inspect
if the result is what you would expect.
After you create the product group, you’ll be presented with information about the new group,
including bookmarkable (permalink) URL, current product count, and other useful information.
Access via URLs
You can access named product groups via URLS of the following format.
- – list a product group after restricting to a particular taxon
- – list a product group
There’s also nameless versions of the above, where the URL encodes the required scopes and their
arguments. After , there should be a sequence of scope-argument pairs followed by an optional
ordering scope. Scope arguments should be comma-separated.
Product groups are effectively a representation of a set of scopes. The API is similar to standard product scope values.
For efficiency, all named product groups are memoized – retrieval (via the overloaded method) just
consults a single database table. The memoization is updated when a product group or product is saved.
Translating Product Groups and scopes
The Product Group functionality relies heavily on Rails I18n capabilities to ensure
appropriate end user text for name, description, and format string for product scopes and groups.
So for example, text for the scope that takes two arguments looks like this
name: “Price between”
sentence: price between %.2f and %.2f
- Name if the name of the scope displayed in admin interface.
- Description is used only in admin interface UI to provide additional information about usage of the scope.
- Sentence is used for describing the scope to the end user,
and as the name suggests it should be formed as a sentence
that can be connected using “and”, sentence is evaluated using String#% method and can be formatted using
printf escape sequences.
WARNING: It’s very important that the number of expected arguments for the ‘sentence’ string does not exceed the number available for the relevant scope, else will raise an exception.
The same rules apply for ordering scopes, except that they don’t take any arguments so do not need a
The documentation of Scopes module provides some additional information on translating customized scopes.
Modifying available scopes
All scopes available in the admin UI are defined in file, in the constant.
Format of the constant should be self explanatory, but it’s recomended to read Scopes module documentation
When using Ruby 1.8, the order of scopes within groups, and order of groups themselves, is arbitrary,
Ruby 1.9 preserves order of hash, and in effect reflects order of declarated scopes in UI.
You can include any valid Product scope (including dynamically created SearchLogic scopes) in the
Product Groups mechanism by declaring it in the constant.
If you plan to provide your own custom scope make sure to check that:
- the new scope doesn’t clash with any of the existing scopes (especially when you’re using joins)
- it doesn’t change order of the result (unless it is a new order scope)
- it does specify all neccessary joins and includes (i.e. it is self-contained and commutative – you shouldn’t rely on joins or includes from other scopes)
- it doesn’t do unnecessary joins or includes (each extra join may be a performance bottleneck)
- be very careful with scope of names, especially when you’re joining with or ,
e.g. make sure to use “AS” sql statement on joins and in conditions to avoid name capture or ambiguity.
Remember also to provide translations for your new scopes; there’s a
method that can be helpful here.