mirror of
https://github.com/Codappix/search_core.git
synced 2024-12-22 18:56:10 +01:00
Merge pull request #12 from DanielSiepmann/feature/resolve-relations
Feature/resolve relations
This commit is contained in:
commit
8565da8679
9 changed files with 366 additions and 30 deletions
|
@ -17,8 +17,6 @@ env:
|
|||
- typo3DatabasePassword=""
|
||||
matrix:
|
||||
- TYPO3_VERSION="~6.2"
|
||||
- TYPO3_VERSION="~7.6"
|
||||
- TYPO3_VERSION="dev-master"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
|
|
@ -20,9 +20,6 @@ namespace Leonmrni\SearchCore\Configuration;
|
|||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
const OPTION_DOES_NOT_EXIST = 1481623127;
|
||||
|
|
27
Classes/Domain/Index/TcaIndexer/InvalidArgumentException.php
Normal file
27
Classes/Domain/Index/TcaIndexer/InvalidArgumentException.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
const COLUMN_DOES_NOT_EXIST = 1481632388;
|
||||
const RECORD_NOT_FOUND = 1481643208;
|
||||
}
|
119
Classes/Domain/Index/TcaIndexer/RelationResolver.php
Normal file
119
Classes/Domain/Index/TcaIndexer/RelationResolver.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
namespace Leonmrni\SearchCore\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\SingletonInterface as Singleton;
|
||||
|
||||
/**
|
||||
* Resolves relations from TCA using TCA.
|
||||
*
|
||||
* E.g. resolves mm relations, items for selects, group db, etc.
|
||||
* Will replace the column with an array of resolved labels.
|
||||
*/
|
||||
class RelationResolver implements Singleton
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Backend\Form\DataPreprocessor
|
||||
* @inject
|
||||
*/
|
||||
protected $formEngine;
|
||||
|
||||
/**
|
||||
* Resolve relations for the given record.
|
||||
*
|
||||
* @param TcaTableService $service
|
||||
* @param array $record
|
||||
*/
|
||||
public function resolveRelationsForRecord(TcaTableService $service, array &$record)
|
||||
{
|
||||
$preprocessedData = $this->formEngine->renderRecordRaw(
|
||||
$service->getTableName(),
|
||||
$record['uid'],
|
||||
$record['pid'],
|
||||
$record
|
||||
);
|
||||
|
||||
foreach (array_keys($record) as $column) {
|
||||
try {
|
||||
$config = $service->getColumnConfig($column);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// Column is not configured.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->isRelation($config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$record[$column] = $this->resolveValue(
|
||||
$preprocessedData[$column],
|
||||
$config,
|
||||
$column
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given value from TYPO3 API response.
|
||||
*
|
||||
* As FormEngine uses an internal format, we resolve it to a usable format
|
||||
* for indexing.
|
||||
*
|
||||
* TODO: Unittest to break as soon as TYPO3 api has changed, so we know
|
||||
* exactly that we need to tackle this place.
|
||||
*
|
||||
* @param string $value The value from FormEngine to resolve.
|
||||
*
|
||||
* @return array<String>|string
|
||||
*/
|
||||
protected function resolveValue($value)
|
||||
{
|
||||
$newValue = [];
|
||||
if ($value === '' || $value === '0') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (strpos($value, '|') === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
foreach (\TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $value) as $value) {
|
||||
$value = substr($value, strpos($value, '|') + 1);
|
||||
$value = rawurldecode($value);
|
||||
$newValue[] = $value;
|
||||
}
|
||||
|
||||
return $newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array Column config.
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRelation(array &$config)
|
||||
{
|
||||
return isset($config['foreign_table'])
|
||||
|| (isset($config['items']) && is_array($config['items']))
|
||||
|| (isset($config['internal_type']) && strtolower($config['internal_type']) === 'db')
|
||||
;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ use Leonmrni\SearchCore\Domain\Index\IndexingException;
|
|||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
|
||||
/**
|
||||
* Encapsulate logik related to tca configuration.
|
||||
* Encapsulate logik related to TCA configuration.
|
||||
*/
|
||||
class TcaTableService
|
||||
{
|
||||
|
@ -51,6 +51,11 @@ class TcaTableService
|
|||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var RelationResolver
|
||||
*/
|
||||
protected $relationResolver;
|
||||
|
||||
/**
|
||||
* Inject log manager to get concrete logger from it.
|
||||
*
|
||||
|
@ -65,8 +70,11 @@ class TcaTableService
|
|||
* @param string $tableName
|
||||
* @param ConfigurationContainer $configuration
|
||||
*/
|
||||
public function __construct($tableName, ConfigurationContainerInterface $configuration)
|
||||
{
|
||||
public function __construct(
|
||||
$tableName,
|
||||
RelationResolver $relationResolver,
|
||||
ConfigurationContainerInterface $configuration
|
||||
) {
|
||||
if (!isset($GLOBALS['TCA'][$tableName])) {
|
||||
throw new IndexingException(
|
||||
'Table "' . $tableName . '" is not configured in TCA.',
|
||||
|
@ -77,6 +85,7 @@ class TcaTableService
|
|||
$this->tableName = $tableName;
|
||||
$this->tca = &$GLOBALS['TCA'][$this->tableName];
|
||||
$this->configuration = $configuration;
|
||||
$this->relationResolver = $relationResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +110,7 @@ class TcaTableService
|
|||
*/
|
||||
public function prepareRecord(array &$record)
|
||||
{
|
||||
// TODO: Resolve values from 'items' like static select, radio or checkbox.
|
||||
$this->relationResolver->resolveRelationsForRecord($this, $record);
|
||||
|
||||
if (isset($record['uid']) && !isset($record['search_identifier'])) {
|
||||
$record['search_identifier'] = $record['uid'];
|
||||
|
@ -144,8 +153,7 @@ class TcaTableService
|
|||
array_filter(
|
||||
array_keys($this->tca['columns']),
|
||||
function ($columnName) {
|
||||
$columnConfig = $this->tca['columns'][$columnName]['config'];
|
||||
return !$this->isRelation($columnConfig) && !$this->isSystemField($columnName);
|
||||
return !$this->isSystemField($columnName);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -158,15 +166,6 @@ class TcaTableService
|
|||
return implode(',', $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRelation(array &$columnConfig)
|
||||
{
|
||||
return isset($columnConfig['foreign_table']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return bool
|
||||
|
@ -189,4 +188,22 @@ class TcaTableService
|
|||
|
||||
return in_array($columnName, $systemFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $columnName
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getColumnConfig($columnName)
|
||||
{
|
||||
if (!isset($this->tca['columns'][$columnName])) {
|
||||
throw new InvalidArgumentException(
|
||||
'Column does not exist.',
|
||||
InvalidArgumentException::COLUMN_DOES_NOT_EXIST
|
||||
);
|
||||
}
|
||||
|
||||
return $this->tca['columns'][$columnName]['config'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@ abstract class AbstractFunctionalTestCase extends CoreTestCase
|
|||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpBackendUserFromFixture(1);
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->initializeLanguageObject();
|
||||
|
||||
// Provide necessary configuration for extension
|
||||
$this->importDataSet('Tests/Functional/Fixtures/BasicSetup.xml');
|
||||
$this->setUpFrontendRootPage(1, ['EXT:search_core/Tests/Functional/Fixtures/BasicSetup.ts']);
|
||||
|
|
123
Tests/Functional/Fixtures/Indexing/ResolveRelations.xml
Normal file
123
Tests/Functional/Fixtures/Indexing/ResolveRelations.xml
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<dataset>
|
||||
<tt_content>
|
||||
<uid>9</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<hidden>0</hidden>
|
||||
<sorting>72</sorting>
|
||||
<CType>textmedia</CType>
|
||||
<header>Record with relation to multiple sys category record</header>
|
||||
<bodytext>some content</bodytext>
|
||||
<media>0</media>
|
||||
<layout>0</layout>
|
||||
<deleted>0</deleted>
|
||||
<cols>0</cols>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<colPos>0</colPos>
|
||||
<categories>3</categories>
|
||||
<filelink_sorting>0</filelink_sorting>
|
||||
</tt_content>
|
||||
|
||||
<tt_content>
|
||||
<uid>10</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<hidden>0</hidden>
|
||||
<sorting>72</sorting>
|
||||
<CType>textmedia</CType>
|
||||
<header>Record with relation to a single sys category record</header>
|
||||
<bodytext>some content</bodytext>
|
||||
<media>0</media>
|
||||
<layout>0</layout>
|
||||
<deleted>0</deleted>
|
||||
<cols>0</cols>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<colPos>0</colPos>
|
||||
<categories>1</categories>
|
||||
<filelink_sorting>0</filelink_sorting>
|
||||
</tt_content>
|
||||
|
||||
<sys_category>
|
||||
<uid>1</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<deleted>0</deleted>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<sorting>1</sorting>
|
||||
<title>Category 1</title>
|
||||
<description>Category for testing</description>
|
||||
<parent>0</parent>
|
||||
<items>1</items>
|
||||
</sys_category>
|
||||
|
||||
<sys_category>
|
||||
<uid>2</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<deleted>0</deleted>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<sorting>1</sorting>
|
||||
<title>Category 2</title>
|
||||
<description>Another category for testing</description>
|
||||
<parent>1</parent>
|
||||
<items>1</items>
|
||||
</sys_category>
|
||||
|
||||
<sys_category>
|
||||
<uid>3</uid>
|
||||
<pid>1</pid>
|
||||
<tstamp>1480686370</tstamp>
|
||||
<crdate>1480686370</crdate>
|
||||
<deleted>1</deleted>
|
||||
<starttime>0</starttime>
|
||||
<endtime>0</endtime>
|
||||
<sorting>1</sorting>
|
||||
<title>Deleted category</title>
|
||||
<description></description>
|
||||
<parent>0</parent>
|
||||
<items>1</items>
|
||||
</sys_category>
|
||||
|
||||
<sys_category_record_mm>
|
||||
<uid_local>1</uid_local>
|
||||
<uid_foreign>9</uid_foreign>
|
||||
<tablenames>tt_content</tablenames>
|
||||
<fieldname>categories</fieldname>
|
||||
<sorting>2</sorting>
|
||||
<sorting_foreign>2</sorting_foreign>
|
||||
</sys_category_record_mm>
|
||||
<sys_category_record_mm>
|
||||
<uid_local>2</uid_local>
|
||||
<uid_foreign>9</uid_foreign>
|
||||
<tablenames>tt_content</tablenames>
|
||||
<fieldname>categories</fieldname>
|
||||
<sorting>1</sorting>
|
||||
<sorting_foreign>1</sorting_foreign>
|
||||
</sys_category_record_mm>
|
||||
<sys_category_record_mm>
|
||||
<uid_local>3</uid_local>
|
||||
<uid_foreign>9</uid_foreign>
|
||||
<tablenames>tt_content</tablenames>
|
||||
<fieldname>categories</fieldname>
|
||||
<sorting>3</sorting>
|
||||
<sorting_foreign>3</sorting_foreign>
|
||||
</sys_category_record_mm>
|
||||
|
||||
<sys_category_record_mm>
|
||||
<uid_local>2</uid_local>
|
||||
<uid_foreign>10</uid_foreign>
|
||||
<tablenames>tt_content</tablenames>
|
||||
<fieldname>categories</fieldname>
|
||||
<sorting>1</sorting>
|
||||
<sorting_foreign>1</sorting_foreign>
|
||||
</sys_category_record_mm>
|
||||
</dataset>
|
|
@ -109,7 +109,7 @@ class IndexTcaTableTest extends AbstractFunctionalTestCase
|
|||
$response = $this->client->request('typo3content/_search?q=*:*');
|
||||
|
||||
$this->assertTrue($response->isOK());
|
||||
$this->assertSame($response->getData()['hits']['total'], 2, 'Not exactly 2 document was indexed.');
|
||||
$this->assertSame($response->getData()['hits']['total'], 2, 'Not exactly 2 documents were indexed.');
|
||||
$this->assertArraySubset(
|
||||
['_source' => ['header' => 'Also indexable record']],
|
||||
$response->getData()['hits']['hits'][0],
|
||||
|
@ -123,4 +123,57 @@ class IndexTcaTableTest extends AbstractFunctionalTestCase
|
|||
'Record was not indexed.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function resolvesRelations()
|
||||
{
|
||||
$this->importDataSet('Tests/Functional/Fixtures/Indexing/ResolveRelations.xml');
|
||||
|
||||
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class)
|
||||
->get(IndexerFactory::class)
|
||||
->getIndexer('tt_content')
|
||||
->index()
|
||||
;
|
||||
|
||||
$response = $this->client->request('typo3content/_search?q=*:*');
|
||||
$this->assertTrue($response->isOK());
|
||||
$this->assertSame($response->getData()['hits']['total'], 3, 'Not exactly 3 documents were indexed.');
|
||||
|
||||
$response = $this->client->request('typo3content/_search?q=uid:9');
|
||||
$this->assertArraySubset(
|
||||
['_source' => [
|
||||
'uid' => '9',
|
||||
'CType' => 'textmedia', // Testing items
|
||||
'categories' => ['Category 2', 'Category 1'], // Testing mm (with sorting)
|
||||
]],
|
||||
$response->getData()['hits']['hits'][0],
|
||||
false,
|
||||
'Record was not indexed with resolved category relations to multiple values.'
|
||||
);
|
||||
|
||||
$response = $this->client->request('typo3content/_search?q=uid:10');
|
||||
$this->assertArraySubset(
|
||||
['_source' => [
|
||||
'uid' => '10',
|
||||
'CType' => 'textmedia',
|
||||
'categories' => ['Category 2'],
|
||||
]],
|
||||
$response->getData()['hits']['hits'][0],
|
||||
false,
|
||||
'Record was not indexed with resolved category relations to a single value.'
|
||||
);
|
||||
|
||||
$response = $this->client->request('typo3content/_search?q=uid:6');
|
||||
$this->assertArraySubset(
|
||||
['_source' => [
|
||||
'uid' => '6',
|
||||
'categories' => null,
|
||||
]],
|
||||
$response->getData()['hits']['hits'][0],
|
||||
false,
|
||||
'Record was indexed with resolved category relation, but should not have any.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,15 +14,14 @@ call_user_func(
|
|||
Leonmrni\SearchCore\Command\IndexCommandController::class,
|
||||
],
|
||||
],
|
||||
// Not yet, first finish whole indexing through command controller as it's more important.
|
||||
// 't3lib/class.t3lib_tcemain.php' => [
|
||||
// 'processCmdmapClass' => [
|
||||
// $extensionKey => \Leonmrni\SearchCore\Hook\DataHandler::class,
|
||||
// ],
|
||||
// 'processDatamapClass' => [
|
||||
// $extensionKey => \Leonmrni\SearchCore\Hook\DataHandler::class,
|
||||
// ],
|
||||
// ],
|
||||
't3lib/class.t3lib_tcemain.php' => [
|
||||
'processCmdmapClass' => [
|
||||
$extensionKey => \Leonmrni\SearchCore\Hook\DataHandler::class,
|
||||
],
|
||||
'processDatamapClass' => [
|
||||
$extensionKey => \Leonmrni\SearchCore\Hook\DataHandler::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue