mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-07 02:31:40 +09:00
Merge pull request #1517 from misol/db-search-operator
Improve the 'search' operators for DB quries to enable parentheses, quotation marks, AND, and OR.
This commit is contained in:
commit
0e52b363cf
4 changed files with 129 additions and 20 deletions
|
|
@ -237,24 +237,9 @@ class VariableBase
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'search':
|
case 'search':
|
||||||
$keywords = preg_split('/[\s,]+/', $value, 10, \PREG_SPLIT_NO_EMPTY);
|
$parsed_keywords = $this->_parseSearchKeywords($column, $value);
|
||||||
$conditions = array();
|
$where = $parsed_keywords[0];
|
||||||
$placeholders = implode(', ', array_fill(0, count($keywords), '?'));
|
$params = array_merge($params, $parsed_keywords[1]);
|
||||||
foreach ($keywords as $item)
|
|
||||||
{
|
|
||||||
if (substr($item, 0, 1) === '-')
|
|
||||||
{
|
|
||||||
$conditions[] = sprintf('%s NOT LIKE ?', $column);
|
|
||||||
$item = substr($item, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$conditions[] = sprintf('%s LIKE ?', $column);
|
|
||||||
}
|
|
||||||
$params[] = '%' . str_replace(['\\', '_', '%'], ['\\\\', '\_', '\%'], $item) . '%';
|
|
||||||
}
|
|
||||||
$conditions = implode(' AND ', $conditions);
|
|
||||||
$where = count($keywords) === 1 ? $conditions : "($conditions)";
|
|
||||||
break;
|
break;
|
||||||
case 'plus':
|
case 'plus':
|
||||||
$where = sprintf('%s = %s + %s', $column, $column, $is_expression ? $value : '?');
|
$where = sprintf('%s = %s + %s', $column, $column, $is_expression ? $value : '?');
|
||||||
|
|
@ -449,4 +434,90 @@ class VariableBase
|
||||||
throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $column . ' must contain no more than ' . $this->minlength . ' characters');
|
throw new \Rhymix\Framework\Exceptions\QueryError('Variable ' . $this->var . ' for column ' . $column . ' must contain no more than ' . $this->minlength . ' characters');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the search text.
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* @param string $value
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function _parseSearchKeywords($column, $value)
|
||||||
|
{
|
||||||
|
// Initialze the return values.
|
||||||
|
$where = '';
|
||||||
|
$params = array();
|
||||||
|
|
||||||
|
// parse the value (text);
|
||||||
|
$value = str_replace('"', '"', $value);
|
||||||
|
$keywords = preg_split('/(\([^\)]*?\))|(\-?\"[^\"]*?\")|[\s,]+/', trim($value), 10, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
$conditions = array();
|
||||||
|
$operators = array('AND', 'OR', '|');
|
||||||
|
// loop the parsed keywords or operators
|
||||||
|
foreach ($keywords as $item)
|
||||||
|
{
|
||||||
|
// treat parenthesis
|
||||||
|
if (substr($item, 0, 1) === '(' && substr($item, -1) === ')')
|
||||||
|
{
|
||||||
|
$item = trim(substr($item, 1, -1));
|
||||||
|
if ( $item !== "" )
|
||||||
|
{
|
||||||
|
$parsed_keywords = $this->_parseSearchKeywords($column, substr($item, 1, -1));
|
||||||
|
$conditions[] = $parsed_keywords[0];
|
||||||
|
$conditions[] = 'AND';
|
||||||
|
$params = array_merge($params, $parsed_keywords[1]);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process 'AND' or 'OR' operator
|
||||||
|
if (in_array($item, $operators))
|
||||||
|
{
|
||||||
|
if ($item === '|')
|
||||||
|
{
|
||||||
|
$item = 'OR';
|
||||||
|
}
|
||||||
|
// remove the last point (would be an operator)
|
||||||
|
array_pop($conditions);
|
||||||
|
$conditions[] = $item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$item = str_replace('"', '"', $item);
|
||||||
|
|
||||||
|
if (substr($item, 0, 1) === '-')
|
||||||
|
{
|
||||||
|
$conditions[] = sprintf('%s NOT LIKE ?', $column);
|
||||||
|
$item = substr($item, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$conditions[] = sprintf('%s LIKE ?', $column);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim quotation mark
|
||||||
|
if (substr($item, 0, 6) === substr($item, -6) && substr($item, -6) === '"')
|
||||||
|
{
|
||||||
|
$item = substr($item, 6, -6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass blank text
|
||||||
|
if (trim($item) === "")
|
||||||
|
{
|
||||||
|
array_pop($conditions);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[] = '%' . str_replace(['\\', '_', '%'], ['\\\\', '\_', '\%'], $item) . '%';
|
||||||
|
// if there is no operator, assume 'AND'
|
||||||
|
$conditions[] = 'AND';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove the last point (would be an operator)
|
||||||
|
array_pop($conditions);
|
||||||
|
$conditions = implode(' ', $conditions);
|
||||||
|
$where = count($keywords) === 1 ? $conditions : "($conditions)";
|
||||||
|
|
||||||
|
return [$where, $params];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,9 @@ class integration_searchView extends integration_search
|
||||||
$is_keyword = Context::get('is_keyword');
|
$is_keyword = Context::get('is_keyword');
|
||||||
// As the variables from GET or POST will be escaped by setRequestArguments method at Context class, the double_escape variable should be "FALSE", and also the escape function might be useful when this method was called from the other way (for not escaped keyword).
|
// As the variables from GET or POST will be escaped by setRequestArguments method at Context class, the double_escape variable should be "FALSE", and also the escape function might be useful when this method was called from the other way (for not escaped keyword).
|
||||||
$is_keyword = escape(trim(utf8_normalize_spaces($is_keyword)), false);
|
$is_keyword = escape(trim(utf8_normalize_spaces($is_keyword)), false);
|
||||||
if (mb_strlen($is_keyword, 'UTF-8') > 40)
|
if (mb_strlen($is_keyword, 'UTF-8') > 250)
|
||||||
{
|
{
|
||||||
$is_keyword = mb_substr($is_keyword, 0, 40);
|
$is_keyword = mb_substr($is_keyword, 0, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set page variables
|
// Set page variables
|
||||||
|
|
|
||||||
24
tests/_data/dbquery/selectTest3.xml
Normal file
24
tests/_data/dbquery/selectTest3.xml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<query id="selectTest3" action="select">
|
||||||
|
<tables>
|
||||||
|
<table name="documents" />
|
||||||
|
</tables>
|
||||||
|
<columns>
|
||||||
|
<column name="*" />
|
||||||
|
</columns>
|
||||||
|
<conditions>
|
||||||
|
<group pipe="and">
|
||||||
|
<condition operation="more" column="list_order" var="division" pipe="and" />
|
||||||
|
<condition operation="below" column="list_order" var="last_division" pipe="and" />
|
||||||
|
</group>
|
||||||
|
<group pipe="and">
|
||||||
|
<condition operation="search" column="title" var="s_title" />
|
||||||
|
<condition operation="search" column="content" var="s_content" pipe="or" />
|
||||||
|
</group>
|
||||||
|
</conditions>
|
||||||
|
<navigation>
|
||||||
|
<index var="sort_index" default="list_order" order="order_type" />
|
||||||
|
<list_count var="list_count" default="20" />
|
||||||
|
<page_count var="page_count" default="10" />
|
||||||
|
<page var="page" default="1" />
|
||||||
|
</navigation>
|
||||||
|
</query>
|
||||||
|
|
@ -70,6 +70,20 @@ class DBQueryParserTest extends \Codeception\TestCase\Test
|
||||||
$this->assertEquals([20, '20201021'], $params);
|
$this->assertEquals([20, '20201021'], $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSelectWithSearch()
|
||||||
|
{
|
||||||
|
$query = Rhymix\Framework\Parsers\DBQueryParser::loadXML(\RX_BASEDIR . 'tests/_data/dbquery/selectTest3.xml');
|
||||||
|
$args = array('division' => 1234, 'last_division' => 4567, 's_title' => '"I love you" -"I hate you"', 's_content' => '"I love you" -"I hate you"', 'page' => 3);
|
||||||
|
$sql = $query->getQueryString('rx_', $args);
|
||||||
|
$params = $query->getQueryParams();
|
||||||
|
|
||||||
|
$this->assertEquals('SELECT * FROM `rx_documents` AS `documents` ' .
|
||||||
|
'WHERE (`list_order` >= ? AND `list_order` < ?) AND ' .
|
||||||
|
'((`title` LIKE ? AND `title` NOT LIKE ?) OR (`content` LIKE ? AND `content` NOT LIKE ?)) ' .
|
||||||
|
'ORDER BY `list_order` ASC LIMIT 40, 20', $sql);
|
||||||
|
$this->assertEquals(['1234', '4567', '%I love you%', '%I hate you%', '%I love you%', '%I hate you%'], $params);
|
||||||
|
}
|
||||||
|
|
||||||
public function testJoin1()
|
public function testJoin1()
|
||||||
{
|
{
|
||||||
$query = Rhymix\Framework\Parsers\DBQueryParser::loadXML(\RX_BASEDIR . 'tests/_data/dbquery/selectJoinTest1.xml');
|
$query = Rhymix\Framework\Parsers\DBQueryParser::loadXML(\RX_BASEDIR . 'tests/_data/dbquery/selectJoinTest1.xml');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue