Commit 463f6ec5 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Fix #3419 / typeAheadSql: Array mit nur einer Spalte oder andere KeyNamen als...

Fix #3419 / typeAheadSql: Array mit nur einer Spalte oder andere KeyNamen als key / value werden nicht korrekt behandelt.
TypeAhead.php: Added detection of missing ... LIMIT in FE_TYPEAHEAD_SQL. Make resultset always a two column array with columns key/value.
Database.php: Added function hasLimit() and makeArrayDict().
parent ba5e2b4a
......@@ -136,8 +136,8 @@ Setup a *report* to manage all *forms*: Create a Typo3 page and insert a content
10 {
# List of Forms: Do not show this list of forms if there is a form given by SIP.
# Table header.
sql = SELECT CONCAT('{{pageId:T}}&form=Form&') as Pagen, '#', 'Name', 'Title', 'Table' FROM (SELECT 1) AS fake WHERE '{{form:SE}}'=''
head = <table class="table table-hover">
sql = SELECT CONCAT('{{pageId:T}}&form=Form&') as _Pagen, '#', 'Name', 'Title', 'Table', '' FROM (SELECT 1) AS fake WHERE '{{form:SE}}'=''
head = <table class="table table-hover qfq-table-50">
tail = </table>
rbeg = <thead><tr>
rend = </tr></thead>
......@@ -146,7 +146,7 @@ Setup a *report* to manage all *forms*: Create a Typo3 page and insert a content
10 {
# All forms
sql = SELECT CONCAT('{{pageId:T}}&form=Form&r=', f.id) as Pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=Form&r=', f.id) as Paged FROM Form AS f ORDER BY f.name
sql = SELECT CONCAT('{{pageId:T}}&form=Form&r=', f.id) as _Pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=Form&r=', f.id) as _Paged FROM Form AS f ORDER BY f.name
rbeg = <tr>
rend = </tr>
fbeg = <td>
......@@ -1593,6 +1593,7 @@ SQL
* *typeAheadSql* = `SELECT ... AS 'key', ... AS 'value' WHERE name LIKE ? OR firstName LIKE ? LIMIT 100`
* If there is only one column in the SELECT statement, that one will be used and there is no dict (key/value pair).
* If there is no column `key` or no column `value`, than the first column becomes `key` and the second column becomes `value`.
* The query will be fired as a 'prepared statement'.
* The value, typed by the user, will be replaced on all places where a `?` appears.
* All `?` will be automatically surrounded by '%'. Therefore wildcard search is implemented: `... LIKE '%<?>%' ...`
......
......@@ -656,4 +656,77 @@ class Database {
return $feSpecNative;
}
/**
* Checks if there is the SQL keyword 'limit' at the end of the SQL statement.
* returns true for '... LIMIT', '.... LIMIT 1, ... LIMIT 1,2, ... LIMIT 1 , 2
*
* @param $sql
* @return bool
*/
public function hasLimit($sql) {
$sql = trim(strtolower($sql));
$arr = explode(' ', $sql);
$ii = 3;
array_pop($arr); // the last token can't be 'limit'
while ($ii > 0) {
$item = array_pop($arr);
if ($item === null) {
return false;
}
if ($item != '') {
if ($item == 'limit') {
return true;
} else {
$ii--;
}
}
}
return false;
}
/**
* $arr will be converted to a two column array with keys $keyName1 and $keyName2.
* $arr might be one column or more columns.
* Only when $keyName1 and $keyName2 exist, those will be used. Else the first column becomes $keyName1 and the second becomes $keyName2.
* If there is only one column, that column will be doubled.
*
* @param array $arr
* @param string $keyName1
* @param string $keyName2
* @return array
*/
public function makeArrayDict(array $arr, $keyName1, $keyName2) {
$keyName = 0;
$valueName = 1;
$new = array();
if ($arr == array() || $arr === null) {
return array();
}
$row = $arr[0];
$keys = array_keys($row);
if (count($row) < 2) {
$keyName = $keys[0];
$valueName = $keys[0];
} elseif (array_key_exists($keyName1, $row) && array_key_exists($keyName2, $row)) {
$keyName = $keyName1;
$valueName = $keyName2;
} else {
$keyName = $keys[0];
$valueName = $keys[1];
}
$row = array_shift($arr);
while (null !== $row) {
$new[] = [$keyName1 => $row[$keyName], $keyName2 => $row[$valueName]];
$row = array_shift($arr);
}
return $new;
}
}
\ No newline at end of file
......@@ -8,29 +8,13 @@
namespace qfq;
use TYPO3\CMS\Core\FormProtection\Exception;
//use TYPO3\CMS\Core\FormProtection\Exception;
require_once(__DIR__ . '/../store/Sip.php');
//require_once(__DIR__ . '/store/FillStoreForm.php');
require_once(__DIR__ . '/../store/Session.php');
require_once(__DIR__ . '/../Constants.php');
//require_once(__DIR__ . '/Save.php');
//require_once(__DIR__ . '/helper/KeyValueStringParser.php');
require_once(__DIR__ . '/../helper/Ldap.php');
//require_once(__DIR__ . '/helper/HelperFormElement.php');
//require_once(__DIR__ . '/exceptions/UserFormException.php');
//require_once(__DIR__ . '/exceptions/CodeException.php');
//require_once(__DIR__ . '/exceptions/DbException.php');
//require_once(__DIR__ . '/exceptions/ErrorHandler.php');
require_once(__DIR__ . '/../Database.php');
//require_once(__DIR__ . '/Evaluate.php');
//require_once(__DIR__ . '/BuildFormPlain.php');
//require_once(__DIR__ . '/BuildFormTable.php');
//require_once(__DIR__ . '/BuildFormBootstrap.php');
//require_once(__DIR__ . '/report/Report.php');
//require_once(__DIR__ . '/BodytextParser.php');
//require_once(__DIR__ . '/Delete.php');
//require_once(__DIR__ . '/form/FormAction.php');
class TypeAhead {
......@@ -89,22 +73,44 @@ class TypeAhead {
}
/**
* Do a wildcard serach on the prepared statement $config[FE_TYPEAHEAD_SQL].
* All '?' will be replaced by '%$value%'.
* If there is no 'LIMIT x' defined, append it.
* Returns an dict array [ API_TYPEAHEAD_KEY => key, API_TYPEAHEAD_VALUE => value ]
*
* @param array $config
* @param $query
* @return array|int
* @param string $value
* @return array
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
private function typeAheadSql(array $config, $query) {
private function typeAheadSql(array $config, $value) {
$values = array();
$query = '%' . $query . '%';
$cnt = substr_count($config[FE_TYPEAHEAD_SQL], '?');
$sql = $config[FE_TYPEAHEAD_SQL];
$value = '%' . $value . '%';
$cnt = substr_count($sql, '?');
if ($cnt == 0) {
throw new UserFormException("Missing at least one '?' in " . FE_TYPEAHEAD_SQL);
}
for ($ii = 0; $ii < $cnt; $ii++) {
$values[] = $query;
$values[] = $value;
}
return $this->db->sql($config[FE_TYPEAHEAD_SQL], ROW_REGULAR, $values);
if (!$this->db->hasLimit($sql)) {
$sql .= ' LIMIT ' . $config[FE_TYPEAHEAD_LIMIT];
}
$arr = $this->db->sql($sql, ROW_REGULAR, $values);
if ($arr == false || count($arr) == 0) {
return array();
}
return $this->db->makeArrayDict($arr, API_TYPEAHEAD_KEY, API_TYPEAHEAD_VALUE);
}
}
\ No newline at end of file
......@@ -373,7 +373,9 @@ class DatabaseTest extends AbstractDatabaseTest {
$this->assertEquals($expected, $this->db->getTableDefinition('Person'));
}
/**
*
*/
public function testSqlKeys() {
$keys = array();
......@@ -392,6 +394,71 @@ class DatabaseTest extends AbstractDatabaseTest {
$this->assertEquals(['id', 'name', 'id'], $keys);
}
/**
*
*/
public function testhasLimit() {
$this->assertEquals(false, $this->db->hasLimit(''));
$this->assertEquals(false, $this->db->hasLimit('SELECT'));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1'));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1,1'));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1,1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1,1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1,1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 ,1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 , 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 , 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 , 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 , 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 , 1 '));
$this->assertEquals(true, $this->db->hasLimit('SELECT ... LIMIT 1 , 1 '));
}
public function testMakeArrayDict() {
$this->assertEquals(array(), $this->db->makeArrayDict(array(), 'key', 'value'));
$arr[] = ['red', 'green'];
$expect[] = ['key' => 'red', 'value' => 'green'];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr[] = ['blue', 'orange'];
$expect[] = ['key' => 'blue', 'value' => 'orange'];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['key1' => 'red', 'value1' => 'green'], ['key1' => 'blue', 'value1' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['key' => 'red', 'value1' => 'green'], ['key' => 'blue', 'value1' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['key1' => 'red', 'value' => 'green'], ['key1' => 'blue', 'value' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['key' => 'red', 'value' => 'green'], ['key' => 'blue', 'value' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['value' => 'green', 'key' => 'red'], ['value' => 'orange', 'key' => 'blue']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['green'], ['orange']];
$expect = [['key' => 'green', 'value' => 'green'], ['key' => 'orange', 'value' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['value1' => 'green'], ['value1' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['key' => 'green'], ['key' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
$arr = [['value' => 'green'], ['value' => 'orange']];
$this->assertEquals($expect, $this->db->makeArrayDict($arr, 'key', 'value'));
}
/**
* @throws Exception
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment