Merge branch 'develop' into feature/75-make-index-configurable

# Conflicts:
#	Documentation/source/configuration/connections.rst
#	Documentation/source/connections.rst
This commit is contained in:
Justus Moroni 2018-05-17 23:51:55 +02:00
commit 2b56d4c2d5
129 changed files with 3906 additions and 1175 deletions

96
.phan/config.php Normal file
View file

@ -0,0 +1,96 @@
<?php
/**
* This configuration will be read and overlaid on top of the
* default configuration. Command line arguments will be applied
* after this file is read.
*/
return [
// Supported values: '7.0', '7.1', '7.2', null.
// If this is set to null,
// then Phan assumes the PHP version which is closest to the minor version
// of the php executable used to execute phan.
"target_php_version" => '7.0',
// Override to hardcode existence and types of (non-builtin) globals.
// Class names should be prefixed with '\\'.
// (E.g. ['_FOO' => '\\FooClass', 'page' => '\\PageClass', 'userId' => 'int'])
'globals_type_map' => [
'_EXTKEY' => 'string',
'EM_CONF' => 'array',
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors.
//
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'directory_list' => [
'Classes',
'.Build/vendor',
],
// A list of files to include in analysis
'file_list' => [
'ext_emconf.php',
'ext_tables.php',
'ext_localconf.php',
],
// A directory list that defines files that will be excluded
// from static analysis, but whose class and method
// information should be included.
//
// Generally, you'll want to include the directories for
// third-party code (such as "vendor/") in this list.
//
// n.b.: If you'd like to parse but not analyze 3rd
// party code, directories containing that code
// should be added to the `directory_list` as
// to `exclude_analysis_directory_list`.
"exclude_analysis_directory_list" => [
'.Build/vendor'
],
// A list of directories that should be parsed for class and
// method information. After excluding the directories
// defined in exclude_analysis_directory_list, the remaining
// files will be statically analyzed for errors.
//
// Thus, both first-party and third-party code being used by
// your application should be included in this list.
'directory_list' => [
'Classes',
// 'Tests',
'.Build/vendor',
],
// The number of processes to fork off during the analysis phase.
'processes' => 3,
// Add any issue types (such as 'PhanUndeclaredMethod')
// here to inhibit them from being reported
'suppress_issue_types' => [
'PhanDeprecatedFunction', // For now
'PhanParamTooMany', // For now, due to ObjectManager->get()
],
// A list of plugin files to execute.
// See https://github.com/phan/phan/tree/master/.phan/plugins for even more.
// (Pass these in as relative paths.
// The 0.10.2 release will allow passing 'AlwaysReturnPlugin' if referring to a plugin that is bundled with Phan)
'plugins' => [
// checks if a function, closure or method unconditionally returns.
'AlwaysReturnPlugin', // can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
// Checks for syntactically unreachable statements in
// the global scope or function bodies.
'UnreachableCodePlugin',
'DollarDollarPlugin',
'DuplicateArrayKeyPlugin',
'PregRegexCheckerPlugin',
'PrintfCheckerPlugin',
],
];

View file

@ -1,3 +1,11 @@
build:
nodes:
analysis:
project_setup:
override: true
tests:
override: [php-scrutinizer-run]
filter:
excluded_paths:
- 'Configuration/*'
@ -19,7 +27,7 @@ tools:
php_hhvm:
enabled: true
config:
use_undeclared_constant: false
use_undeclared_constant: false
php_mess_detector:
enabled: true
@ -34,5 +42,5 @@ tools:
enabled: true
# We generate code coverage during tests at travis and will send them here
external_code_coverage:
runs: 2
timeout: 1200
runs: 2
timeout: 1200

View file

@ -5,13 +5,15 @@ addons:
packages:
- oracle-java8-set-default
before_install:
- curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.0.deb && sudo dpkg -i --force-confnew elasticsearch-5.2.0.deb && sudo service elasticsearch start
- curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.0.deb && sudo dpkg -i --force-confnew elasticsearch-5.2.0.deb && sudo service elasticsearch restart
- mysql -u root -e 'GRANT ALL ON `typo3_ci_ft%`.* TO travis@127.0.0.1;'
language: php
php:
- 7.0
- 7.1
- 7.2
env:
global:
@ -23,6 +25,9 @@ env:
- typo3DatabaseHost="127.0.0.1"
- typo3DatabaseUsername="travis"
- typo3DatabasePassword=""
matrix:
- TYPO3_VERSION="~7.6"
- TYPO3_VERSION="~8.7"
matrix:
fast_finish: true
@ -33,6 +38,7 @@ services:
install: make install
script:
- make cgl
- make unitTests
- make functionalTests

View file

@ -22,7 +22,6 @@ namespace Codappix\SearchCore\Command;
use Codappix\SearchCore\Domain\Index\IndexerFactory;
use Codappix\SearchCore\Domain\Index\NoMatchingIndexerException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\CommandController;
/**
@ -48,7 +47,7 @@ class IndexCommandController extends CommandController
*
* @param string $identifier
*/
public function indexCommand($identifier)
public function indexCommand(string $identifier)
{
try {
$this->indexerFactory->getIndexer($identifier)->indexAllDocuments();
@ -63,7 +62,7 @@ class IndexCommandController extends CommandController
*
* @param string $identifier
*/
public function deleteCommand($identifier)
public function deleteCommand(string $identifier)
{
try {
$this->indexerFactory->getIndexer($identifier)->delete();

View file

@ -0,0 +1,56 @@
<?php
namespace Codappix\SearchCore\Compatibility;
/*
* Copyright (C) 2018 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\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Extbase\Object\Container\Container;
/**
* Register different concrete implementations, depending on current TYPO3 version.
* This way we can provide working implementations for multiple TYPO3 versions.
*/
class ImplementationRegistrationService
{
public static function registerImplementations()
{
$container = GeneralUtility::makeInstance(Container::class);
if (VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) >= 8000000) {
$container->registerImplementation(
\Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class,
\Codappix\SearchCore\Compatibility\TypoScriptService::class
);
$container->registerImplementation(
\Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface::class,
\Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService::class
);
} else {
$container->registerImplementation(
\Codappix\SearchCore\Compatibility\TypoScriptServiceInterface::class,
\Codappix\SearchCore\Compatibility\TypoScriptService76::class
);
$container->registerImplementation(
\Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface::class,
\Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService76::class
);
}
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Codappix\SearchCore\Compatibility;
/*
* Copyright (C) 2018 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\Core\TypoScript\TypoScriptService as CoreTypoScriptService;
/**
* Used since TYPO3 CMS 8.7.
*/
class TypoScriptService extends CoreTypoScriptService implements TypoScriptServiceInterface
{
}

View file

@ -0,0 +1,31 @@
<?php
namespace Codappix\SearchCore\Compatibility;
/*
* Copyright (C) 2018 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\Extbase\Service\TypoScriptService as CoreTypoScriptService;
/**
* Used before TYPO3 CMS 8.7.
*/
class TypoScriptService76 extends CoreTypoScriptService implements TypoScriptServiceInterface
{
}

View file

@ -0,0 +1,30 @@
<?php
namespace Codappix\SearchCore\Compatibility;
/*
* Copyright (C) 2018 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.
*/
/**
* Allows to use DI configuration to switch concrete implementation, depending
* on current TYPO3 Version.
*/
interface TypoScriptServiceInterface
{
public function convertPlainArrayToTypoScriptArray(array $plainArray);
}

View file

@ -39,7 +39,6 @@ class ConfigurationContainer implements ConfigurationContainerInterface
/**
* Inject settings via ConfigurationManager.
*
* @param ConfigurationManagerInterface $configurationManager
* @throws NoConfigurationException
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
@ -59,7 +58,7 @@ class ConfigurationContainer implements ConfigurationContainerInterface
* @return mixed
* @throws InvalidArgumentException
*/
public function get($path)
public function get(string $path)
{
$value = ArrayUtility::getValueByPath($this->settings, $path);
@ -77,7 +76,7 @@ class ConfigurationContainer implements ConfigurationContainerInterface
* @param string $path In dot notation.
* @return mixed
*/
public function getIfExists($path)
public function getIfExists(string $path)
{
return ArrayUtility::getValueByPath($this->settings, $path);
}

View file

@ -20,13 +20,11 @@ namespace Codappix\SearchCore\Configuration;
* 02110-1301, USA.
*/
use TYPO3\CMS\Core\SingletonInterface as Singleton;
/**
* Container of all configurations for extension.
* Always inject this to have a single place for configuration and parsing only once.
* Always inject this to have a single place for configuration.
*/
interface ConfigurationContainerInterface extends Singleton
interface ConfigurationContainerInterface
{
/**
* Returns the option defined by section and key.
@ -37,7 +35,7 @@ interface ConfigurationContainerInterface extends Singleton
*
* @throws InvalidArgumentException
*/
public function get($path);
public function get(string $path);
/**
* Same as get but will not throw an exception but return null.
@ -45,5 +43,5 @@ interface ConfigurationContainerInterface extends Singleton
* @param string $path In dot notation.
* @return mixed|null
*/
public function getIfExists($path);
public function getIfExists(string $path);
}

View file

@ -28,62 +28,44 @@ interface ConnectionInterface
/**
* Will add a new document.
*
* @param string $documentType
* @param array $document
*
* @return void
*/
public function addDocument($documentType, array $document);
public function addDocument(string $documentType, array $document);
/**
* Add the given documents.
*
* @param string $documentType
* @param array $documents
*
* @return void
*/
public function addDocuments($documentType, array $documents);
public function addDocuments(string $documentType, array $documents);
/**
* Will update an existing document.
*
* NOTE: Batch updating is not yet supported.
*
* @param string $documentType
* @param array $document
*
* @return void
*/
public function updateDocument($documentType, array $document);
public function updateDocument(string $documentType, array $document);
/**
* Will remove an existing document.
*
* NOTE: Batch deleting is not yet supported.
*
* @param string $documentType
* @param int $identifier
*
* @return void
*/
public function deleteDocument($documentType, $identifier);
public function deleteDocument(string $documentType, string $identifier);
/**
* Search by given request and return result.
*
* @param SearchRequestInterface $searchRequest
*
* @return SearchResultInterface
*/
public function search(SearchRequestInterface $searchRequest);
public function search(SearchRequestInterface $searchRequest) : SearchResultInterface;
/**
* Will delete the whole index / db.
*
* @param string $documentType
*
* @return void
*/
public function deleteIndex($documentType);
public function deleteIndex(string $documentType);
}

View file

@ -112,7 +112,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
$this->queryFactory = $queryFactory;
}
public function addDocument($documentType, array $document)
public function addDocument(string $documentType, array $document)
{
$this->withType(
$documentType,
@ -122,7 +122,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
);
}
public function deleteDocument($documentType, $identifier)
public function deleteDocument(string $documentType, string $identifier)
{
try {
$this->withType(
@ -132,11 +132,14 @@ class Elasticsearch implements Singleton, ConnectionInterface
}
);
} catch (\Elastica\Exception\NotFoundException $exception) {
$this->logger->debug('Tried to delete document in index, which does not exist.', [$documentType, $identifier]);
$this->logger->debug(
'Tried to delete document in index, which does not exist.',
[$documentType, $identifier]
);
}
}
public function updateDocument($documentType, array $document)
public function updateDocument(string $documentType, array $document)
{
$this->withType(
$documentType,
@ -146,7 +149,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
);
}
public function addDocuments($documentType, array $documents)
public function addDocuments(string $documentType, array $documents)
{
$this->withType(
$documentType,
@ -156,7 +159,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
);
}
public function deleteIndex($documentType)
public function deleteIndex(string $documentType)
{
$index = $this->connection->getClient()->getIndex($this->indexFactory->getIndexName());
@ -173,11 +176,8 @@ class Elasticsearch implements Singleton, ConnectionInterface
/**
* Execute given callback with Elastica Type based on provided documentType
*
* @param string $documentType
* @param callable $callback
*/
protected function withType($documentType, callable $callback)
protected function withType(string $documentType, callable $callback)
{
$type = $this->getType($documentType);
// TODO: Check whether it's to heavy to send it so often e.g. for every single document.
@ -191,12 +191,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
$type->getIndex()->refresh();
}
/**
* @param SearchRequestInterface $searchRequest
*
* @return SearchResultInterface
*/
public function search(SearchRequestInterface $searchRequest)
public function search(SearchRequestInterface $searchRequest) : SearchResultInterface
{
$this->logger->debug('Search for', [$searchRequest->getSearchTerm()]);
@ -207,12 +202,7 @@ class Elasticsearch implements Singleton, ConnectionInterface
return $this->objectManager->get(SearchResult::class, $searchRequest, $search->search());
}
/**
* @param string $documentType
*
* @return \Elastica\Type
*/
protected function getType($documentType)
protected function getType(string $documentType) : \Elastica\Type
{
return $this->typeFactory->getType(
$this->indexFactory->getIndex(

View file

@ -44,7 +44,7 @@ class Connection implements Singleton
/**
* @param ConfigurationContainerInterface $configuration
* @param \Elastica\Client $elasticaClient
* @param \Elastica\Client|null $elasticaClient
*/
public function __construct(
ConfigurationContainerInterface $configuration,
@ -52,9 +52,8 @@ class Connection implements Singleton
) {
$this->configuration = $configuration;
$this->elasticaClient = $elasticaClient;
if ($this->elasticaClient === null) {
$this->elasticaClient = new \Elastica\Client([
if ($elasticaClient === null) {
$elasticaClient = new \Elastica\Client([
'host' => $this->configuration->get('connections.elasticsearch.host'),
'port' => $this->configuration->get('connections.elasticsearch.port'),
// TODO: Make configurable
@ -63,14 +62,13 @@ class Connection implements Singleton
// TODO: Make configurable.
// new \Elastica\Log($this->elasticaClient);
}
$this->elasticaClient = $elasticaClient;
}
/**
* Get the concrete client for internal usage!
*
* @return \Elastica\Client
*/
public function getClient()
public function getClient() : \Elastica\Client
{
return $this->elasticaClient;
}

View file

@ -44,13 +44,8 @@ class DocumentFactory implements Singleton
/**
* Creates document from document.
*
* @param string $documentType
* @param array $document
*
* @return \Elastica\Document
*/
public function getDocument($documentType, array $document)
public function getDocument(string $documentType, array $document) : \Elastica\Document
{
// TODO: Use DocumentType for further configuration.
@ -70,13 +65,8 @@ class DocumentFactory implements Singleton
/**
* Creates documents based on documents.
*
* @param string $documentType
* @param array $documents
*
* @return array
*/
public function getDocuments($documentType, array $documents)
public function getDocuments(string $documentType, array $documents) : array
{
foreach ($documents as &$document) {
$document = $this->getDocument($documentType, $document);

View file

@ -22,6 +22,7 @@ namespace Codappix\SearchCore\Connection\Elasticsearch;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Connection\FacetInterface;
use Codappix\SearchCore\Connection\FacetOptionInterface;
class Facet implements FacetInterface
{
@ -45,25 +46,26 @@ class Facet implements FacetInterface
*/
protected $options = [];
public function __construct($name, array $aggregation, ConfigurationContainerInterface $configuration)
public function __construct(string $name, array $aggregation, ConfigurationContainerInterface $configuration)
{
$this->name = $name;
$this->buckets = $aggregation['buckets'];
$this->field = $configuration->getIfExists('searching.facets.' . $this->name . '.field') ?: '';
$config = $configuration->getIfExists('searching.facets.' . $this->name) ?: [];
foreach ($config as $configEntry) {
if (isset($configEntry['field'])) {
$this->field = $configEntry['field'];
break;
}
}
}
/**
* @return string
*/
public function getName()
public function getName() : string
{
return $this->name;
}
/**
* @return string
*/
public function getField()
public function getField() : string
{
return $this->field;
}
@ -73,7 +75,7 @@ class Facet implements FacetInterface
*
* @return array<FacetOptionInterface>
*/
public function getOptions()
public function getOptions() : array
{
$this->initOptions();

View file

@ -29,6 +29,11 @@ class FacetOption implements FacetOptionInterface
*/
protected $name = '';
/**
* @var string
*/
protected $displayName = '';
/**
* @var int
*/
@ -40,21 +45,21 @@ class FacetOption implements FacetOptionInterface
public function __construct(array $bucket)
{
$this->name = $bucket['key'];
$this->displayName = isset($bucket['key_as_string']) ? $bucket['key_as_string'] : $this->getName();
$this->count = $bucket['doc_count'];
}
/**
* @return string
*/
public function getName()
public function getName() : string
{
return $this->name;
}
/**
* @return int
*/
public function getCount()
public function getDisplayName() : string
{
return $this->displayName;
}
public function getCount() : int
{
return $this->count;
}

View file

@ -22,10 +22,8 @@ namespace Codappix\SearchCore\Connection\Elasticsearch;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Configuration\InvalidArgumentException;
use Elastica\Exception\ResponseException;
use TYPO3\CMS\Core\SingletonInterface as Singleton;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* Factory to get indexes.
@ -39,6 +37,21 @@ class IndexFactory implements Singleton
*/
protected $configuration;
/**
* @var \TYPO3\CMS\Core\Log\Logger
*/
protected $logger;
/**
* Inject log manager to get concrete logger from it.
*
* @param \TYPO3\CMS\Core\Log\LogManager $logManager
*/
public function injectLogger(\TYPO3\CMS\Core\Log\LogManager $logManager)
{
$this->logger = $logManager->getLogger(__CLASS__);
}
/**
* @param ConfigurationContainerInterface $configuration
*/
@ -59,36 +72,33 @@ class IndexFactory implements Singleton
/**
* Get an index bases on TYPO3 table name.
*
* @param Connection $connection
* @param string $documentType
*
* @return \Elastica\Index
*/
public function getIndex(Connection $connection, $documentType)
public function getIndex(Connection $connection, string $documentType) : \Elastica\Index
{
$index = $connection->getClient()->getIndex($this->getIndexName());
if ($index->exists() === false) {
$index->create($this->getConfigurationFor($documentType));
$config = $this->getConfigurationFor($documentType);
$this->logger->debug(sprintf('Create index %s.', $documentType), [$documentType, $config]);
$index->create($config);
$this->logger->debug(sprintf('Created index %s.', $documentType), [$documentType]);
}
return $index;
}
/**
* @param string $documentType
*
* @return array
*/
protected function getConfigurationFor($documentType)
protected function getConfigurationFor(string $documentType) : array
{
try {
$configuration = $this->configuration->get('indexing.' . $documentType . '.index');
if (isset($configuration['analysis']['analyzer'])) {
foreach ($configuration['analysis']['analyzer'] as $key => $analyzer) {
$configuration['analysis']['analyzer'][$key] = $this->prepareAnalyzerConfiguration($analyzer);
foreach (['analyzer', 'filter'] as $optionsToExpand) {
if (isset($configuration['analysis'][$optionsToExpand])) {
foreach ($configuration['analysis'][$optionsToExpand] as $key => $options) {
$configuration['analysis'][$optionsToExpand][$key] = $this->prepareAnalyzerConfiguration(
$options
);
}
}
}
@ -98,14 +108,9 @@ class IndexFactory implements Singleton
}
}
/**
* @param array $analyzer
*
* @return array
*/
protected function prepareAnalyzerConfiguration(array $analyzer)
protected function prepareAnalyzerConfiguration(array $analyzer) : array
{
$fieldsToExplode = ['char_filter', 'filter'];
$fieldsToExplode = ['char_filter', 'filter', 'word_list'];
foreach ($fieldsToExplode as $fieldToExplode) {
if (isset($analyzer[$fieldToExplode])) {

View file

@ -44,12 +44,8 @@ class MappingFactory implements Singleton
/**
* Get an mapping based on type.
*
* @param \Elastica\Type $type
*
* @return \Elastica\Mapping
*/
public function getMapping(\Elastica\Type $type)
public function getMapping(\Elastica\Type $type) : \Elastica\Type\Mapping
{
$mapping = new \Elastica\Type\Mapping();
$mapping->setType($type);
@ -64,11 +60,7 @@ class MappingFactory implements Singleton
return $mapping;
}
/**
* @param string $identifier
* @return array
*/
protected function getConfiguration($identifier)
protected function getConfiguration(string $identifier) : array
{
try {
return $this->configuration->get('indexing.' . $identifier . '.mapping');

View file

@ -24,10 +24,14 @@ use Codappix\SearchCore\Connection\FacetInterface;
use Codappix\SearchCore\Connection\ResultItemInterface;
use Codappix\SearchCore\Connection\SearchRequestInterface;
use Codappix\SearchCore\Connection\SearchResultInterface;
use Codappix\SearchCore\Domain\Model\QueryResultInterfaceStub;
use Codappix\SearchCore\Domain\Model\ResultItem;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
class SearchResult implements SearchResultInterface
{
use QueryResultInterfaceStub;
/**
* @var SearchRequestInterface
*/
@ -73,7 +77,7 @@ class SearchResult implements SearchResultInterface
/**
* @return array<ResultItemInterface>
*/
public function getResults()
public function getResults() : array
{
$this->initResults();
@ -85,14 +89,14 @@ class SearchResult implements SearchResultInterface
*
* @return array<FacetInterface>
*/
public function getFacets()
public function getFacets() : array
{
$this->initFacets();
return $this->facets;
}
public function getCurrentCount()
public function getCurrentCount() : int
{
return $this->result->count();
}
@ -104,7 +108,7 @@ class SearchResult implements SearchResultInterface
}
foreach ($this->result->getResults() as $result) {
$this->results[] = new ResultItem($result);
$this->results[] = new ResultItem($result->getData(), $result->getParam('_type'));
}
}
@ -153,41 +157,8 @@ class SearchResult implements SearchResultInterface
$this->position = 0;
}
// Extbase QueryResultInterface - Implemented to support Pagination of Fluid.
public function getQuery()
{
return $this->searchRequest;
}
public function getFirst()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502195121);
}
public function toArray()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502195135);
}
public function offsetExists($offset)
{
// Return false to allow Fluid to use appropriate getter methods.
return false;
}
public function offsetGet($offset)
{
throw new \BadMethodCallException('Use getter to fetch properties.', 1502196933);
}
public function offsetSet($offset, $value)
{
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196934);
}
public function offsetUnset($offset)
{
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196936);
}
}

View file

@ -21,7 +21,6 @@ namespace Codappix\SearchCore\Connection\Elasticsearch;
*/
use TYPO3\CMS\Core\SingletonInterface as Singleton;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* Factory to get indexes.
@ -32,13 +31,8 @@ class TypeFactory implements Singleton
{
/**
* Get an index bases on TYPO3 table name.
*
* @param \Elastica\Index $index
* @param string $documentType
*
* @return \Elastica\Type
*/
public function getType(\Elastica\Index $index, $documentType)
public function getType(\Elastica\Index $index, string $documentType) : \Elastica\Type
{
return $index->getType($documentType);
}

View file

@ -25,15 +25,12 @@ namespace Codappix\SearchCore\Connection;
*/
interface FacetInterface
{
/**
* @return string
*/
public function getName();
public function getName() : string;
/**
* Returns all possible options for this facet.
*
* @return array<FacetOptionInterface>
*/
public function getOptions();
public function getOptions() : array;
}

View file

@ -28,15 +28,17 @@ interface FacetOptionInterface
/**
* Returns the name of this option. Equivalent
* to value used for filtering.
*
* @return string
*/
public function getName();
public function getName() : string;
/**
* If a pre-rendered name is provided, this will be returned.
* Otherwise it's the same as getName().
*/
public function getDisplayName() : string;
/**
* Returns the number of found results for this option.
*
* @return int
*/
public function getCount();
public function getCount() : int;
}

View file

@ -28,15 +28,11 @@ interface FacetRequestInterface
/**
* The identifier of the facet, used as key in arrays and to get the facet
* from search request, etc.
*
* @return string
*/
public function getIdentifier();
public function getIdentifier() : string;
/**
* The field to use for facet building.
*
* @return string
* The config to use for facet building.
*/
public function getField();
public function getConfig() : array;
}

View file

@ -25,5 +25,20 @@ namespace Codappix\SearchCore\Connection;
*/
interface ResultItemInterface extends \ArrayAccess
{
/**
* Returns every information as array.
*
* Provide key/column/field => data.
*
* Used e.g. for dataprocessing.
*/
public function getPlainData() : array;
/**
* Returns the type of the item.
*
* That should make it easier to differentiate if multiple
* types are returned for one query.
*/
public function getType() : string;
}

View file

@ -20,24 +20,47 @@ namespace Codappix\SearchCore\Connection;
* 02110-1301, USA.
*/
use Codappix\SearchCore\Connection\ConnectionInterface;
use Codappix\SearchCore\Connection\FacetRequestInterface;
use Codappix\SearchCore\Domain\Search\SearchService;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
interface SearchRequestInterface extends QueryInterface
{
/**
* Returns the actual string the user searched for.
*/
public function getSearchTerm() : string;
public function hasFilter() : bool;
public function getFilter() : array;
public function setFilter(array $filter);
/**
* @return void
*/
public function addFacet(FacetRequestInterface $facet);
/**
* @return array<FacetRequestInterface>
*/
public function getFacets() : array;
/**
* Workaround for paginate widget support which will
* use the request to build another search.
*
* @return string
* @return void
*/
public function getSearchTerm();
public function setConnection(ConnectionInterface $connection);
/**
* @return bool
* Workaround for paginate widget support which will
* use the request to build another search.
*
* @return void
*/
public function hasFilter();
/**
* @return array
*/
public function getFilter();
public function setSearchService(SearchService $searchService);
}

View file

@ -30,19 +30,17 @@ interface SearchResultInterface extends \Iterator, \Countable, QueryResultInterf
/**
* @return array<ResultItemInterface>
*/
public function getResults();
public function getResults() : array;
/**
* Return all facets, if any.
*
* @return array<FacetInterface>
*/
public function getFacets();
public function getFacets() : array;
/**
* Returns the number of results in current result
*
* @return int
*/
public function getCurrentCount();
public function getCurrentCount() : int;
}

View file

@ -0,0 +1,59 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
* Copyright (C) 2017 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 Codappix\SearchCore\Compatibility\TypoScriptServiceInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
* Executes an existing TYPO3 DataProcessor on the given data.
*/
class ContentObjectDataProcessorAdapterProcessor implements ProcessorInterface
{
/**
* @var TypoScriptServiceInterface
*/
protected $typoScriptService;
public function __construct(TypoScriptServiceInterface $typoScriptService)
{
$this->typoScriptService = $typoScriptService;
}
public function processData(array $data, array $configuration) : array
{
$dataProcessor = GeneralUtility::makeInstance($configuration['_dataProcessor']);
$contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$contentObjectRenderer->data = $data;
if (isset($configuration['_table'])) {
$contentObjectRenderer->start($data, $configuration['_table']);
}
return $dataProcessor->process(
$contentObjectRenderer,
[],
$this->typoScriptService->convertPlainArrayToTypoScriptArray($configuration),
$data
);
}
}

View file

@ -25,13 +25,23 @@ namespace Codappix\SearchCore\DataProcessing;
*/
class CopyToProcessor implements ProcessorInterface
{
public function processRecord(array $record, array $configuration) : array
public function processData(array $record, array $configuration) : array
{
$all = [];
$target = [];
$this->addArray($all, $record);
$all = array_filter($all);
$record[$configuration['to']] = implode(PHP_EOL, $all);
$from = $record;
if (isset($configuration['from'])) {
$from = $record[$configuration['from']];
}
if (is_array($from)) {
$this->addArray($target, $from);
} else {
$target[] = (string) $from;
}
$target = array_filter($target);
$record[$configuration['to']] = implode(PHP_EOL, $target);
return $record;
}

View file

@ -25,9 +25,9 @@ namespace Codappix\SearchCore\DataProcessing;
*/
class GeoPointProcessor implements ProcessorInterface
{
public function processRecord(array $record, array $configuration) : array
public function processData(array $record, array $configuration) : array
{
if (! $this->canApply($record, $configuration)) {
if (! $this->isApplyable($record, $configuration)) {
return $record;
}
@ -39,7 +39,7 @@ class GeoPointProcessor implements ProcessorInterface
return $record;
}
protected function canApply(array $record, array $configuration) : bool
protected function isApplyable(array $record, array $configuration) : bool
{
if (!isset($record[$configuration['lat']])
|| !is_numeric($record[$configuration['lat']])

View file

@ -21,14 +21,13 @@ namespace Codappix\SearchCore\DataProcessing;
*/
/**
* All DataProcessing Processors should implement this interface, otherwise they
* will not be executed.
* All DataProcessing Processors should implement this interface.
*/
interface ProcessorInterface
{
/**
* Processes the given record.
* Processes the given data.
* Also retrieves the configuration for this processor instance.
*/
public function processRecord(array $record, array $configuration) : array;
public function processData(array $record, array $configuration) : array;
}

View file

@ -27,7 +27,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
*/
class RemoveProcessor implements ProcessorInterface
{
public function processRecord(array $record, array $configuration) : array