Zum Inhalt springen

TCA searchFields numerisch

Erstellt am 4. Oktober 2012 · 7 Antworten · letzte Antwort am 19. Oktober 2012

Tags: Frage

sunixzs ·

Hallo liebe T3-Community,

da ist ein spezielles Problem bei der Suche im Typo3-Backend seit Typo3 4.6, welches ich noch nicht lösen konnte:

Im Typo3-Backend im Modul "Seite" und "Liste" gibt es unten auf jeder Seite ein Feld "Suchbegriff", mit dem man die aktuelle Seite und Unterseiten durchsuchen lassen kann.

Seit Typo3 4.6 muss dazu im TCA definiert sein, welche Felder durchsuchbar sein sollen.

Beispiel:

$TCA['fe_users']['ctrl']['searchFields'] = 'first_name,last_name';

Technisch werden bei der Suche alle numerischen Felder zusammengefasst und durchsucht, wenn man eine numerische Suchanfrage startet. (Die Quelle kenne ich nicht mehr - das habe ich irgendwo im Inet gelesen.)

Das Problem ist nun folgendes:
Ich habe eine Tabelle mit Postleitzahlen. Da Postleitzahlen immer fünfstellig sind und mit einer 0 beginnen können, ist dieses Feld in der Datenbank kein numerisches Feld (z. B. integer 5), sondern ein Textfeld (z. B. varchar 5). Typo3 findet somit kein Ergebnis, wenn ich nach einer Postleitzahl suche. Die Suchanfrage ist numerisch und das Datenbankfeld nicht - in der Datenbank wird aber nur in den numerischen Feldern gesucht, da eine numerische Suchanfrage kam.

Was einer Rat wie man die Suche wieder zum Laufen bringt?

Vielen Dank für euer Feedback.

Schönen Abend, Sun.

LuP ·

Hallo,

die BE-Suche findet auch Zeichenkette (nicht nur numerische Werte!). Für die Suchabfrage wird für den Vergleich den equal Operator genommen, wenn der Suchstring als Integer interpretiert werden kann, ansonst wird ein LIKE verwendet. In beiden Fällen werden alle in $GLOBALS['TCA'][$tableName]['ctrl']['searchFields'] angegebenen Felder berücksichtigt.

Beispiel fe_users Tabelle. Es gilt defaultmäßig:

