Jump to content

WireArray filterData selector


dscONE
 Share

Recommended Posts

Found myself needing to filter repeater items based upon start and end date fields.  These fields aren't required, so repeater items with no dates should be included and those with dates (or a just one date -- start or end) should be evaluated for inclusion.

I slowly figured out that I was dealing with in-memory filtering (of a RepeaterPageArray) and not a database-driven operation.  So, that eliminated the possibility of using or-groups in my selector.  I still thought I could use the 'filter' and 'not' functions to get the job done.  Turns out the way selectors are handled for in-memory filtering is quite different from database-driven selectors.

Here is what I want(ed) to do to filter the dates:

// Start date not specified or in the past
$repeater->filter('start_time<='.$now);
			
// Not when end date is specified and has passed
$repeater->not('end_time>0, end_time<'.$now);

The first one worked exactly as expected.  The second it where I ran into problems.  The 'filter' function (which calls the 'filterData' function) takes a $selector argument.  From everything I read about selectors, I assumed that the entire selector would have to match.  In other words, each comma represented an 'and' scenario.  Turns out that each separate comma-separated-selector in the $selector as a whole gets evaluated independently.  In the case of the 'filterData' function, the elements are removed if any one of those selectors matches.  Essentially, we have an 'or' scenario instead of an 'and' scenario.

In my case above, there was no way for me to filter for a non-empty date AND one that occurs before a given date...at least that I could think of.

So, my main question is if the filter (and related) function should operate on the entire selector passed to the function instead of its individual parts in some sequence.  That is what I would have assumed a selector to do.

In my project, altering the segment of the WireArray::filterData function solved my problem for now.  ...at least till I forget about it and overwrite it when I upgrade next.  Here is the code I changed within the function

// now filter the data according to the selectors that remain
foreach($this->data as $key => $item) {
	$filter_item = true;
	foreach($selectors as $selector) {
		if(is_array($selector->field)) {
			$value = array();
			foreach($selector->field as $field) $value[] = (string) $this->getItemPropertyValue($item, $field);
		} else {
			$value = (string) $this->getItemPropertyValue($item, $selector->field);
		}
		if($not === $selector->matches($value))
			continue;
		$filter_item = false;
		break;
	}
	if($filter_item && isset($this->data[$key])) {
		$this->trackRemove($this->data[$key], $key);
		unset($this->data[$key]);
	}
}

I would love to hear what you all think.  If I have misunderstood something, then I would welcome a different solution.
Thanks!

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Hi and welcome @dscONE!

Yes, I do think the behaviour you have discovered is unexpected. Just to reiterate in a simplified way what I think you are saying....

The expected behaviour is that for any selector, the combined results of $pagearray->find($some_selector) and $pagearray->not($some_selector) should equal the entire PageArray. But this is not the case, as can be demonstrated with a PageArray containing three pages titled "red car", "blue car", "green truck"...

$matches = $pagearray->find("title~=car"); // "red car", "blue car"
$not_matches = $pagearray->not("title~=car"); // "green truck"
// So far, so good

$matches = $pagearray->find("title~=car, !title~=red"); // "blue car"
$not_matches = $pagearray->not("title~=car, !title~=red"); // expected "red car", "green truck" but got unexpected empty PageArray

Would you please report this issue over at the PW issues GitHub repo? https://github.com/processwire/processwire-issues/issues

 

 

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Thank you, Robin S, for the response.  I must have missed your first one, so I apologize for not reporting the issue myself.  But, thanks for the assist.

It seems to me that you have represented my problem accurately with your example.  The problem arises with multiple selectors and because it looks like the code does the actual filtering (destructive) as soon as it finds a match as it loops through the array of selector items parsed from the selector as a whole.

Thanks again.  I appreciate your response.

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...