Skip to content

Commit

Permalink
[FEATURE] Make it possible to index multiple additional tables for a …
Browse files Browse the repository at this point in the history
…content element, see #230
  • Loading branch information
Christian Bülter committed Oct 29, 2024
1 parent 7ee474b commit fe487f1
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 19 deletions.
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
ChangeLog

Upcoming version
[FEATURE] Make it possible to index multiple additional tables for a content element. https://github.com/tpwd/ke_search/issues/230

Version 5.5.3, 25 October 2024
[BUGFIX] Undefined array key "sortByAdmin". Thanks to Simon Praetorius. https://github.com/tpwd/ke_search/issues/252
[BUGFIX] parse_ini_string(): Passing null to parameter #1 ($ini_string) of type string is deprecated. Thanks to medarob. https://github.com/tpwd/ke_search/issues/247
Expand Down
98 changes: 82 additions & 16 deletions Classes/Service/AdditionalContentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,7 @@ public function getContentAndFilesFromAdditionalTables(array $ttContentRow): arr
{
$content = ' ';
$files = [];
$config = false;
if (isset($this->processedAdditionalTableConfig[$ttContentRow['CType']])) {
$config = $this->processedAdditionalTableConfig[$ttContentRow['CType']];
}
if (is_array($config) & !empty($config['fields'])) {
foreach ($this->getProcessedConfigsForCType($ttContentRow['CType']) as $config) {
$genericRepository = GeneralUtility::makeInstance(GenericRepository::class);
$additionalTableContentRows = $genericRepository->findByReferenceField(
$config['table'],
Expand All @@ -65,40 +61,55 @@ public function getContentAndFilesFromAdditionalTables(array $ttContentRow): arr
foreach ($additionalTableContentRows as $additionalTableContentRow) {
foreach ($config['fields'] as $field) {
$content .= ' ' . ContentUtility::getPlainContentFromContentRow(
$additionalTableContentRow,
$field,
$GLOBALS['TCA'][$config['table']]['columns'][$field]['config']['type'] ?? ''
);
$additionalTableContentRow,
$field,
$GLOBALS['TCA'][$config['table']]['columns'][$field]['config']['type'] ?? ''
);
$files = array_merge($files, $this->findLinkedFiles($additionalTableContentRow, $field));
}
}
}
return ['content' => trim($content), 'files' => $files];
}

/**
* Parses and processes additional table configurations from the indexer configuration. Validates the
* configurations and structures them by content types.
*
* @return array Structured additional table configurations, organized by content types.
* Returns an empty array if any error occurs during parsing or if no valid table configuration is found.
*/
protected function parseAndProcessAdditionalTablesConfiguration(): array
{
$additionalTableConfig = false;
$tempAdditionalTableConfig = false;
// parse_ini_string will throw a warning if it could not parse the string.
// If the system is configured to turn a warning into an exception we catch it here.
try {
$additionalTableConfig = parse_ini_string($this->indexerConfig['additional_tables'] ?? '', true);
$tempAdditionalTableConfig = parse_ini_string($this->indexerConfig['additional_tables'] ?? '', true);
} catch (\Exception $e) {
$errorMessage =
'Error while parsing additional table configuration for indexer "' . $this->indexerConfig['title']
. '": ' . $e->getMessage();
// @extensionScannerIgnoreLine
$this->logger->error($errorMessage);
}
if ($additionalTableConfig === false) {
$errorMessage = 'Could not parse additional table configuration for indexer "' . $this->indexerConfig['title'] . '".';
if ($tempAdditionalTableConfig === false) {
$errorMessage = 'Could not parse additional table configuration for indexer "'
. $this->indexerConfig['title'] . '"';
// @extensionScannerIgnoreLine
$this->logger->error($errorMessage);
$additionalTableConfig = [];
return [];
}
foreach ($additionalTableConfig as $configKey => $config) {
foreach ($tempAdditionalTableConfig as $configKey => $config) {
if (!$this->genericRepository->tableExists($config['table'])) {
unset($additionalTableConfig[$configKey]);
unset($tempAdditionalTableConfig[$configKey]);
}
}
$cTypes = $this->findAllCTypesInConfiguration($tempAdditionalTableConfig);
$additionalTableConfig = [];
foreach ($cTypes as $cType) {
$additionalTableConfig[$cType] = $this->getUnprocessedConfigsForCType($tempAdditionalTableConfig, $cType);
}
return $additionalTableConfig;
}

Expand Down Expand Up @@ -127,6 +138,7 @@ public function findLinkedFiles(array $contentRow, string $field = 'bodytext'):
$fileObjects[] = $hrefInformation['file'];
}
} catch (\Exception $exception) {
// @extensionScannerIgnoreLine
$this->logger->error($exception->getMessage());
}
}
Expand All @@ -148,4 +160,58 @@ public function findLinkedFiles(array $contentRow, string $field = 'bodytext'):
}
return $fileObjects;
}

