mirror of
https://github.com/processwire/processwire.git
synced 2025-08-09 16:26:59 +02:00
Fix issue processwire/processwire-issues#973
This commit is contained in:
@@ -521,6 +521,16 @@ abstract class Fieldtype extends WireData implements Module {
|
|||||||
* Example: an integer or text Fieldtype might not consider a "0" to be empty,
|
* Example: an integer or text Fieldtype might not consider a "0" to be empty,
|
||||||
* whereas a Page reference would.
|
* whereas a Page reference would.
|
||||||
*
|
*
|
||||||
|
* This method is primarily used by the PageFinder::whereEmptyValuePossible()
|
||||||
|
* method to determine whether to include non-present (null) rows.
|
||||||
|
*
|
||||||
|
* 3.0.164+: If given a Selector object for $value, PageFinder is proposing
|
||||||
|
* handling the empty-value match condition internally rather than calling
|
||||||
|
* the Fieldtype’s getMatchQuery() method. Return true if this Fieldtype would
|
||||||
|
* prefer to handle the match, or false if not. Fieldtype modules do not need
|
||||||
|
* to consider this unless they want to override the default empty value match
|
||||||
|
* behavior in PageFinder::whereEmptyValuePossible().
|
||||||
|
*
|
||||||
* #pw-group-finding
|
* #pw-group-finding
|
||||||
*
|
*
|
||||||
* @param Field $field
|
* @param Field $field
|
||||||
|
@@ -1790,14 +1790,15 @@ class PageFinder extends Wire {
|
|||||||
// look in table that has no pages_id relation back to pages, using the LEFT JOIN / IS NULL trick
|
// look in table that has no pages_id relation back to pages, using the LEFT JOIN / IS NULL trick
|
||||||
// OR check for blank value as defined by the fieldtype
|
// OR check for blank value as defined by the fieldtype
|
||||||
|
|
||||||
|
static $tableCnt = 0;
|
||||||
|
|
||||||
|
$ft = $field->type;
|
||||||
$operator = $selector->operator;
|
$operator = $selector->operator;
|
||||||
$database = $this->wire('database');
|
$database = $this->wire('database');
|
||||||
static $tableCnt = 0;
|
|
||||||
$table = $database->escapeTable($field->table);
|
$table = $database->escapeTable($field->table);
|
||||||
$tableAlias = $table . "__blank" . (++$tableCnt);
|
$tableAlias = $table . "__blank" . (++$tableCnt);
|
||||||
$blankValue = $field->type->getBlankValue(new NullPage(), $field);
|
$blankValue = $ft->getBlankValue(new NullPage(), $field);
|
||||||
$blankIsObject = is_object($blankValue);
|
$blankIsObject = is_object($blankValue);
|
||||||
if($blankIsObject) $blankValue = '';
|
|
||||||
$whereType = 'OR';
|
$whereType = 'OR';
|
||||||
$sql = '';
|
$sql = '';
|
||||||
$operators = array(
|
$operators = array(
|
||||||
@@ -1808,29 +1809,42 @@ class PageFinder extends Wire {
|
|||||||
'>' => '<=',
|
'>' => '<=',
|
||||||
'>=' => '<'
|
'>=' => '<'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if($blankIsObject) $blankValue = '';
|
||||||
if(!isset($operators[$operator])) return false;
|
if(!isset($operators[$operator])) return false;
|
||||||
if($selector->not) $operator = $operators[$operator]; // reverse
|
if($selector->not) $operator = $operators[$operator]; // reverse
|
||||||
|
|
||||||
if($operator == '=') {
|
// ask Fieldtype if it would prefer to handle matching this empty value selector
|
||||||
|
if($ft->isEmptyValue($field, $selector)) {
|
||||||
|
// fieldtype will handle matching the selector in its getMatchQuery
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if(($operator === '=' || $operator === '!=') && $ft->isEmptyValue($field, $value) && $ft->isEmptyValue($field, '0000-00-00')) {
|
||||||
|
// matching empty in date, datetime, timestamp column with equals or not-equals condition
|
||||||
|
// non-presence of row is required in order to match empty/blank (in MySQL 8.x)
|
||||||
|
$is = $operator === '=' ? 'IS' : 'IS NOT';
|
||||||
|
$sql = "$tableAlias.pages_id $is NULL ";
|
||||||
|
|
||||||
|
} else if($operator === '=') {
|
||||||
// equals
|
// equals
|
||||||
// non-presence of row is equal to value being blank
|
// non-presence of row is equal to value being blank
|
||||||
$bindKey = $query->bindValueGetKey($blankValue);
|
$bindKey = $query->bindValueGetKey($blankValue);
|
||||||
if($field->type->isEmptyValue($field, $value)) {
|
if($ft->isEmptyValue($field, $value)) {
|
||||||
$sql = "$tableAlias.pages_id IS NULL OR ($tableAlias.data=$bindKey";
|
$sql = "$tableAlias.pages_id IS NULL OR ($tableAlias.data=$bindKey";
|
||||||
} else {
|
} else {
|
||||||
$sql = "($tableAlias.data=$bindKey";
|
$sql = "($tableAlias.data=$bindKey";
|
||||||
}
|
}
|
||||||
if($value !== "0" && $blankValue !== "0" && !$field->type->isEmptyValue($field, "0")) {
|
if($value !== "0" && $blankValue !== "0" && !$ft->isEmptyValue($field, "0")) {
|
||||||
// if zero is not considered an empty value, exclude it from matching
|
// if zero is not considered an empty value, exclude it from matching
|
||||||
// if the search isn't specifically for a "0"
|
// if the search isn't specifically for a "0"
|
||||||
$sql .= " AND $tableAlias.data!='0'";
|
$sql .= " AND $tableAlias.data!='0'";
|
||||||
}
|
}
|
||||||
$sql .= ")";
|
$sql .= ")";
|
||||||
|
|
||||||
} else if($operator == '!=' || $operator == '<>') {
|
} else if($operator === '!=' || $operator === '<>') {
|
||||||
// not equals
|
// not equals
|
||||||
// $whereType = 'AND';
|
// $whereType = 'AND';
|
||||||
if($value === "0" && !$field->type->isEmptyValue($field, "0")) {
|
if($value === "0" && !$ft->isEmptyValue($field, "0")) {
|
||||||
// may match rows with no value present
|
// may match rows with no value present
|
||||||
$sql = "$tableAlias.pages_id IS NULL OR ($tableAlias.data!='0'";
|
$sql = "$tableAlias.pages_id IS NULL OR ($tableAlias.data!='0'";
|
||||||
|
|
||||||
@@ -1840,7 +1854,7 @@ class PageFinder extends Wire {
|
|||||||
} else {
|
} else {
|
||||||
$bindKey = $query->bindValueGetKey($blankValue);
|
$bindKey = $query->bindValueGetKey($blankValue);
|
||||||
$sql = "$tableAlias.pages_id IS NOT NULL AND ($tableAlias.data!=$bindKey";
|
$sql = "$tableAlias.pages_id IS NOT NULL AND ($tableAlias.data!=$bindKey";
|
||||||
if($blankValue !== "0" && !$field->type->isEmptyValue($field, "0")) {
|
if($blankValue !== "0" && !$ft->isEmptyValue($field, "0")) {
|
||||||
$sql .= " OR $tableAlias.data='0'";
|
$sql .= " OR $tableAlias.data='0'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1848,7 +1862,7 @@ class PageFinder extends Wire {
|
|||||||
|
|
||||||
} else if($operator == '<' || $operator == '<=') {
|
} else if($operator == '<' || $operator == '<=') {
|
||||||
// less than
|
// less than
|
||||||
if($value > 0 && $field->type->isEmptyValue($field, "0")) {
|
if($value > 0 && $ft->isEmptyValue($field, "0")) {
|
||||||
// non-rows can be included as counting for 0
|
// non-rows can be included as counting for 0
|
||||||
$bindKey = $query->bindValueGetKey($value);
|
$bindKey = $query->bindValueGetKey($value);
|
||||||
$sql = "$tableAlias.pages_id IS NULL OR $tableAlias.data$operator$bindKey";
|
$sql = "$tableAlias.pages_id IS NULL OR $tableAlias.data$operator$bindKey";
|
||||||
@@ -1857,7 +1871,7 @@ class PageFinder extends Wire {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if($operator == '>' || $operator == '>=') {
|
} else if($operator == '>' || $operator == '>=') {
|
||||||
if($value < 0 && $field->type->isEmptyValue($field, "0")) {
|
if($value < 0 && $ft->isEmptyValue($field, "0")) {
|
||||||
// non-rows can be included as counting for 0
|
// non-rows can be included as counting for 0
|
||||||
$bindKey = $query->bindValueGetKey($value);
|
$bindKey = $query->bindValueGetKey($value);
|
||||||
$sql = "$tableAlias.pages_id IS NULL OR $tableAlias.data$operator$bindKey";
|
$sql = "$tableAlias.pages_id IS NULL OR $tableAlias.data$operator$bindKey";
|
||||||
|
@@ -202,13 +202,28 @@ class FieldtypeDatetime extends Fieldtype {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function isEmptyValue(Field $field, $value) {
|
public function isEmptyValue(Field $field, $value) {
|
||||||
return !strlen($value);
|
|
||||||
|
if(is_object($value) && $value instanceof Selector) {
|
||||||
|
// PageFinder is asking if it should let this Fieldtype handle the operator/value
|
||||||
|
// combination with potential empty value present in a Selector
|
||||||
|
$selector = $value;
|
||||||
|
$op = substr($selector->operator, 0, 1);
|
||||||
|
// tell PageFinder we will handle greater-than/less-than conditions in our getMatchQuery()
|
||||||
|
if($op === '>' || $op === '<') return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: 0000-00-00 intentionally returns true, which is what $value is when PageFinder is testing
|
||||||
|
// whether the Fieldtype recognizes an empty ISO-8601 date that it will convert to matching null
|
||||||
|
|
||||||
|
$value = trim($value, '-0 ');
|
||||||
|
|
||||||
|
return !strlen($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Match a date/time value in the database, as used by PageFinder
|
* Match a date/time value in the database, as used by PageFinder
|
||||||
*
|
*
|
||||||
* @param DatabaseQuerySelect $query
|
* @param PageFinderDatabaseQuerySelect $query
|
||||||
* @param string $table
|
* @param string $table
|
||||||
* @param string $subfield
|
* @param string $subfield
|
||||||
* @param string $operator
|
* @param string $operator
|
||||||
@@ -219,10 +234,11 @@ class FieldtypeDatetime extends Fieldtype {
|
|||||||
*/
|
*/
|
||||||
public function getMatchQuery($query, $table, $subfield, $operator, $value) {
|
public function getMatchQuery($query, $table, $subfield, $operator, $value) {
|
||||||
|
|
||||||
$database = $this->wire('database');
|
$database = $this->wire()->database;
|
||||||
$intValue = $this->_sanitizeValue($value);
|
$intValue = $this->_sanitizeValue($value);
|
||||||
$table = $database->escapeTable($table);
|
$table = $database->escapeTable($table);
|
||||||
$subfield = $subfield ? $database->escapeCol($subfield) : 'data';
|
$subfield = $subfield ? $database->escapeCol($subfield) : 'data';
|
||||||
|
$minDT = '1000-01-01 00:00:00'; // $maxDT = '9999-12-31 23:59:59';
|
||||||
|
|
||||||
if(is_string($value) && in_array($operator, array('%=', '^='))) {
|
if(is_string($value) && in_array($operator, array('%=', '^='))) {
|
||||||
// partial date string match
|
// partial date string match
|
||||||
@@ -232,9 +248,9 @@ class FieldtypeDatetime extends Fieldtype {
|
|||||||
if(!ctype_digit(str_replace(array('-', ' '), '', $value))) {
|
if(!ctype_digit(str_replace(array('-', ' '), '', $value))) {
|
||||||
throw new WireException("Invalid partial date string '$value' (numbers, hyphens and space only)");
|
throw new WireException("Invalid partial date string '$value' (numbers, hyphens and space only)");
|
||||||
}
|
}
|
||||||
$value = $database->escapeStr($value);
|
$value = str_replace(array('%', '_'), '', $value);
|
||||||
$value = $operator === '^=' ? "$value%" : "%$value%";
|
$value = $operator === '^=' ? "$value%" : "%$value%";
|
||||||
$query->where("$table.$subfield LIKE '$value'");
|
$query->where("$table.$subfield LIKE ?", $value);
|
||||||
|
|
||||||
} else if(!$database->isOperator($operator)) {
|
} else if(!$database->isOperator($operator)) {
|
||||||
// invalid operator
|
// invalid operator
|
||||||
@@ -244,21 +260,19 @@ class FieldtypeDatetime extends Fieldtype {
|
|||||||
// matching a populated value that successfully converted to unix timestamp
|
// matching a populated value that successfully converted to unix timestamp
|
||||||
$dateString = date('Y-m-d H:i:s', $intValue);
|
$dateString = date('Y-m-d H:i:s', $intValue);
|
||||||
if($dateString !== false) {
|
if($dateString !== false) {
|
||||||
$dateString = $database->escapeStr($dateString);
|
$query->where("$table.$subfield$operator?", $dateString);
|
||||||
$query->where("$table.$subfield$operator'$dateString'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// matching an empty value
|
// matching an empty value
|
||||||
$minDT = $database->escapeStr(date('Y-m-d H:i:s', 0));
|
|
||||||
if(in_array($operator, array('!=', '>', '>='))) {
|
if(in_array($operator, array('!=', '>', '>='))) {
|
||||||
// match NOT empty (!=0, >0)
|
// match NOT empty (!=0, >0)
|
||||||
$query->where("$table.$subfield>='$minDT'");
|
$query->where("$table.$subfield>=?", $minDT);
|
||||||
|
|
||||||
} else if(in_array($operator, array('=', '<', '<='))) {
|
} else if(in_array($operator, array('=', '<', '<='))) {
|
||||||
// match empty (=0, <0, <=0): match null or value below unix timestamp range
|
// match empty (=0, <0, <=0): match null or value below unix timestamp range
|
||||||
// this includes 0000-00-00 when present and used by MySQL version
|
// this includes 0000-00-00 when present and used by MySQL version
|
||||||
$query->where("$table.$subfield IS NULL OR $table.$subfield<'$minDT'");
|
$query->where("$table.$subfield IS NULL OR $table.$subfield<?", $minDT);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// unsupported operator
|
// unsupported operator
|
||||||
|
Reference in New Issue
Block a user