$TCA['fe_users']['ctrl'][''searchFields'] =
'username,name,first_name,last_name,middle_name,address,telephone,fax,email,title,zip,city,country,company';

zip ist vom Typ String varchar(10). Bei mir (TYPO3 4.6) liefert die Suche in der Listenansicht eindeutig PLZ, die auch mit 0 anfangen.

VG,
LuP

sunixzs ·

Moin LuP,

vielen Dank für den schnellen Test.

Bei mir habe ich nun festgestellt, dass die ZIP-Felder durchsucht werden...
Es sei denn, es wird eine UID mit der gesuchten Nummer gefunden. Dann wird wohl das Feld "zip" ignoriert. Vllt. nicht ganz:

Suche : Treffer
010 : 4 Treffer aus Feld zip
257 : 1 Treffer aus Feld uid, obwohl zip 25709 vorhanden ist.
25709 : kein Treffer

Bei 010 weiß ich nicht, ob es als Int interpretiert wird - anscheinend nicht, da hierbei die Suche korrekt funktioniert.

257 wäre dann ein Int, welches dann korrekterweise den Datensatz mit der UID 257 liefert.

25709 würde als Int interpretiert werden, sollte aber in der Datenbank ein Ergebnis für die zip liefern.

SELECT * 
FROM  `tx_myext_domain_model_mytable` 
WHERE zip = 25709

Ein SQL-Statement liefert z. B. das Ergebnis beim equal-Operator, obwohl die Typisierung sogar falsch ist.

Ich würde mir die Suche auch gerne selber mal anschauen, kann sie aber nicht in t3lib finden.

Die Suche oben rechts im Backend ist in t3lib/search/class.t3lib_search_lifesearch.php zu finden. Wo findet man den Quelltext für die Suche, die auf jeder Seite unten zu finden ist?

LG sun

LuP ·
sunixzs schrieb

Bei 010 weiß ich nicht, ob es als Int interpretiert wird - anscheinend nicht, da hierbei die Suche korrekt funktioniert.

010 wird nicht als Integer interpretiert

sunixzs schrieb

25709 würde als Int interpretiert werden, sollte aber in der Datenbank ein Ergebnis für die zip liefern.

In der Tat.

Mit folgenden Tricks sollte es klappen:
man setzt explizit im TCA der entsprechenden Tabelle oder direkt in /typo3conf/extTables.php (hier im Beispiel fe_users):

$GLOBALS['TCA']['fe_users']['columns']['zip']['config']['eval'] = 'int';
$GLOBALS['TCA']['fe_users']['columns']['zip']['config']['search'] = 'pidonly';
sunixzs schrieb

Ich würde mir die Suche auch gerne selber mal anschauen, kann sie aber nicht in t3lib finden.

/typo3/class.db_list.inc, siehe function makeSearchString()

VG,
LuP

LuP ·

sorry, ich war ein bisschen zu voreilig. Die o.g. Tricks würden nur gelten, wenn das Feld wirklich vom Typ Integer ist. Dann würde die Abfrage sowohl die uid als auch andere in searchFields deklarierten Felder berücksichtigen.

Bei einem Feld vom Typ String würde es nicht funktionieren, denn die Evaluierung ist nicht erfüllt. Nach meiner Meinung würde hier nur eine Code-Änderung in makeSearchString() helfen. Man könnte einen zusätzlichen Check im if-Konstrukt schalten, um zu prüfen, ob im TCA eval = int gesetzt ist. Erst dann wäre der Suchstring als Integer zu interpretieren, um falsch positiv zu vermeiden, wie hier beim Feld zip der Fall ist.

VG,
LuP

sunixzs ·

Danke für dein Feedback.

Etwas spät, aber die Zeit rennt ja nur so... mein Feedback...

/typo3/class.db_list.inc -> makeSearchString() habe ich nun auf die Schnelle bearbeitet und den Integer-Part deaktiviert:

ca. Zeile 560:

// original: if (t3lib_utility_Math::canBeInterpretedAsInteger($this->searchString))
if (false && t3lib_utility_Math::canBeInterpretedAsInteger($this->searchString)) {

Damit dennoch nach einer UID gesucht werden kann, habe ich den wherePart im Else definiert:

ca. Zeile 578:

//original: $whereParts = array();
$whereParts = array(
	'uid=' . $this->searchString
);

Nun wäre es so, dass Integer-Felder garnicht mehr berücksichtig werden. Dazu habe ich etwas weiter unten die Bedingung geändert:

ca. Zeile 602:

//original: ($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/',$fieldConfig['eval'])))) {
($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time/',$fieldConfig['eval'])))) {

Hier die komplette geänderte Methode (/typo3/class.db_list.inc -> recordList -> makeSearchString()):

/**
 * Creates part of query for searching after a word ($this->searchString)
 * fields in input table.
 *
 * @param string $table Table, in which the fields are being searched.
 * @param integer $currentPid Page id for the possible search limit. -1 only if called from an old XCLASS.
 * @return string Returns part of WHERE-clause for searching, if applicable.
 */
function makeSearchString($table, $currentPid = -1) {
	$result = '';

	$currentPid = intval($currentPid);
	$tablePidField = ($table == 'pages' ? 'uid' : 'pid');

		// Make query, only if table is valid and a search string is actually defined:
	if ($this->searchString) {
		$result = ' AND 0=1';
		$searchableFields = $this->getSearchFields($table);
		if (count($searchableFields) > 0) {
				// Loading full table description - we need to traverse fields:
			t3lib_div::loadTCA($table);
			
			// original: if (t3lib_utility_Math::canBeInterpretedAsInteger($this->searchString) ) {
			if (false && t3lib_utility_Math::canBeInterpretedAsInteger($this->searchString) ) {
				$whereParts = array(
					'uid=' . $this->searchString
				);

				foreach($searchableFields as $fieldName)	{
					if (isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
						$fieldConfig = &$GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
						if ($fieldConfig['type'] == 'input' && $fieldConfig['eval'] && t3lib_div::inList($fieldConfig['eval'], 'int')) {
							$condition = $fieldName . '=' . $this->searchString;
							if (is_array($fieldConfig['search']) && in_array('pidonly', $fieldConfig['search']) && $currentPid > 0) {
								$condition = '(' . $condition . ' AND ' . $tablePidField . '=' . $currentPid . ')';
							}
							$whereParts[] = $condition;
						}
					}
				}
			} else {
				// original: $whereParts = array();
				$whereParts = array(
					'uid=' . $this->searchString
				);
				$like = '\'%' .
					$GLOBALS['TYPO3_DB']->quoteStr($GLOBALS['TYPO3_DB']->escapeStrForLike($this->searchString, $table), $table) .
					'%\'';
				foreach($searchableFields as $fieldName) {
					if (isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
						$fieldConfig = &$GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
						$format = 'LCASE(%s) LIKE LCASE(%s)';
						if (is_array($fieldConfig['search'])) {
							if (in_array('case', $fieldConfig['search'])) {
								$format = '%s LIKE %s';
							}
							if (in_array('pidonly', $fieldConfig['search']) && $currentPid > 0) {
								$format = '(' . $format . ' AND ' . $tablePidField . '=' . $currentPid . ')';
							}
							if ($fieldConfig['search']['andWhere']) {
								$format = '((' . $fieldConfig['search']['andWhere'] . ') AND (' . $format . '))';
							}
						}
						if ($fieldConfig['type'] == 'text' ||
								$fieldConfig['type'] == 'flex' ||
								// original: ($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/',$fieldConfig['eval'])))) {
								($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time/',$fieldConfig['eval'])))) {
							$whereParts[] = sprintf($format, $fieldName, $like);
						}
					}
				}
			}

				// If search-fields were defined (and there always are) we create the query:
			if (count($whereParts)) {
				$result = ' AND (' . implode(' OR ', $whereParts) . ')';
			}
		}
	}
	return $result;
}

Quick and dirty - bei nem Update wieder alles weg und keiner denkt daran - die Verbesserung der Entwickler einfach über den Haufen geworfen... aber es funktioniert erstmal.

LG Sun