mirror of
https://github.com/processwire/processwire.git
synced 2025-08-17 04:04:13 +02:00
Fix issue processwire/processwire-issues#1743
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
* /wire/core/Fieldtype.php
|
* /wire/core/Fieldtype.php
|
||||||
* /wire/core/FieldtypeMulti.php
|
* /wire/core/FieldtypeMulti.php
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -19,7 +19,7 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
public static function getModuleInfo() {
|
public static function getModuleInfo() {
|
||||||
return array(
|
return array(
|
||||||
'title' => 'Page Reference',
|
'title' => 'Page Reference',
|
||||||
'version' => 106,
|
'version' => 107,
|
||||||
'summary' => 'Field that stores one or more references to ProcessWire pages',
|
'summary' => 'Field that stores one or more references to ProcessWire pages',
|
||||||
'permanent' => true,
|
'permanent' => true,
|
||||||
);
|
);
|
||||||
@@ -45,6 +45,13 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
'published',
|
'published',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime/temp caches indexed by function name
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected $caches = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup a hook to Pages::delete so that we can remove references when pages are deleted
|
* Setup a hook to Pages::delete so that we can remove references when pages are deleted
|
||||||
@@ -356,19 +363,20 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
if($field->get('derefAsPage') > 0) {
|
if($field->get('derefAsPage') > 0) {
|
||||||
// if the $value isn't specifically a Page, make it a blank array for storage
|
// if the $value isn't specifically a Page, make it a blank array for storage
|
||||||
if(!$value instanceof Page || !$value->id) return $sleepValue;
|
if(!$value instanceof Page || !$value->id) return $sleepValue;
|
||||||
// if $value is a Page (not a NullPage) then place it's ID in an array for storage
|
// if $value is a Page (not a NullPage) then place its ID in an array for storage
|
||||||
$this->isValidPage($value, $field, $page, true);
|
if($this->isValidPage($value, $field, $page)) {
|
||||||
$sleepValue[] = $value->id;
|
$sleepValue[] = $value->id;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// if $value isn't a PageArray then we'll store a blank array
|
// if $value isn't a PageArray then we'll store a blank array
|
||||||
if(!$value instanceof PageArray) return $sleepValue;
|
if(!$value instanceof PageArray) return $sleepValue;
|
||||||
// iterate through the array and place each Page ID
|
// iterate through the array and place each Page ID
|
||||||
foreach($value as $pg) {
|
foreach($value as $pg) {
|
||||||
if(!$pg->id) continue;
|
if($pg->id && $this->isValidPage($pg, $field, $page)) {
|
||||||
$this->isValidPage($pg, $field, $page, true);
|
|
||||||
$sleepValue[] = $pg->id;
|
$sleepValue[] = $pg->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $sleepValue;
|
return $sleepValue;
|
||||||
}
|
}
|
||||||
@@ -1076,10 +1084,14 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
|
|
||||||
$database = $this->wire()->database;
|
$database = $this->wire()->database;
|
||||||
|
|
||||||
if(!in_array($subfield, $this->nativeNames)) return false;
|
$key = array_search($subfield, $this->nativeNames);
|
||||||
|
if($key === false) return false;
|
||||||
|
$subfield = $this->nativeNames[$key];
|
||||||
|
|
||||||
// we let the custom field query matcher handle the '!=' scenario
|
// we let the custom field query matcher handle the '!=' scenario
|
||||||
if(!$database->isOperator($operator)) return $this->getMatchQueryCustom($query, $table, $subfield, $operator, $value);
|
if(!$database->isOperator($operator)) {
|
||||||
|
return $this->getMatchQueryCustom($query, $table, $subfield, $operator, $value);
|
||||||
|
}
|
||||||
|
|
||||||
if($subfield == 'created' || $subfield == 'modified' || $subfield == 'published') {
|
if($subfield == 'created' || $subfield == 'modified' || $subfield == 'published') {
|
||||||
if(!ctype_digit($value)) $value = strtotime($value);
|
if(!ctype_digit($value)) $value = strtotime($value);
|
||||||
@@ -1106,7 +1118,27 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
} else if($subfield == 'name') {
|
} else if($subfield == 'name') {
|
||||||
$value = $this->wire()->sanitizer->pageName($value, Sanitizer::toAscii);
|
$value = $this->wire()->sanitizer->pageName($value, Sanitizer::toAscii);
|
||||||
|
|
||||||
} else $value = (int) $value;
|
} else {
|
||||||
|
$value = (int) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = $query->selector->values();
|
||||||
|
|
||||||
|
if(count($values) > 1 && $operator === '=') {
|
||||||
|
// we are in an OR match among multiple values
|
||||||
|
// make sure they all exist before sending to match query
|
||||||
|
// which performs regular joins (not left joins)
|
||||||
|
$valids = $this->getMatchQueryNativeValidValues($subfield, $values);
|
||||||
|
if(!count($valids)) {
|
||||||
|
// none of the values in the OR are possible matches
|
||||||
|
// let it pass through to query, as query will fail as it should
|
||||||
|
} else if(count($valids) === count($values)) {
|
||||||
|
// all values are valid
|
||||||
|
} else if(!in_array($value, $valids)) {
|
||||||
|
// value not valid and should be ignored since others are valid
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static $n = 0;
|
static $n = 0;
|
||||||
$table = $database->escapeTable($table);
|
$table = $database->escapeTable($table);
|
||||||
@@ -1119,6 +1151,56 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get values for $subfield that match those of given $values
|
||||||
|
*
|
||||||
|
* @param string $subfield
|
||||||
|
* @param array $values
|
||||||
|
* @return array
|
||||||
|
* @since 3.0.220
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function getMatchQueryNativeValidValues($subfield, array $values) {
|
||||||
|
|
||||||
|
$cacheKey = implode('|', $values);
|
||||||
|
|
||||||
|
if(isset($this->caches[__FUNCTION__])) {
|
||||||
|
// cache is used to prevent back-to-back duplicate queries
|
||||||
|
$a = $this->caches[__FUNCTION__];
|
||||||
|
if($a[0] === $cacheKey) return $a[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$wheres = array();
|
||||||
|
$binds = array();
|
||||||
|
|
||||||
|
foreach(array_values($values) as $i => $v) {
|
||||||
|
$wheres[] = "$subfield=:value$i";
|
||||||
|
$binds[":value$i"] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
$where = implode(' OR ', $wheres);
|
||||||
|
$sql = "SELECT id, $subfield FROM pages WHERE $where GROUP BY $subfield";
|
||||||
|
$query = $this->wire()->database->prepare($sql);
|
||||||
|
|
||||||
|
foreach($binds as $bindKey => $bindValue) {
|
||||||
|
$query->bindValue($bindKey, $bindValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->execute();
|
||||||
|
$valids = array();
|
||||||
|
|
||||||
|
while($row = $query->fetch(\PDO::FETCH_NUM)) {
|
||||||
|
$id = (int) $row[0];
|
||||||
|
$valids[$id] = $row[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->closeCursor();
|
||||||
|
|
||||||
|
$this->caches[__FUNCTION__] = array($cacheKey, $valids);
|
||||||
|
|
||||||
|
return $valids;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a DatabaseSelectQuery object to match a Page containing a matching custom subfield
|
* Update a DatabaseSelectQuery object to match a Page containing a matching custom subfield
|
||||||
*
|
*
|
||||||
@@ -1168,7 +1250,7 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
if(in_array($subfield, $this->nativeNames)) {
|
if(in_array($subfield, $this->nativeNames)) {
|
||||||
// fine then, we can handle that here when needed (like !=)
|
// fine then, we can handle that here when needed (like !=)
|
||||||
} else {
|
} else {
|
||||||
$subfield = $this->wire('fields')->get($subfield);
|
$subfield = $this->wire()->fields->get($subfield);
|
||||||
if(!$subfield) return false; // not a custom field
|
if(!$subfield) return false; // not a custom field
|
||||||
$subfield = $subfield->name;
|
$subfield = $subfield->name;
|
||||||
}
|
}
|
||||||
@@ -1202,7 +1284,7 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
/** @noinspection PhpUnusedLocalVariableInspection */
|
/** @noinspection PhpUnusedLocalVariableInspection */
|
||||||
list($itemField, $itemSubfield) = explode('.', $itemField);
|
list($itemField, $itemSubfield) = explode('.', $itemField);
|
||||||
if($itemField != $field->name) continue; // only group the same fields together in one selector query
|
if($itemField != $field->name) continue; // only group the same fields together in one selector query
|
||||||
if(!preg_match('/^' . $group . '@' . $field->name . '\.(([_a-zA-z0-9]+).*)$/', (string) $item, $matches)) continue;
|
if(!preg_match('/^' . $group . '@' . $field->name . '\.(([_a-zA-Z0-9]+).*)$/', (string) $item, $matches)) continue;
|
||||||
// extract the field name portion so we just get the subfield and rest of the selector
|
// extract the field name portion so we just get the subfield and rest of the selector
|
||||||
$selector .= "$matches[1], ";
|
$selector .= "$matches[1], ";
|
||||||
}
|
}
|
||||||
@@ -1523,20 +1605,21 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
$data = parent::___exportConfigData($field, $data);
|
$data = parent::___exportConfigData($field, $data);
|
||||||
if(!empty($data['parent_id']) && ctype_digit("$data[parent_id]")) {
|
if(!empty($data['parent_id']) && ctype_digit("$data[parent_id]")) {
|
||||||
// convert parent ID to parent path
|
// convert parent ID to parent path
|
||||||
$data['parent_id'] = $this->wire('pages')->get((int) $data['parent_id'])->path;
|
$data['parent_id'] = $this->wire()->pages->get((int) $data['parent_id'])->path;
|
||||||
}
|
}
|
||||||
|
$templates = $this->wire()->templates;
|
||||||
foreach(array('template_id', 'template_ids') as $key) {
|
foreach(array('template_id', 'template_ids') as $key) {
|
||||||
if(empty($data[$key])) continue;
|
if(empty($data[$key])) continue;
|
||||||
if(is_array($data[$key])) {
|
if(is_array($data[$key])) {
|
||||||
// convert array of template ids to template names
|
// convert array of template ids to template names
|
||||||
foreach($data[$key] as $k => $id) {
|
foreach($data[$key] as $k => $id) {
|
||||||
if(ctype_digit("$id")) continue;
|
if(ctype_digit("$id")) continue;
|
||||||
$template = $this->wire('templates')->get((int) $id);
|
$template = $templates->get((int) $id);
|
||||||
if($template) $data[$key][$k] = $template->name;
|
if($template) $data[$key][$k] = $template->name;
|
||||||
}
|
}
|
||||||
} else if(ctype_digit((string) $data[$key])) {
|
} else if(ctype_digit((string) $data[$key])) {
|
||||||
// convert template id to template name
|
// convert template id to template name
|
||||||
$template = $this->wire('templates')->get((int) $data[$key]);
|
$template = $templates->get((int) $data[$key]);
|
||||||
if($template) $data[$key] = $template->name;
|
if($template) $data[$key] = $template->name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,12 +38,15 @@ class PageField extends Field {
|
|||||||
*/
|
*/
|
||||||
public function getTemplateAndParentIds() {
|
public function getTemplateAndParentIds() {
|
||||||
|
|
||||||
|
$pages = $this->wire()->pages;
|
||||||
|
$templates = $this->wire()->templates;
|
||||||
|
|
||||||
$parentId = $this->get('parent_id');
|
$parentId = $this->get('parent_id');
|
||||||
$parentIds = array();
|
$parentIds = array();
|
||||||
$templateIds = array();
|
$templateIds = array();
|
||||||
|
|
||||||
if(empty($parentId)) {
|
if(empty($parentId)) {
|
||||||
$parentIds = array();
|
// $parentIds = array();
|
||||||
} else if(is_string($parentId)) {
|
} else if(is_string($parentId)) {
|
||||||
if(ctype_digit($parentId)) {
|
if(ctype_digit($parentId)) {
|
||||||
$parentIds = array((int) $parentId);
|
$parentIds = array((int) $parentId);
|
||||||
@@ -80,7 +83,7 @@ class PageField extends Field {
|
|||||||
if(ctype_digit("$v")) {
|
if(ctype_digit("$v")) {
|
||||||
$parentIds[] = (int) $v;
|
$parentIds[] = (int) $v;
|
||||||
} else if(strpos($v, '/')) {
|
} else if(strpos($v, '/')) {
|
||||||
$p = $this->wire()->pages->get($v);
|
$p = $pages->get($v);
|
||||||
if($p->id) $parentIds[] = $p->id;
|
if($p->id) $parentIds[] = $p->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +96,7 @@ class PageField extends Field {
|
|||||||
if(ctype_digit("$v")) {
|
if(ctype_digit("$v")) {
|
||||||
$templateIds[] = (int) $v;
|
$templateIds[] = (int) $v;
|
||||||
} else if($v) {
|
} else if($v) {
|
||||||
$template = $this->wire()->templates->get($v);
|
$template = $templates->get($v);
|
||||||
if($template instanceof Template) $templateIds[] = $template->id;
|
if($template instanceof Template) $templateIds[] = $template->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user