Skip to content

Commit

Permalink
[FEATURE] Allow recursive indexing of additional content
Browse files Browse the repository at this point in the history
Refactor the method to recursively fetch and combine content and files
from additional tables. Modify `GenericRepository` to support queries
filtering by parent table for recursive indexing.
  • Loading branch information
Christian Bülter committed Oct 30, 2024
1 parent 0a0b277 commit 5c294ac
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 29 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ChangeLog
Upcoming version
[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
[FEATURE] Make it possible to index multiple additional tables for a content element. https://github.com/tpwd/ke_search/issues/230
[FEATURE] Allow recursive indexing of additional content

Version 6.0.1, 25 October 2024
[BUGFIX] Add missing storage page and recursive option. Thanks to Torben Hansen. https://github.com/tpwd/ke_search/issues/251
Expand Down
25 changes: 25 additions & 0 deletions Classes/Domain/Repository/GenericRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,31 @@ public function findByReferenceField(
->fetchAllAssociative();
}

public function findByReferenceFieldAndParentTable(
string $table,
string $parentTable,
string $fieldName,
int $value,
bool $includeHiddenAndTimeRestricted = false
) {
$queryBuilder = $this->getQueryBuilder($table, $includeHiddenAndTimeRestricted);
return $queryBuilder
->select('*')
->from($table)
->where(
$queryBuilder->expr()->eq(
'parenttable',
$queryBuilder->createNamedParameter($parentTable)
),
$queryBuilder->expr()->eq(
$fieldName,
$queryBuilder->createNamedParameter($value, Connection::PARAM_INT)
)
)
->executeQuery()
->fetchAllAssociative();
}

public function tableExists(string $table): bool
{
$table = strip_tags(htmlentities($table));
Expand Down
102 changes: 76 additions & 26 deletions Classes/Service/AdditionalContentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,68 @@ public function init(array $indexerConfig)
* @return array
*/
public function getContentAndFilesFromAdditionalTables(array $ttContentRow): array
{
$combinedAdditionalContentAndFiles = [
'content' => '',
'files' => [],
];
foreach ($this->getConfigs($ttContentRow['CType']) as $config) {
$additionalContentAndFiles = $this->getContentAndFilesForSingleConfig(
$ttContentRow,
$ttContentRow['CType'],
$config
);
$combinedAdditionalContentAndFiles['content'] .= ' ' . $additionalContentAndFiles['content'];
$combinedAdditionalContentAndFiles['files'] = array_merge(
$combinedAdditionalContentAndFiles['files'],
$additionalContentAndFiles['files']
);
}
return [
'content' => trim($combinedAdditionalContentAndFiles['content']),
'files' => $combinedAdditionalContentAndFiles['files'],
];
}

protected function getContentAndFilesForSingleConfig(array $row, string $cType, array $config): array
{
$content = ' ';
$files = [];
foreach ($this->getProcessedConfigsForCType($ttContentRow['CType']) as $config) {
$genericRepository = GeneralUtility::makeInstance(GenericRepository::class);
$additionalTableContentRows = $genericRepository->findByReferenceField(
$genericRepository = GeneralUtility::makeInstance(GenericRepository::class);
if (isset($config['parentTable'])) {
$additionalRows = $genericRepository->findByReferenceFieldAndParentTable(
$config['table'],
$config['parentTable'],
$config['referenceFieldName'],
$ttContentRow['uid']
$row['uid']
);
foreach ($additionalTableContentRows as $additionalTableContentRow) {
foreach ($config['fields'] as $field) {
$content .= ' ' . ContentUtility::getPlainContentFromContentRow(
$additionalTableContentRow,
$field,
$GLOBALS['TCA'][$config['table']]['columns'][$field]['config']['type'] ?? ''
);
$files = array_merge($files, $this->findLinkedFiles($additionalTableContentRow, $field));
}
} else {
$additionalRows = $genericRepository->findByReferenceField(
$config['table'],
$config['referenceFieldName'],
$row['uid']
);
}
foreach ($additionalRows as $additionalRow) {
foreach ($config['fields'] as $field) {
$content .= ' ' . ContentUtility::getPlainContentFromContentRow(
$additionalRow,
$field,
$GLOBALS['TCA'][$config['table']]['columns'][$field]['config']['type'] ?? ''
);
$files = array_merge($files, $this->findLinkedFiles($additionalRow, $field));
}

// Get content from tables which have the current table as parent recursively
$subConfigs = $this->getConfigs($cType, $config['table']);
foreach ($subConfigs as $subConfig) {
$additionalContentAndFiles = $this->getContentAndFilesForSingleConfig(
$additionalRow,
$cType,
$subConfig
);
$content .= ' ' . $additionalContentAndFiles['content'];
$files = array_merge($files, $additionalContentAndFiles['files']);
}
}
return ['content' => trim($content), 'files' => $files];
Expand Down Expand Up @@ -108,7 +151,10 @@ protected function parseAndProcessAdditionalTablesConfiguration(): array
$cTypes = $this->findAllCTypesInConfiguration($tempAdditionalTableConfig);
$additionalTableConfig = [];
foreach ($cTypes as $cType) {
$additionalTableConfig[$cType] = $this->getUnprocessedConfigsForCType($tempAdditionalTableConfig, $cType);
$additionalTableConfig[$cType] = $this->getCombinedConfigsForCTypeFromRawConfig(
$tempAdditionalTableConfig,
$cType
);
}
return $additionalTableConfig;
}
Expand Down Expand Up @@ -167,7 +213,7 @@ public function findLinkedFiles(array $contentRow, string $field = 'bodytext'):
* @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
protected function findAllCTypesInConfiguration(array $additionalTableConfig): array
{
$cTypes = [];
foreach ($additionalTableConfig as $cType => $config) {
Expand All @@ -180,15 +226,15 @@ protected function findAllCTypesInConfiguration($additionalTableConfig): array
}

/**
* 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.
* Retrieves an array of configurations for a given content type. Expects the additional table config in ini
* format which may have multiple configurations for the same CType with indexes in it
* ("my_ctype.1", "my_ctype.2") and combines them into one array.
*
* @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.
* @param string $cType The content type for which configurations are to be fetched.
* @return array Array containing combined configurations specific to the given content type.
*/
protected function getUnprocessedConfigsForCType(array $additionalTableConfig, string $cType): array
protected function getCombinedConfigsForCTypeFromRawConfig(array $additionalTableConfig, string $cType): array
{
$configs = [];
foreach ($additionalTableConfig as $currentCType => $currentConfig) {
Expand All @@ -202,16 +248,20 @@ protected function getUnprocessedConfigsForCType(array $additionalTableConfig, s
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.
* Retrieves processed configurations for a given content type and, if given, the parent 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
protected function getConfigs(string $cType, string $parentTable = ''): array
{
return $this->processedAdditionalTableConfig[$cType] ?? $this->processedAdditionalTableConfig[$cType] ?? [];
$configs = $this->processedAdditionalTableConfig[$cType] ?? $this->processedAdditionalTableConfig[$cType] ?? [];
foreach ($configs as $key => $config) {
if ($parentTable != '' && $config['parentTable'] !== $parentTable) {
unset($configs[$key]);
}
}
return $configs;
}
}
34 changes: 31 additions & 3 deletions Documentation/Indexing/IndexerTypes/Pages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,19 @@ table
This is the table that holds the content.

referenceFieldName
This ist the field that holds the relation to the tt_content record (the
This is the field that holds the relation to the tt_content record (the
UID of the record). In EXT:bootstrap_package it is named `tt_content`,
in EXT:mask it is named `parentid`.

parentTable
The parent table is an optional setting. It's only necessary if you want to index
sub-elements of EXT:mask. For example If you have repeating elements in a mask
element which themselves have repeating elements. You can define the parent table
for the sub-elements here (see example below). Indexing will be done recursively.
If set the database query will contain a "WHERE parenttable = ..." condition. This
column exists in content elements from EXT:mask but not in content elements
from EXT:bootstrap_package.

fields[]
A list of database fields which should be indexed. If the field is
configured as type "file" in the TCA the indexer will check if it links
Expand Down Expand Up @@ -165,15 +174,34 @@ index mask elements (remember to also add
referenceFieldName = parentid
fields[] = tx_mask_content_item
This is an example how to add multiple additional tables for the same CType.

.. code-block:: ini
[mask_mytest]
table = tx_mask_repeating1
referenceFieldName = parentid
fields[] = tx_mask_name
[mask_mytest.1]
table = tx_mask_repeating2
referenceFieldName = parentid
fields[] = tx_mask_title
This is an example how to index sub-elements of additional tables (note the `parentTable` configuration line).

.. code-block:: ini
[mask_mytest]
table = tx_mask_repeating1
referenceFieldName = parentid
fields[] = tx_mask_string1
fields[] = tx_mask_name
[mask_mytest.1]
table = tx_mask_repeating2
parentTable = tx_mask_repeating1
referenceFieldName = parentid
fields[] = tx_mask_string2
fields[] = tx_mask_title
This is an example for a some mask elements:

Expand Down

0 comments on commit 5c294ac

Please sign in to comment.