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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user