[FEATURE] Allow flexible search in Suggest Wizard 46/55546/4
authorMathias Schreiber <mathias.schreiber@typo3.org>
Sun, 4 Feb 2018 10:08:31 +0000 (11:08 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 4 Feb 2018 17:41:14 +0000 (18:41 +0100)
The Suggest Wizard now allows to search for multiple
terms in field by splitting the searchterm by +.

This allows to search for a combination of values which
is helpful when dealing with large data sets.

Resolves: #61981
Releases: master
Change-Id: I764039a575ca5d9ccbd97dd3dc57f3947906a5c3
Reviewed-on: https://review.typo3.org/55546
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/backend/Classes/Form/Wizard/SuggestWizardDefaultReceiver.php
typo3/sysext/core/Documentation/Changelog/master/Feature-61981-SearchAllFieldsInSuggestWizard.rst [new file with mode: 0644]

index 76b8723..0e8fedc 100644 (file)
@@ -25,7 +25,9 @@ use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
+use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
 
 /**
  * Default implementation of a handler class for an ajax record selector.
@@ -110,7 +112,7 @@ class SuggestWizardDefaultReceiver
             $depth = (int)$config['pidDepth'];
             foreach ($pageIds as $pageId) {
                 if ($pageId > 0) {
-                    \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
+                    ArrayUtility::mergeRecursiveWithOverrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
                 }
             }
             $this->allowedPages = array_unique($allowedPages);
@@ -213,26 +215,16 @@ class SuggestWizardDefaultReceiver
     protected function prepareSelectStatement()
     {
         $expressionBuilder = $this->queryBuilder->expr();
-        $searchWholePhrase = !isset($this->config['searchWholePhrase']) || $this->config['searchWholePhrase'];
         $searchString = $this->params['value'];
-        $searchUid = (int)$searchString;
         if ($searchString !== '') {
-            $likeCondition = ($searchWholePhrase ? '%' : '') . $searchString . '%';
-            // Search in all fields given by label or label_alt
-            $selectFieldsList = $GLOBALS['TCA'][$this->table]['ctrl']['label'] . ',' . $GLOBALS['TCA'][$this->table]['ctrl']['label_alt'] . ',' . $this->config['additionalSearchFields'];
-            $selectFields = GeneralUtility::trimExplode(',', $selectFieldsList, true);
-            $selectFields = array_unique($selectFields);
-            $selectParts = $expressionBuilder->orX();
-            foreach ($selectFields as $field) {
-                $selectParts->add($expressionBuilder->like($field, $this->queryBuilder->createPositionalParameter($likeCondition)));
+            $splitStrings = $this->splitSearchString($searchString);
+            $constraints = [];
+            foreach ($splitStrings as $splitString) {
+                $constraints[] = $this->buildConstraintBlock($splitString);
             }
-
-            $searchClause = $expressionBuilder->orX($selectParts);
-            if ($searchUid > 0 && $searchUid == $searchString) {
-                $searchClause->add($expressionBuilder->eq('uid', $searchUid));
+            foreach ($constraints as $constraint) {
+                $this->queryBuilder->andWhere($expressionBuilder->andX($constraint));
             }
-
-            $this->queryBuilder->andWhere($expressionBuilder->orX($searchClause));
         }
         if (!empty($this->allowedPages)) {
             $pidList = array_map('intval', $this->allowedPages);
@@ -249,6 +241,47 @@ class SuggestWizardDefaultReceiver
     }
 
     /**
+     * Creates OR constraints for each split searchWord.
+     *
+     * @param string $searchString
+     * @return string|\TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression
+     */
+    protected function buildConstraintBlock(string $searchString)
+    {
+        $expressionBuilder = $this->queryBuilder->expr();
+        if (MathUtility::canBeInterpretedAsInteger($searchString) && (int)$searchString > 0) {
+            $searchClause = $expressionBuilder->eq('uid', (int)$searchString);
+        } else {
+            $searchWholePhrase = !isset($this->config['searchWholePhrase']) || $this->config['searchWholePhrase'];
+            $likeCondition = ($searchWholePhrase ? '%' : '') . $this->queryBuilder->escapeLikeWildcards($searchString) . '%';
+            // Search in all fields given by label or label_alt
+            $selectFieldsList = ($GLOBALS['TCA'][$this->table]['ctrl']['label'] ?? '') . ',' . ($GLOBALS['TCA'][$this->table]['ctrl']['label_alt'] ?? '') . ',' . $this->config['additionalSearchFields'];
+            $selectFields = GeneralUtility::trimExplode(',', $selectFieldsList, true);
+            $selectFields = array_unique($selectFields);
+            $selectParts = $expressionBuilder->orX();
+            foreach ($selectFields as $field) {
+                $selectParts->add($expressionBuilder->like($field, $this->queryBuilder->createPositionalParameter($likeCondition)));
+            }
+            $searchClause = $expressionBuilder->orX($selectParts);
+        }
+        return $searchClause;
+    }
+
+    /**
+     * Splits the search string by +
+     * This allows searching for "elements+basic" and will find results like
+     * "elements rte basic
+     *
+     * @param string $searchString
+     * @return array
+     */
+    protected function splitSearchString(string $searchString): array
+    {
+        $spitStrings = GeneralUtility::trimExplode('+', $searchString, true);
+        return $spitStrings;
+    }
+
+    /**
      * Selects all subpages of one page, optionally only up to a certain level
      *
      * @param int $uid The uid of the page
@@ -357,7 +390,7 @@ class SuggestWizardDefaultReceiver
      * The path is returned uncut, cutting has to be done by calling function.
      *
      * @param array $row The row
-     * @param array $record The record
+     * @param int $uid UID of the record
      * @return string The record-path
      */
     protected function getRecordPath(&$row, $uid)
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-61981-SearchAllFieldsInSuggestWizard.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-61981-SearchAllFieldsInSuggestWizard.rst
new file mode 100644 (file)
index 0000000..24d95b3
--- /dev/null
@@ -0,0 +1,25 @@
+.. include:: ../../Includes.txt
+
+=====================================================
+Feature: #61981 - Search all fields in Suggest Wizard
+=====================================================
+
+See :issue:`61981`
+
+Description
+===========
+
+Suggest Wizard search terms are split by +.
+This allows to search for a combination of strings in any given field.
+
+
+Impact
+======
+
+Searching for the term "elements+basic" will find the following results:
+
+* elements basic
+* elements rte basic
+* basic rte elements
+
+.. index:: Backend, TCA, NotScanned
\ No newline at end of file