1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-08 07:47:00 +02:00

Update the Page::matches() method to make it smarter and more flexible. This also accommodates issue #21.

This commit is contained in:
Ryan Cramer
2016-10-06 10:13:14 -04:00
parent c5eaf61dfc
commit 16a8232dfe
2 changed files with 146 additions and 30 deletions

View File

@@ -48,14 +48,28 @@ class PageComparison {
*
*/
public function matches(Page $page, $s) {
$selectors = array();
if(is_string($s) || is_int($s)) {
if(ctype_digit("$s")) $s = (int) $s;
if(is_string($s)) {
// exit early for simple path comparison
if(substr($s, 0, 1) == '/' && $page->path() == (rtrim($s, '/') . '/')) return true;
if(!Selectors::stringHasOperator($s)) return false;
$selectors = $page->wire(new Selectors($s));
if(!strlen($s)) {
// blank string matches nothing
return false;
} else if(substr($s, 0, 1) == '/' && $page->path() == (rtrim($s, '/') . '/')) {
// exit early for simple path comparison
return true;
} else if($page->name === $s) {
// early exit for simple name atch
return true;
} else if(Selectors::stringHasOperator($s)) {
// selectors string
$selectors = $page->wire(new Selectors($s));
} else {
// some other type of string
return false;
}
} else if(is_int($s)) {
// exit early for simple ID comparison
@@ -66,39 +80,27 @@ class PageComparison {
$selectors = $s;
} else {
// unknown data type to match
return false;
}
$matches = false;
$ignores = array('limit', 'start', 'sort', 'include');
foreach($selectors as $selector) {
$name = $selector->field;
if(in_array($name, array('limit', 'start', 'sort', 'include'))) continue;
$matches = true;
$value = $page->getUnformatted($name);
$property = $selector->field;
$subproperty = '';
if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2);
if(in_array($property, $ignores)) continue;
$matches = true;
$value = $page->getUnformatted($property);
if(is_object($value)) {
// if the current page value resolves to an object
if($value instanceof Page) {
// if it's a Page, get both the ID and path as allowed comparison values
$value = array($value->id, $value->path);
} else if($value instanceof PageArray) {
// if it's a PageArray, then get the ID and path of all of them
// @todo add support for @ selectors
$_value = array();
foreach($value as $v) {
$_value[] = $v->id;
$_value[] = $v->path;
}
$value = $_value;
} else if($value instanceof Template) {
$value = array($value->id, $value->name);
} else {
// otherwise just get the string value of the object
$value = "$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 {
@@ -115,5 +117,112 @@ class PageComparison {
return $matches;
}
/**
* Given an object, return the value(s) it represents (optionally from a property in the object)
*
* This method is setup for the matches() method above this. It will go recursive when given a property
* that resolves to another object.
*
* @param Wire|object $object
* @param string $property Optional property to pull from object (may also be property.subproperty, and so on)
* @return array Always returns an array, which may be empty or populated
*
*/
protected function getObjectValueArray($object, $property = '') {
$value = array();
$_property = $property; // original
$subproperty = '';
if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2);
// if the current page value resolves to an object
if($object instanceof Page) {
// object is a Page
if($property) {
// pull specific property from page
$v = $object->getUnformatted($property);
if(is_object($v)) {
$value = $this->getObjectValueArray($v, $subproperty);
} else if(!is_null($v)) {
$value = array($v);
}
} else {
// if no property, get id, name and path as allowed comparison values
$value[] = $object->id;
$value[] = $object->path;
$value[] = $object->name;
}
} else if($object instanceof WireArray) {
// it's a WireArray|PageArray
if($property === 'count') {
// quick exit for count property
return array(count($object));
}
// iterate and get value of each item present
foreach($object as $v) {
if(is_object($v)) {
$v = $this->getObjectValueArray($v, $_property); // use original property.subproperty
if(count($v)) $value = array_merge($value, $v);
} else {
$value[] = $v;
}
}
} else if($object instanceof Template) {
// Template object, compare to id and name
if($property) {
$v = $object->get($property);
if(!is_null($v)) $value[] = $v;
} else {
$value[] = $object->id;
$value[] = $object->name;
}
} else if($object instanceof WireData) {
// some other type of WireData object
if($property) {
$v = $object->get($property);
if(is_object($v)) {
$value = $this->getObjectValueArray($v, $subproperty);
} else if(!is_null($v)) {
$value = array($v);
}
} else {
// no property present, so we'll find some other way to identify the object
// get string value of object as a potential comparison
$v = (string) $object;
// string value that doesn't match class name identifies the object in some way
if($v !== wireClassName($object)) $value[] = $v;
// if the object uses the common 'id' or 'name' properties, consider those as well
foreach(array('id', 'name') as $key) {
$v = $object->get($key);
if(!is_null($v)) $value[] = $v;
}
}
} else if($property && method_exists($object, '__get')) {
// some other object, property is present, object has a __get method that we can pull it from
$v = $object->__get($property);
if(is_object($v)) {
$value = $this->getObjectValueArray($v, $subproperty);
} else if(!is_null($v)) {
$value = array($v);
}
} else if(!$property && method_exists($object, '__toString')) {
// items in WireArray are some type of Wire, use string value if not className
if(wireClassName($object) != (string) $object) $value[] = (string) $object;
} else {
// property present with some kind of value that we don't know how to pull from
}
return $value;
}
}

View File

@@ -225,8 +225,15 @@ class InputfieldPage extends Inputfield implements ConfigurableModule {
$selector = $findPagesSelector;
if($editPage) $selector = self::getFindPagesSelector($editPage, $selector);
if(!$page->matches($selector)) {
if($editPage) $editPage->set('_isValidPage', "Page $page does not match findPagesSelector: $selector");
$valid = false;
// failed in-memory check, attempt $page->count() check...
$selector .= ", id=$page->id";
if($page->wire('pages')->count($selector)) {
// looks like its okay
} else {
// also fails $pages->cont() check, so definitely not valid
if($editPage) $editPage->set('_isValidPage', "Page $page does not match findPagesSelector: $selector");
$valid = false;
}
}
}