/**
* Finds all content types (CTypes) in the provided table configuration.
*
* @param array $additionalTableConfig The additional table configuration to search for cTypes.
* @return array An array of unique cTypes found in the configuration.
*/
protected function findAllCTypesInConfiguration($additionalTableConfig): array
{
$cTypes = [];
foreach ($additionalTableConfig as $cType => $config) {
[$cType] = explode('.', $cType);
if (!in_array($cType, $cTypes)) {
$cTypes[] = $cType;
}
}
return $cTypes;
}

/**
* Retrieves an array of unprocessed configurations for a given content type. Unprocessed means
* the CTypes names my have indexes in it ("my_ctype.1", "my_ctype.2") and each configuration
* configures only one table.
*
* @param array $additionalTableConfig Array containing configurations for various content types.
* @param string $cType The content type for which unprocessed configurations are to be fetched.
* @return array Array containing unprocessed configurations specific to the given content type.
*/
protected function getUnprocessedConfigsForCType(array $additionalTableConfig, string $cType): array
{
$configs = [];
foreach ($additionalTableConfig as $currentCType => $currentConfig) {
[$currentCType] = explode('.', $currentCType);
if ($currentCType === $cType) {
if (is_array($currentConfig) & !empty($currentConfig['fields'])) {
$configs[] = $currentConfig;
}
}
}
return $configs;
}


/**
* Retrieves processed configurations for a given content type. The result is a multidimensional array, in the first
* level there's one element per table.
*
* @param string $cType The content type identifier.
* @return array The processed configurations associated with the specified content type.
*/
protected function getProcessedConfigsForCType(string $cType): array
{
return $this->processedAdditionalTableConfig[$cType] ?? $this->processedAdditionalTableConfig[$cType] ?? [];
}
}
19 changes: 16 additions & 3 deletions Documentation/Indexing/IndexerTypes/Pages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ Options
first line (eg. `[custom_element]`)
The content type, stored as `CType` in the table `tt_content`. You will
also have to add this to :guilabel:`Content element types which
should be indexed`
should be indexed`. If your content element has multiple additional tables,
you can have multiple configurations for the same CType by adding a dot and
an index, e.g. "my_ctype.1", "my_ctype.2" which then will all internally be
mapped to the configuration for "my_ctype".

table
This is the table that holds the content.
Expand Down Expand Up @@ -152,8 +155,8 @@ index the bootstrap package element "accordion" (remember to also add
fields[] = bodytext
Add this to :guilabel:`Additional tables for content elements` to
index a mask element (remember to also add
`mask_list` to :guilabel:`Content element types which should be indexed`:
index mask elements (remember to also add
`mask_list` and `mask_mytest` to :guilabel:`Content element types which should be indexed`:

.. code-block:: ini
Expand All @@ -162,6 +165,16 @@ index a mask element (remember to also add
referenceFieldName = parentid
fields[] = tx_mask_content_item
[mask_mytest]
table = tx_mask_repeating1
referenceFieldName = parentid
fields[] = tx_mask_string1
[mask_mytest.1]
table = tx_mask_repeating2
referenceFieldName = parentid
fields[] = tx_mask_string2
This is an example for a some mask elements:

* The element `mask_custom_text_element` adds a field `tx_mask_customtext`
Expand Down

0 comments on commit fe487f1

Please sign in to comment.