Closed (fixed)
Project:
Commerce Services resources
Version:
7.x-1.x-dev
Component:
Code
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Reporter:
Created:
27 Sep 2012 at 15:00 UTC
Updated:
22 Dec 2012 at 05:30 UTC
I don't know if we should explicitly support individual conditions, like having a bundle=[bundle_name] parameter, or if we can figure out some way to support multiple conditions in a single parameter. I can look into the array notation for GET parameters, but it could just be better DX to explicitly define the conditions as separate arguments.
Comments
Comment #1
rszrama commentedAt the same time, let's also consider using an argument to determine the sort order.
Comment #2
rszrama commentedI've been thinking about this and searching the web for other examples, and I'm afraid I just can't find anyone who's combining query conditions into a single query parameter array. Everyone seems to prefer:
To an array like so:
I think it's easy to see why - the request URL is cleaner and shorter. I'd like to push for this, and I think it will be possible through the entity metadata API. The function entity_get_property_info() will return a list of properties for an entity type and then an additional set of fields per bundle for the entity type. In the properties list, any property with a schema field can easily be mapped to a query condition, so it would be easy enough to the resource arguments.
For the fields it can be a little more complex, since a field's schema can be multiple columns. For single column fields, we can go ahead and add them as arguments as is, no problem. For multi-column fields, EntityFieldQuery expects you to specify the column name anyways. Perhaps we should just append the column name to the field name, so a request to get all orders of $100 on a site with only USD enabled would be:
However, we might want to have special processing for price fields, where perhaps you could specify "USD100" instead of 10000 (or "USD99.99").
The other thing we need to accommodate here is numeric columns handling inequality comparisons. Perhaps we can do something like the following to get all orders over $100:
I'm up for ideas there.
The only problem here is that these parameters are going to get quite cumbersome. I wonder if instead of having to add every possible parameter to the args array we can implement a preprocess that will find any applicable parameters and pass them to the resource callback in a single variable.
Also, we need to be able to specify the sort column and order (i.e. Order ID, DESC).
Comment #3
ivanbueno commentedFor operators, another syntax to consider is to use prefixes like so
Commonly used by search api's (solr, twitter) - https://twitter.com/search?q=to%3Atwitter%20until%3A2012-12-12&geocode=3...
Regarding single and array query parameters, there may be a usage for both of them. Single params are suitable for resource properties that are constant. An order will always have commerce_order_total/mail/status, and a product will always have a commerce_price/status/sku. The callbacks for these filters can be hardcoded.
Array params may be useful for generically targeting field properties that can change from one Commerce install to another. For example, if the commerce_saleprice module is enabled, the array param can be used to target the field_commerce_saleprice and field_commerce_saleprice_on_sale fields.
Comment #4
rszrama commentedHmm, you know, I wonder if we couldn't go for a full hybrid solution, where the "array notation" filter[] or param[] array is what we actually declare in the resource but then we stuff the other non-array notation parameters into the array in preprocessing. I'll have a go at that in the morning and post a patch against the Product Displays index. Thanks for the ideas on the colon operator, too - very useful!
Comment #5
rszrama commentedAfter a couple days of thinking through this, here's where I've landed.
I actually did go for the hybrid solution mentioned above, where you can use array notation in the GET request to set both a $filter and $filter_op array - the former holding filter field names with their corresponding values and the latter holding the operator to use when filtering. Additionally, if you do not set filter operators, it will default to a simple equality check.
This means the following requests would filter the result set exactly the same:
It also works for fields. For single column fields, it's enough to use the field name. For multi-column fields, you're going to have to specify the column name as well. The following requests would return product displays with a product reference field called field_product that references product 2:
(Note that even if it's a single column field, you can still specify the column name if you really want to. I'll always prefer the simple notation, resorting to the array notation only on the off chance that I have a collision with an actual named argument the index callback expects.)
How about any product display node with a nid greater than 1?
It's not perfect. I'm assuming you'll have to encode the = if you wanted to use an operator like >=. I don't have the encoding handy off the top of my head.
For sorts, it's pretty similar, but I eschewed array notation entirely in favor of comma separated lists for now. Most sorting will likely be done on a single column (i.e. sort by created date descending), but we'll still support multi-column sorting as EntityFieldQuery / SQL does so long as you specify an equal number of $sort_by field names and $sort_order directions.
Similar rules apply to $sort_by values - property names, single column field names, and multi-column field column names all work. Thus, the following would sort product displays by created date descending:
For now, the default sort order is DESC, but I think I'll remove the default in favor of an error if the sort_order isn't explicitly declared. I'm not sure I can say that DESC is a dominant enough default assumption to enshrine it in code. For what it's worth, the index query for product displays defaults to this sort. However, imagine if we wanted to request product display nodes sorted by created date descending with sticky posts at the top (i.e. the normal sort for front page nodes):
Let's combine some of these now with the existing fields, limit, and offset parameters. Here are a couple requests that would page through a list of disabled product displays, returning just the nid and title, sorted by those that were most recently updated:
If you look at the patch (linked at the bottom), you can see this is all being facilitated by a few new API functions and some custom parameters in the metadata of our resource operation arguments. Those parameters are documented in the doc block for the function commerce_services_services_request_preprocess_alter().
Please disregard in the patch an update I made for a change in the Services API; it was a sneaky change, whereby they introduced Services API versioning in a minor update, adjusting the array structure for resource operations' definitions. Note that I haven't applied these changes to the Product resource yet; that will come as I review the other product resource patches and update the resources to use commerce_entity_access().
I'll probably blog this a bit for feedback and write up some formal documentation to deliver with this module. Obviously, more robust solutions will require Views or Search API to be used to build the indexes - think for example of filtering or sorting based on properties of the referenced products. We'll get there. : )
Any thoughts / feedback?
Commit: http://drupalcode.org/project/commerce_services.git/commitdiff/9d8a1b1