1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-13 18:24:57 +02:00

Upgrade Page matching in-memory selectors by adding support for OR-groups, sub-selectors, and same (1) item group matches.

This commit is contained in:
Ryan Cramer
2023-10-27 13:26:24 -04:00
parent 78aea1eedf
commit e53552d8c4

View File

@@ -214,7 +214,7 @@ class PageComparison {
// exit early for simple path comparison
return true;
} else if($page->name === $s) {
// early exit for simple name atch
// early exit for simple name match
return true;
} else if(Selectors::stringHasOperator($s)) {
// selectors string
@@ -243,39 +243,138 @@ class PageComparison {
if(!empty($options['useDatabase'])) {
$selectors->add(new SelectorEqual('id', $page->id))->add(new SelectorEqual('include', 'all'));
return $page->wire()->pages->count($selectors) > 0;
}
}
$matches = false;
$matchFail = false;
$groupSelectors = array(); // same (1) item match group selectors
$orGroupSelectors = array(); // OR-group selectors
foreach($selectors as $selector) {
$property = $selector->field;
$subproperty = '';
if(is_array($property)) $property = reset($property);
if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2);
if(in_array($property, $this->matchesIgnores)) continue;
$matches = true;
$value = $page->getUnformatted($property);
if(is_object($value)) {
// convert object to array value(s)
$value = $this->getObjectValueArray($value, $subproperty);
} else if(is_array($value)) {
// ok: selector matches will accept an array
} else {
// convert to a string value, whatever it may be
$value = "$value";
if($selector->quote === '(') {
// OR-groups are handled below this loop
$orGroup = $selector->field() . '.';
if(!isset($orGroupSelectors[$orGroup])) $orGroupSelectors[$orGroup] = array();
$orGroupSelectors[$orGroup][] = $selector;
continue;
}
if(!$selector->matches($value)) {
$matches = false;
if(!$this->selectorMatches($page, $selector)) {
$matchFail = true;
break;
}
if($selector->group !== null) {
// validate that same (1) item matches from these later
$groupName = $selector->group;
if(!isset($groupSelectors[$groupName])) $groupSelectors[$groupName] = array();
$groupSelectors[$groupName][] = $selector;
}
}
// OR-groups
if(!$matchFail && count($orGroupSelectors)) {
foreach($orGroupSelectors as /* $orGroupName => */ $selectors) {
$orGroupMatches = false;
foreach($selectors as $selector) {
if($this->matches($page, $selector->value)) {
// OR-group selector matches
$orGroupMatches = true;
break;
}
}
if(!$orGroupMatches) {
$matchFail = true;
break;
}
}
}
// same (1) item match groups
if(!$matchFail && count($groupSelectors)) {
foreach($groupSelectors as /* $groupName => */ $selectors) {
$matchGroupKeys = null;
foreach($selectors as $selector) {
$keys = $selector->get('_matchGroupKeys'); // populated by selectorMatchesProperty
if($matchGroupKeys === null) {
$matchGroupKeys = $keys;
} else {
$matchGroupKeys = array_intersect($matchGroupKeys, $keys);
}
}
if(empty($matchGroupKeys)) {
$matchFail = true;
break;
}
}
}
return !$matchFail;
}
return $matches;
/**
* Return whether individual Selector object matches Page
*
* @param Page $page
* @param Selector $selector
* @return bool
*
*/
protected function selectorMatches(Page $page, Selector $selector) {
$match = false;
$properties = $selector->fields();
foreach($properties as $property) {
$match = $this->selectorMatchesProperty($page, $selector, $property);
if($match) break;
}
return $match;
}
/**
* Return whether single property from individual Selector matches Page
*
* @param Page $page
* @param Selector $selector
* @param string $property
* @return bool
*
*/
protected function selectorMatchesProperty(Page $page, Selector $selector, $property) {
$subproperty = '';
if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2);
if(in_array($property, $this->matchesIgnores)) return true;
if($selector->quote === '[' && Selectors::stringHasOperator($selector->value())) {
$selector->value = $page->wire()->pages->findIDs($selector->value());
}
$value = $page->getUnformatted($property);
if(is_object($value)) {
// convert object to array value(s)
$value = $this->getObjectValueArray($value, $subproperty);
} else if(is_array($value)) {
// ok: selector matches will accept an array
} else {
// convert to a string value, whatever it may be
$value = "$value";
}
if(!$selector->matches($value)) return false;
if($selector->group !== null && is_array($value)) {
// see which individual values match and record their keys for later comparison
$matchGroupKeys = array();
foreach($value as $key => $val) {
if($selector->matches($val)) $matchGroupKeys[] = $key;
}
$selector->setQuietly('_matchGroupKeys', $matchGroupKeys); // used by matches() method
}
return true;
}
/**