[TASK] Add docprops to classes

+ Updated version pointer
+ Replaces deprecated ArrayUtility for Core utility
This commit is contained in:
Benjamin Serfhos 2018-10-02 16:25:05 +02:00
parent 7940da14a7
commit a93a7af27f
77 changed files with 1299 additions and 693 deletions

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Command;
/*
@ -36,6 +37,7 @@ class IndexCommandController extends CommandController
/**
* @param IndexerFactory $factory
* @return void
*/
public function injectIndexerFactory(IndexerFactory $factory)
{
@ -46,6 +48,7 @@ class IndexCommandController extends CommandController
* Will index the given identifier.
*
* @param string $identifier
* @return void
*/
public function indexCommand(string $identifier)
{
@ -61,6 +64,7 @@ class IndexCommandController extends CommandController
* Will delete the given identifier.
*
* @param string $identifier
* @return void
*/
public function deleteCommand(string $identifier)
{

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Configuration;
/*
@ -20,8 +21,8 @@ namespace Codappix\SearchCore\Configuration;
* 02110-1301, USA.
*/
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Utility\ArrayUtility;
/**
* Container of all configurations for extension.
@ -39,7 +40,7 @@ class ConfigurationContainer implements ConfigurationContainerInterface
/**
* Inject settings via ConfigurationManager.
*
* @throws NoConfigurationException
* @param ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
@ -60,7 +61,7 @@ class ConfigurationContainer implements ConfigurationContainerInterface
*/
public function get(string $path)
{
$value = ArrayUtility::getValueByPath($this->settings, $path);
$value = $this->getValueByPath($path);
if ($value === null) {
throw new InvalidArgumentException(
@ -78,6 +79,20 @@ class ConfigurationContainer implements ConfigurationContainerInterface
*/
public function getIfExists(string $path)
{
return ArrayUtility::getValueByPath($this->settings, $path);
return $this->getValueByPath($path);
}
/**
* @param string $path
* @return mixed
*/
protected function getValueByPath(string $path)
{
try {
return ArrayUtility::getValueByPath($this->settings, $path, '.');
} catch (\Exception $e) {
// Catch all exceptions to safely handle path retrieval
}
return null;
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Configuration;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Configuration;
/*
@ -27,8 +28,12 @@ class ConfigurationUtility
{
/**
* Will parse all entries, recursive as fluid template, with request variable set to $searchRequest.
*
* @param SearchRequestInterface $searchRequest
* @param array $array
* @return array
*/
public function replaceArrayValuesWithRequestContent(SearchRequestInterface $searchRequest, array $array) : array
public function replaceArrayValuesWithRequestContent(SearchRequestInterface $searchRequest, array $array): array
{
array_walk_recursive($array, function (&$value, $key, SearchRequestInterface $searchRequest) {
$template = new StandaloneView();
@ -38,7 +43,7 @@ class ConfigurationUtility
// As elasticsearch does need some doubles to be send as doubles.
if (is_numeric($value)) {
$value = (float) $value;
$value = (float)$value;
}
}, $searchRequest);
@ -48,14 +53,16 @@ class ConfigurationUtility
/**
* Will check all entries, whether they have a condition and filter entries out, where condition is false.
* Also will remove condition in the end.
*
* @param array $entries
* @return array
*/
public function filterByCondition(array $entries) : array
public function filterByCondition(array $entries): array
{
$entries = array_filter($entries, function ($entry) {
return !is_array($entry)
|| !array_key_exists('condition', $entry)
|| (bool) $entry['condition'] === true
;
|| (bool)$entry['condition'] === true;
});
foreach ($entries as $key => $entry) {

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Configuration;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Configuration;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection;
/*
@ -28,6 +29,8 @@ interface ConnectionInterface
/**
* Will add a new document.
*
* @param string $documentType
* @param array $document
* @return void
*/
public function addDocument(string $documentType, array $document);
@ -35,6 +38,8 @@ interface ConnectionInterface
/**
* Add the given documents.
*
* @param string $documentType
* @param array $documents
* @return void
*/
public function addDocuments(string $documentType, array $documents);
@ -44,6 +49,8 @@ interface ConnectionInterface
*
* NOTE: Batch updating is not yet supported.
*
* @param string $documentType
* @param array $document
* @return void
*/
public function updateDocument(string $documentType, array $document);
@ -53,18 +60,24 @@ interface ConnectionInterface
*
* NOTE: Batch deleting is not yet supported.
*
* @param string $documentType
* @param string $identifier
* @return void
*/
public function deleteDocument(string $documentType, string $identifier);
/**
* Search by given request and return result.
*
* @param SearchRequestInterface $searchRequest
* @return SearchResultInterface
*/
public function search(SearchRequestInterface $searchRequest) : SearchResultInterface;
public function search(SearchRequestInterface $searchRequest): SearchResultInterface;
/**
* Will delete the whole index / db.
*
* @param string $documentType
* @return void
*/
public function deleteIndex(string $documentType);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection;
/*
@ -112,6 +113,10 @@ class Elasticsearch implements Singleton, ConnectionInterface
$this->queryFactory = $queryFactory;
}
/**
* @param string $documentType
* @param array $document
*/
public function addDocument(string $documentType, array $document)
{
$this->withType(
@ -122,6 +127,10 @@ class Elasticsearch implements Singleton, ConnectionInterface
);
}
/**
* @param string $documentType
* @param string $identifier
*/
public function deleteDocument(string $documentType, string $identifier)
{
try {
@ -139,6 +148,10 @@ class Elasticsearch implements Singleton, ConnectionInterface
}
}
/**
* @param string $documentType
* @param array $document
*/
public function updateDocument(string $documentType, array $document)
{
$this->withType(
@ -149,6 +162,10 @@ class Elasticsearch implements Singleton, ConnectionInterface
);
}
/**
* @param string $documentType
* @param array $documents
*/
public function addDocuments(string $documentType, array $documents)
{
$this->withType(
@ -159,11 +176,14 @@ class Elasticsearch implements Singleton, ConnectionInterface
);
}
/**
* @param string $documentType
*/
public function deleteIndex(string $documentType)
{
$index = $this->connection->getClient()->getIndex($this->indexFactory->getIndexName());
if (! $index->exists()) {
if (!$index->exists()) {
$this->logger->notice(
'Index did not exist, therefore was not deleted.',
[$documentType, $this->indexFactory->getIndexName()]
@ -176,6 +196,9 @@ class Elasticsearch implements Singleton, ConnectionInterface
/**
* Execute given callback with Elastica Type based on provided documentType
*
* @param string $documentType
* @param callable $callback
*/
protected function withType(string $documentType, callable $callback)
{
@ -191,7 +214,11 @@ class Elasticsearch implements Singleton, ConnectionInterface
$type->getIndex()->refresh();
}
public function search(SearchRequestInterface $searchRequest) : SearchResultInterface
/**
* @param SearchRequestInterface $searchRequest
* @return SearchResultInterface
*/
public function search(SearchRequestInterface $searchRequest): SearchResultInterface
{
$this->logger->debug('Search for', [$searchRequest->getSearchTerm()]);
@ -202,7 +229,11 @@ class Elasticsearch implements Singleton, ConnectionInterface
return $this->objectManager->get(SearchResult::class, $searchRequest, $search->search());
}
protected function getType(string $documentType) : \Elastica\Type
/**
* @param string $documentType
* @return \Elastica\Type
*/
protected function getType(string $documentType): \Elastica\Type
{
return $this->typeFactory->getType(
$this->indexFactory->getIndex(

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -68,7 +69,7 @@ class Connection implements Singleton
/**
* Get the concrete client for internal usage!
*/
public function getClient() : \Elastica\Client
public function getClient(): \Elastica\Client
{
return $this->elasticaClient;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -44,13 +45,18 @@ class DocumentFactory implements Singleton
/**
* Creates document from document.
*
* @param string $documentType
* @param array $document
* @return \Elastica\Document
* @throws \Exception
*/
public function getDocument(string $documentType, array $document) : \Elastica\Document
public function getDocument(string $documentType, array $document): \Elastica\Document
{
// TODO: Use DocumentType for further configuration.
if (!isset($document['search_identifier'])) {
throw new \Exception('No search_identifier provided for document.', 1481194385);
throw new \Exception('No search_identifier provided for document.', 1481194385);
}
$identifier = $document['search_identifier'];
@ -65,8 +71,12 @@ class DocumentFactory implements Singleton
/**
* Creates documents based on documents.
* @param string $documentType
* @param array $documents
* @return array
* @throws \Exception
*/
public function getDocuments(string $documentType, array $documents) : array
public function getDocuments(string $documentType, array $documents): array
{
foreach ($documents as &$document) {
$document = $this->getDocument($documentType, $document);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -22,7 +23,6 @@ namespace Codappix\SearchCore\Connection\Elasticsearch;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Connection\FacetInterface;
use Codappix\SearchCore\Connection\FacetOptionInterface;
class Facet implements FacetInterface
{
@ -44,8 +44,14 @@ class Facet implements FacetInterface
/**
* @var array<FacetOption>
*/
protected $options = [];
protected $options;
/**
* Facet constructor.
* @param string $name
* @param array $aggregation
* @param ConfigurationContainerInterface $configuration
*/
public function __construct(string $name, array $aggregation, ConfigurationContainerInterface $configuration)
{
$this->name = $name;
@ -60,12 +66,18 @@ class Facet implements FacetInterface
}
}
public function getName() : string
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
public function getField() : string
/**
* @return string
*/
public function getField(): string
{
return $this->field;
}
@ -75,21 +87,23 @@ class Facet implements FacetInterface
*
* @return array<FacetOptionInterface>
*/
public function getOptions() : array
public function getOptions(): array
{
$this->initOptions();
return $this->options;
}
/**
* @return void
*/
protected function initOptions()
{
if ($this->options !== []) {
return;
}
foreach ($this->buckets as $bucket) {
$this->options[$bucket['key']] = new FacetOption($bucket);
if ($this->options === null) {
$this->options = [];
foreach ($this->buckets as $bucket) {
$this->options[$bucket['key']] = new FacetOption($bucket);
}
}
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -49,17 +50,20 @@ class FacetOption implements FacetOptionInterface
$this->count = $bucket['doc_count'];
}
public function getName() : string
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
public function getDisplayName() : string
public function getDisplayName(): string
{
return $this->displayName;
}
public function getCount() : int
public function getCount(): int
{
return $this->count;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -65,15 +66,19 @@ class IndexFactory implements Singleton
*
* @return string
*/
public function getIndexName()
public function getIndexName(): string
{
return $this->configuration->get('connections.elasticsearch.index');
return (string)$this->configuration->get('connections.elasticsearch.index');
}
/**
* Get an index bases on TYPO3 table name.
*
* @param Connection $connection
* @param string $documentType
* @return \Elastica\Index
*/
public function getIndex(Connection $connection, string $documentType) : \Elastica\Index
public function getIndex(Connection $connection, string $documentType): \Elastica\Index
{
$index = $connection->getClient()->getIndex($this->getIndexName());
@ -87,7 +92,11 @@ class IndexFactory implements Singleton
return $index;
}
protected function getConfigurationFor(string $documentType) : array
/**
* @param string $documentType
* @return array
*/
protected function getConfigurationFor(string $documentType): array
{
try {
$configuration = $this->configuration->get('indexing.' . $documentType . '.index');
@ -108,7 +117,11 @@ class IndexFactory implements Singleton
}
}
protected function prepareAnalyzerConfiguration(array $analyzer) : array
/**
* @param array $analyzer
* @return array
*/
protected function prepareAnalyzerConfiguration(array $analyzer): array
{
$fieldsToExplode = ['char_filter', 'filter', 'word_list'];

View file

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

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -20,8 +21,6 @@ namespace Codappix\SearchCore\Connection\Elasticsearch;
* 02110-1301, USA.
*/
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;
@ -45,12 +44,12 @@ class SearchResult implements SearchResultInterface
/**
* @var array<FacetInterface>
*/
protected $facets = [];
protected $facets;
/**
* @var array<ResultItemInterface>
*/
protected $results = [];
protected $results;
/**
* For Iterator interface.
@ -64,6 +63,13 @@ class SearchResult implements SearchResultInterface
*/
protected $objectManager;
/**
* SearchResult constructor.
*
* @param SearchRequestInterface $searchRequest
* @param \Elastica\ResultSet $result
* @param ObjectManagerInterface $objectManager
*/
public function __construct(
SearchRequestInterface $searchRequest,
\Elastica\ResultSet $result,
@ -77,7 +83,7 @@ class SearchResult implements SearchResultInterface
/**
* @return array<ResultItemInterface>
*/
public function getResults() : array
public function getResults(): array
{
$this->initResults();
@ -89,52 +95,74 @@ class SearchResult implements SearchResultInterface
*
* @return array<FacetInterface>
*/
public function getFacets() : array
public function getFacets(): array
{
$this->initFacets();
return $this->facets;
}
public function getCurrentCount() : int
/**
* @return integer
*/
public function getCurrentCount(): int
{
return $this->result->count();
}
/**
* @return void
*/
protected function initResults()
{
if ($this->results !== []) {
return;
}
foreach ($this->result->getResults() as $result) {
$this->results[] = new ResultItem($result->getData(), $result->getParam('_type'));
if ($this->results === null) {
$this->results = [];
foreach ($this->result->getResults() as $result) {
$this->results[] = new ResultItem($result->getData(), $result->getParam('_type'));
}
}
}
/**
* @return void
*/
protected function initFacets()
{
if ($this->facets !== [] || !$this->result->hasAggregations()) {
return;
}
foreach ($this->result->getAggregations() as $aggregationName => $aggregation) {
$this->facets[$aggregationName] = $this->objectManager->get(Facet::class, $aggregationName, $aggregation);
if ($this->facets === null) {
$this->facets = [];
if ($this->result->hasAggregations()) {
foreach ($this->result->getAggregations() as $aggregationName => $aggregation) {
$this->facets[$aggregationName] = $this->objectManager->get(Facet::class, $aggregationName, $aggregation);
}
}
}
}
// Countable - Interface
/**
* Countable - Interface
*
* @return integer
*/
public function count()
{
return $this->result->getTotalHits();
}
// Iterator - Interface
/**
* Iterator - Interface
*
* @return mixed
*/
public function current()
{
return $this->getResults()[$this->position];
}
/**
* Iterator - Interface
*
* @return mixed
*/
public function next()
{
++$this->position;
@ -142,21 +170,37 @@ class SearchResult implements SearchResultInterface
return $this->current();
}
/**
* Iterator - Interface
*
* @return int|mixed
*/
public function key()
{
return $this->position;
}
/**
* Iterator - Interface
*
* @return bool
*/
public function valid()
{
return isset($this->getResults()[$this->position]);
}
/**
* Iterator - Interface
*/
public function rewind()
{
$this->position = 0;
}
/**
* @return SearchRequestInterface|\TYPO3\CMS\Extbase\Persistence\QueryInterface
*/
public function getQuery()
{
return $this->searchRequest;

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection\Elasticsearch;
/*
@ -31,8 +32,11 @@ 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, string $documentType) : \Elastica\Type
public function getType(\Elastica\Index $index, string $documentType): \Elastica\Type
{
return $index->getType($documentType);
}

View file

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

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection;
/*
@ -28,17 +29,23 @@ interface FacetOptionInterface
/**
* Returns the name of this option. Equivalent
* to value used for filtering.
*
* @return string
*/
public function getName() : string;
public function getName(): string;
/**
* If a pre-rendered name is provided, this will be returned.
* Otherwise it's the same as getName().
*
* @return string
*/
public function getDisplayName() : string;
public function getDisplayName(): string;
/**
* Returns the number of found results for this option.
*
* @return integer
*/
public function getCount() : int;
public function getCount(): int;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection;
/*
@ -28,11 +29,15 @@ 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() : string;
public function getIdentifier(): string;
/**
* The config to use for facet building.
*
* @return array
*/
public function getConfig() : array;
public function getConfig(): array;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection;
/*
@ -31,14 +32,18 @@ interface ResultItemInterface extends \ArrayAccess
* Provide key/column/field => data.
*
* Used e.g. for dataprocessing.
*
* @return array
*/
public function getPlainData() : array;
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.
*
* @return string
*/
public function getType() : string;
public function getType(): string;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Connection;
/*
@ -20,8 +21,6 @@ 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;
@ -29,16 +28,29 @@ interface SearchRequestInterface extends QueryInterface
{
/**
* Returns the actual string the user searched for.
*
* @return string
*/
public function getSearchTerm() : string;
public function getSearchTerm(): string;
public function hasFilter() : bool;
/**
* @return bool
*/
public function hasFilter(): bool;
public function getFilter() : array;
/**
* @return array
*/
public function getFilter(): array;
/**
* @param array $filter
* @return void
*/
public function setFilter(array $filter);
/**
* @param FacetRequestInterface $facet
* @return void
*/
public function addFacet(FacetRequestInterface $facet);
@ -46,12 +58,13 @@ interface SearchRequestInterface extends QueryInterface
/**
* @return array<FacetRequestInterface>
*/
public function getFacets() : array;
public function getFacets(): array;
/**
* Workaround for paginate widget support which will
* use the request to build another search.
*
* @param ConnectionInterface $connection
* @return void
*/
public function setConnection(ConnectionInterface $connection);
@ -60,6 +73,7 @@ interface SearchRequestInterface extends QueryInterface
* Workaround for paginate widget support which will
* use the request to build another search.
*
* @param SearchService $searchService
* @return void
*/
public function setSearchService(SearchService $searchService);

View file

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

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Controller;
/*
@ -44,30 +45,33 @@ class SearchController extends ActionController
parent::__construct();
}
/**
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException
* @return void
*/
public function initializeSearchAction()
{
if (isset($this->settings['searching']['mode']) && $this->settings['searching']['mode'] === 'filter'
if (isset($this->settings['searching']['mode'])
&& $this->settings['searching']['mode'] === 'filter'
&& $this->request->hasArgument('searchRequest') === false
) {
$this->request->setArguments(array_merge(
$this->request->getArguments(),
[
'searchRequest' => $this->objectManager->get(SearchRequest::class),
]
['searchRequest' => $this->objectManager->get(SearchRequest::class)]
));
}
if ($this->arguments->hasArgument('searchRequest')) {
$this->arguments->getArgument('searchRequest')->getPropertyMappingConfiguration()
->allowAllProperties()
;
->allowAllProperties();
}
}
/**
* Process a search and deliver original request and result to view.
*
* @param null|SearchRequest $searchRequest
* @param SearchRequest $searchRequest
* @return void
*/
public function searchAction(SearchRequest $searchRequest = null)
{

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -34,12 +35,21 @@ class ContentObjectDataProcessorAdapterProcessor implements ProcessorInterface
*/
protected $typoScriptService;
/**
* ContentObjectDataProcessorAdapterProcessor constructor.
* @param TypoScriptServiceInterface $typoScriptService
*/
public function __construct(TypoScriptServiceInterface $typoScriptService)
{
$this->typoScriptService = $typoScriptService;
}
public function processData(array $data, array $configuration) : array
/**
* @param array $data
* @param array $configuration
* @return array
*/
public function processData(array $data, array $configuration): array
{
$dataProcessor = GeneralUtility::makeInstance($configuration['_dataProcessor']);
$contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -25,7 +26,12 @@ namespace Codappix\SearchCore\DataProcessing;
*/
class CopyToProcessor implements ProcessorInterface
{
public function processData(array $record, array $configuration) : array
/**
* @param array $record
* @param array $configuration
* @return array
*/
public function processData(array $record, array $configuration): array
{
$target = [];
@ -37,7 +43,7 @@ class CopyToProcessor implements ProcessorInterface
if (is_array($from)) {
$this->addArray($target, $from);
} else {
$target[] = (string) $from;
$target[] = (string)$from;
}
$target = array_filter($target);
@ -46,6 +52,11 @@ class CopyToProcessor implements ProcessorInterface
return $record;
}
/**
* @param array $target
* @param array $from
* @return void
*/
protected function addArray(array &$target, array $from)
{
foreach ($from as $value) {
@ -54,7 +65,7 @@ class CopyToProcessor implements ProcessorInterface
continue;
}
$target[] = (string) $value;
$target[] = (string)$value;
}
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -25,21 +26,31 @@ namespace Codappix\SearchCore\DataProcessing;
*/
class GeoPointProcessor implements ProcessorInterface
{
public function processData(array $record, array $configuration) : array
/**
* @param array $record
* @param array $configuration
* @return array
*/
public function processData(array $record, array $configuration): array
{
if (! $this->isApplyable($record, $configuration)) {
if (!$this->isApplyable($record, $configuration)) {
return $record;
}
$record[$configuration['to']] = [
'lat' => (float) $record[$configuration['lat']],
'lon' => (float) $record[$configuration['lon']],
'lat' => (float)$record[$configuration['lat']],
'lon' => (float)$record[$configuration['lon']],
];
return $record;
}
protected function isApplyable(array $record, array $configuration) : bool
/**
* @param array $record
* @param array $configuration
* @return bool
*/
protected function isApplyable(array $record, array $configuration): bool
{
if (!isset($record[$configuration['lat']])
|| !is_numeric($record[$configuration['lat']])

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -28,6 +29,9 @@ interface ProcessorInterface
/**
* Processes the given data.
* Also retrieves the configuration for this processor instance.
* @param array $record
* @param array $configuration
* @return array
*/
public function processData(array $record, array $configuration) : array;
public function processData(array $record, array $configuration): array;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -27,7 +28,12 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
*/
class RemoveProcessor implements ProcessorInterface
{
public function processData(array $record, array $configuration) : array
/**
* @param array $record
* @param array $configuration
* @return array
*/
public function processData(array $record, array $configuration): array
{
if (!isset($configuration['fields'])) {
return $record;

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -32,6 +33,10 @@ class Service
*/
protected $objectManager;
/**
* Service constructor.
* @param ObjectManagerInterface $objectManager
*/
public function __construct(ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
@ -41,8 +46,11 @@ class Service
* Executes the dataprocessor depending on configuration and returns the result.
*
* @param array|string $configuration Either the full configuration or only the class name.
* @param array $data
* @param string $recordType
* @return array
*/
public function executeDataProcessor($configuration, array $data, string $recordType = '') : array
public function executeDataProcessor($configuration, array $data, string $recordType = ''): array
{
if (is_string($configuration)) {
$configuration = [
@ -50,7 +58,7 @@ class Service
];
}
if (!isset($configuration['_table']) && $recordType !== '') {
if ($recordType !== '' && !isset($configuration['_table'])) {
$configuration['_table'] = $recordType;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\DataProcessing;
/*
@ -40,6 +41,11 @@ class TcaRelationResolvingProcessor implements ProcessorInterface
*/
protected $relationResolver;
/**
* TcaRelationResolvingProcessor constructor.
* @param ObjectManagerInterface $objectManager
* @param RelationResolver $relationResolver
*/
public function __construct(
ObjectManagerInterface $objectManager,
RelationResolver $relationResolver
@ -49,12 +55,15 @@ class TcaRelationResolvingProcessor implements ProcessorInterface
}
/**
* @throws \InvalidArgumentException If _table is not configured.
* @param array $record
* @param array $configuration
* @return array
*/
public function processData(array $record, array $configuration) : array
public function processData(array $record, array $configuration): array
{
$this->initializeConfiguration($configuration);
/** @var TcaTableServiceInterface $tcaTableService */
$tcaTableService = $this->objectManager->get(
TcaTableServiceInterface::class,
$configuration['_table']
@ -69,6 +78,7 @@ class TcaRelationResolvingProcessor implements ProcessorInterface
}
/**
* @param array $configuration
* @throws \InvalidArgumentException If _table is not configured.
*/
protected function initializeConfiguration(array &$configuration)
@ -84,7 +94,12 @@ class TcaRelationResolvingProcessor implements ProcessorInterface
$configuration['excludeFields'] = GeneralUtility::trimExplode(',', $configuration['excludeFields'], true);
}
protected function getRecordToProcess(array $record, array $configuration) : array
/**
* @param array $record
* @param array $configuration
* @return array
*/
protected function getRecordToProcess(array $record, array $configuration): array
{
if ($configuration['excludeFields'] === []) {
return $record;

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Database\Doctrine;
/*
@ -32,18 +33,29 @@ class Join
*/
protected $condition = '';
/**
* Join constructor.
* @param string $table
* @param string $condition
*/
public function __construct(string $table, string $condition)
{
$this->table = $table;
$this->condition = $condition;
}
public function getTable() : string
/**
* @return string
*/
public function getTable(): string
{
return $this->table;
}
public function getCondition() : string
/**
* @return string
*/
public function getCondition(): string
{
return $this->condition;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Database\Doctrine;
/*
@ -32,18 +33,29 @@ class Where
*/
protected $parameters = [];
/**
* Where constructor.
* @param string $statement
* @param array $parameters
*/
public function __construct(string $statement, array $parameters)
{
$this->statement = $statement;
$this->parameters = $parameters;
}
public function getStatement() : string
/**
* @return string
*/
public function getStatement(): string
{
return $this->statement;
}
public function getParameters() : array
/**
* @return array
*/
public function getParameters(): array
{
return $this->parameters;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*
@ -63,17 +64,29 @@ abstract class AbstractIndexer implements IndexerInterface
$this->logger = $logManager->getLogger(__CLASS__);
}
/**
* @param string $identifier
* @return void
*/
public function setIdentifier(string $identifier)
{
$this->identifier = $identifier;
}
/**
* AbstractIndexer constructor.
* @param ConnectionInterface $connection
* @param ConfigurationContainerInterface $configuration
*/
public function __construct(ConnectionInterface $connection, ConfigurationContainerInterface $configuration)
{
$this->connection = $connection;
$this->configuration = $configuration;
}
/**
* @return void
*/
public function indexAllDocuments()
{
$this->logger->info('Start indexing');
@ -92,11 +105,15 @@ abstract class AbstractIndexer implements IndexerInterface
$this->logger->info('Finish indexing');
}
/**
* @param string $identifier
* @return void
*/
public function indexDocument(string $identifier)
{
$this->logger->info('Start indexing single record.', [$identifier]);
try {
$record = $this->getRecord((int) $identifier);
$record = $this->getRecord((int)$identifier);
$this->prepareRecord($record);
$this->connection->addDocument($this->getDocumentName(), $record);
@ -107,6 +124,9 @@ abstract class AbstractIndexer implements IndexerInterface
$this->logger->info('Finish indexing');
}
/**
* @return void
*/
public function delete()
{
$this->logger->info('Start deletion of index.');
@ -114,7 +134,10 @@ abstract class AbstractIndexer implements IndexerInterface
$this->logger->info('Finish deletion.');
}
protected function getRecordGenerator() : \Generator
/**
* @return \Generator
*/
protected function getRecordGenerator()
{
$offset = 0;
$limit = $this->getLimit();
@ -125,6 +148,10 @@ abstract class AbstractIndexer implements IndexerInterface
}
}
/**
* @param array $record
* @return void
*/
protected function prepareRecord(array &$record)
{
try {
@ -138,6 +165,10 @@ abstract class AbstractIndexer implements IndexerInterface
$this->handleAbstract($record);
}
/**
* @param array $record
* @return void
*/
protected function handleAbstract(array &$record)
{
$record['search_abstract'] = '';
@ -148,8 +179,9 @@ abstract class AbstractIndexer implements IndexerInterface
$this->configuration->get('indexing.' . $this->identifier . '.abstractFields')
);
if ($fieldsToUse === []) {
return;
throw new InvalidArgumentException('No fields to use', 1538487209251);
}
foreach ($fieldsToUse as $fieldToUse) {
if (isset($record[$fieldToUse]) && trim($record[$fieldToUse])) {
$record['search_abstract'] = trim($record[$fieldToUse]);
@ -157,28 +189,36 @@ abstract class AbstractIndexer implements IndexerInterface
}
}
} catch (InvalidArgumentException $e) {
return;
// Nothing to do.
}
}
/**
* Returns the limit to use to fetch records.
*
* @return integer
*/
protected function getLimit() : int
protected function getLimit(): int
{
// TODO: Make configurable.
return 50;
}
/**
* @param integer $offset
* @param integer $limit
* @return array|null
*/
abstract protected function getRecords(int $offset, int $limit);
/**
* @throws NoRecordFoundException If record could not be found.
* @param integer $identifier
* @return array
*/
abstract protected function getRecord(int $identifier) : array;
abstract protected function getRecord(int $identifier): array;
abstract protected function getDocumentName() : string;
/**
* @return string
*/
abstract protected function getDocumentName(): string;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*
@ -22,7 +23,6 @@ namespace Codappix\SearchCore\Domain\Index;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Configuration\InvalidArgumentException;
use Codappix\SearchCore\Domain\Index\IndexerInterface;
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface;
use TYPO3\CMS\Core\SingletonInterface as Singleton;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
@ -55,9 +55,11 @@ class IndexerFactory implements Singleton
}
/**
* @throws NoMatchingIndexer
* @param string $identifier
* @return IndexerInterface
* @throws NoMatchingIndexerException
*/
public function getIndexer(string $identifier) : IndexerInterface
public function getIndexer(string $identifier): IndexerInterface
{
try {
return $this->buildIndexer($this->configuration->get('indexing.' . $identifier . '.indexer'), $identifier);
@ -71,9 +73,12 @@ class IndexerFactory implements Singleton
}
/**
* @throws NoMatchingIndexer
* @param string $indexerClass
* @param string $identifier
* @return \Codappix\SearchCore\Domain\Index\IndexerInterface
* @throws NoMatchingIndexerException
*/
protected function buildIndexer(string $indexerClass, string $identifier) : IndexerInterface
protected function buildIndexer(string $indexerClass, string $identifier): IndexerInterface
{
$indexer = null;
if (is_subclass_of($indexerClass, TcaIndexer\PagesIndexer::class)

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*
@ -35,13 +36,15 @@ interface IndexerInterface
/**
* Fetches a single document and pushes it to the connection.
*
* @param string $identifier
* @return void
*/
public function indexDocument(string $identifier);
/**
* Recieves the identifier of the indexer itself.
* Receives the identifier of the indexer itself.
*
* @param string $identifier
* @return void
*/
public function setIdentifier(string $identifier);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index;
/*
@ -49,6 +50,8 @@ class TcaIndexer extends AbstractIndexer
}
/**
* @param integer $offset
* @param integer $limit
* @return array|null
*/
protected function getRecords(int $offset, int $limit)
@ -67,9 +70,11 @@ class TcaIndexer extends AbstractIndexer
}
/**
* @param integer $identifier
* @return array
* @throws NoRecordFoundException If record could not be found.
*/
protected function getRecord(int $identifier) : array
protected function getRecord(int $identifier): array
{
$record = $this->tcaTableService->getRecord($identifier);
@ -84,7 +89,10 @@ class TcaIndexer extends AbstractIndexer
return $record;
}
protected function getDocumentName() : string
/**
* @return string
*/
protected function getDocumentName(): string
{
return $this->tcaTableService->getTableName();
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
/*

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
/*
@ -23,7 +24,6 @@ namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Connection\ConnectionInterface;
use Codappix\SearchCore\Domain\Index\TcaIndexer;
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
@ -58,6 +58,9 @@ class PagesIndexer extends TcaIndexer
$this->contentTableService = $contentTableService;
}
/**
* @param array $record
*/
protected function prepareRecord(array &$record)
{
parent::prepareRecord($record);
@ -78,7 +81,11 @@ class PagesIndexer extends TcaIndexer
}
}
protected function fetchContentForPage(int $uid) : array
/**
* @param integer $uid
* @return array
*/
protected function fetchContentForPage(int $uid): array
{
if ($this->contentTableService instanceof TcaTableService) {
$queryBuilder = $this->contentTableService->getQuery();
@ -123,17 +130,31 @@ class PagesIndexer extends TcaIndexer
];
}
protected function getContentElementImages(int $uidOfContentElement) : array
/**
* @param integer $uidOfContentElement
* @return array
*/
protected function getContentElementImages(int $uidOfContentElement): array
{
return $this->fetchSysFileReferenceUids($uidOfContentElement, 'tt_content', 'image');
}
protected function fetchMediaForPage(int $uid) : array
/**
* @param integer $uid
* @return array
*/
protected function fetchMediaForPage(int $uid): array
{
return $this->fetchSysFileReferenceUids($uid, 'pages', 'media');
}
protected function fetchSysFileReferenceUids(int $uid, string $tablename, string $fieldname) : array
/**
* @param integer $uid
* @param string $tablename
* @param string $fieldname
* @return array
*/
protected function fetchSysFileReferenceUids(int $uid, string $tablename, string $fieldname): array
{
$imageRelationUids = [];
$imageRelations = $this->fileRepository->findByRelation($tablename, $fieldname, $uid);
@ -145,7 +166,11 @@ class PagesIndexer extends TcaIndexer
return $imageRelationUids;
}
protected function getContentFromContentElement(array $contentElement) : string
/**
* @param array $contentElement
* @return string
*/
protected function getContentFromContentElement(array $contentElement): string
{
$content = '';

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
/*
@ -33,11 +34,16 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
*/
class RelationResolver implements Singleton
{
public function resolveRelationsForRecord(TcaTableServiceInterface $service, array $record) : array
/**
* @param TcaTableServiceInterface $service
* @param array $record
* @return array
*/
public function resolveRelationsForRecord(TcaTableServiceInterface $service, array $record): array
{
foreach (array_keys($record) as $column) {
if (in_array($column, ['pid', $service->getLanguageUidColumn()])) {
$record[$column] = (int) $record[$column];
$record[$column] = (int)$record[$column];
continue;
}
@ -58,6 +64,11 @@ class RelationResolver implements Singleton
return $record;
}
/**
* @param string $value
* @param array $tcaColumn
* @return array
*/
protected function resolveValue($value, array $tcaColumn)
{
if ($value === '' || $value === 'N/A') {
@ -74,25 +85,39 @@ class RelationResolver implements Singleton
return [];
}
protected function isRelation(array &$config) : bool
/**
* @param array $config
* @return boolean
*/
protected function isRelation(array &$config): bool
{
return isset($config['foreign_table'])
|| (isset($config['renderType']) && !in_array($config['renderType'], ['selectSingle', 'inputDateTime']))
|| (isset($config['internal_type']) && strtolower($config['internal_type']) === 'db')
;
|| (isset($config['internal_type']) && strtolower($config['internal_type']) === 'db');
}
protected function resolveForeignDbValue(string $value) : array
/**
* @param string $value
* @return array
*/
protected function resolveForeignDbValue(string $value): array
{
return array_map('trim', explode(';', $value));
}
protected function resolveInlineValue(string $value) : array
/**
* @param string $value
* @return array
*/
protected function resolveInlineValue(string $value): array
{
return array_map('trim', explode(',', $value));
}
protected function getUtilityForMode() : string
/**
* @return string
*/
protected function getUtilityForMode(): string
{
if (TYPO3_MODE === 'BE') {
return BackendUtility::class;
@ -101,15 +126,21 @@ class RelationResolver implements Singleton
return FrontendUtility::class;
}
/**
* @param array $record
* @param string $column
* @param TcaTableServiceInterface $service
* @return string
*/
protected function getColumnValue(array $record, string $column, TcaTableServiceInterface $service): string
{
$utility = GeneralUtility::makeInstance($this->getUtilityForMode());
return $utility::getProcessedValueExtra(
$service->getTableName(),
$column,
$record[$column],
0,
$record['uid']
) ?? '';
$service->getTableName(),
$column,
$record[$column],
0,
$record['uid']
) ?? '';
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
/*
@ -24,7 +25,6 @@ use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Database\Doctrine\Join;
use Codappix\SearchCore\Database\Doctrine\Where;
use Codappix\SearchCore\Domain\Index\IndexingException;
use Codappix\SearchCore\Domain\Index\TcaIndexer\InvalidArgumentException;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
@ -85,6 +85,7 @@ class TcaTableService implements TcaTableServiceInterface
/**
* @param string $tableName
* @param ConfigurationContainerInterface $configuration
* @throws IndexingException
*/
public function __construct(
$tableName,
@ -102,17 +103,28 @@ class TcaTableService implements TcaTableServiceInterface
$this->configuration = $configuration;
}
public function getTableName() : string
/**
* @return string
*/
public function getTableName(): string
{
return $this->tableName;
}
public function getTableClause() : string
/**
* @return string
*/
public function getTableClause(): string
{
return $this->tableName;
}
public function getRecords(int $offset, int $limit) : array
/**
* @param integer $offset
* @param integer $limit
* @return array
*/
public function getRecords(int $offset, int $limit): array
{
$records = $this->getQuery()
->setFirstResult($offset)
@ -123,7 +135,11 @@ class TcaTableService implements TcaTableServiceInterface
return $records ?: [];
}
public function getRecord(int $identifier) : array
/**
* @param integer $identifier
* @return array
*/
public function getRecord(int $identifier): array
{
$query = $this->getQuery();
$query = $query->andWhere($this->getTableName() . '.uid = ' . $identifier);
@ -132,16 +148,24 @@ class TcaTableService implements TcaTableServiceInterface
return $record ?: [];
}
/**
* @param array $records
* @return void
*/
public function filterRecordsByRootLineBlacklist(array &$records)
{
$records = array_filter(
$records,
function ($record) {
return ! $this->isRecordBlacklistedByRootline($record);
return !$this->isRecordBlacklistedByRootline($record);
}
);
}
/**
* @param array $record
* @return void
*/
public function prepareRecord(array &$record)
{
if (isset($record['uid']) && !isset($record['search_identifier'])) {
@ -152,7 +176,10 @@ class TcaTableService implements TcaTableServiceInterface
}
}
protected function getWhereClause() : Where
/**
* @return Where
*/
protected function getWhereClause(): Where
{
$parameters = [];
$whereClause = $this->getSystemWhereClause();
@ -174,17 +201,19 @@ class TcaTableService implements TcaTableServiceInterface
return new Where($whereClause, $parameters);
}
protected function getFields() : array
/**
* @return array
*/
protected function getFields(): array
{
$fields = array_merge(
['uid','pid'],
['uid', 'pid'],
array_filter(
array_keys($this->tca['columns']),
function ($columnName) {
return !$this->isSystemField($columnName)
&& !$this->isUserField($columnName)
&& !$this->isPassthroughField($columnName)
;
&& !$this->isPassthroughField($columnName);
}
)
);
@ -197,7 +226,11 @@ class TcaTableService implements TcaTableServiceInterface
return $fields;
}
protected function getJoins() : array
/**
* @return array
*/
protected function getJoins(): array
{
if ($this->tableName === 'pages') {
return [];
@ -212,31 +245,40 @@ class TcaTableService implements TcaTableServiceInterface
* Generate SQL for TYPO3 as a system, to make sure only available records
* are fetched.
*/
protected function getSystemWhereClause() : string
protected function getSystemWhereClause(): string
{
$whereClause = '1=1'
. BackendUtility::BEenableFields($this->tableName)
. BackendUtility::deleteClause($this->tableName)
. ' AND pages.no_search = 0'
;
. ' AND pages.no_search = 0';
if ($this->tableName !== 'pages') {
$whereClause .= BackendUtility::BEenableFields('pages')
. BackendUtility::deleteClause('pages')
;
. BackendUtility::deleteClause('pages');
}
return $whereClause;
}
protected function isSystemField(string $columnName) : bool
/**
* @param string $columnName
* @return bool
*/
protected function isSystemField(string $columnName): bool
{
$systemFields = [
// Versioning fields,
// https://docs.typo3.org/typo3cms/TCAReference/Reference/Ctrl/Index.html#versioningws
't3ver_oid', 't3ver_id', 't3ver_label', 't3ver_wsid',
't3ver_state', 't3ver_stage', 't3ver_count', 't3ver_tstamp',
't3ver_move_id', 't3ver_swapmode',
't3ver_oid',
't3ver_id',
't3ver_label',
't3ver_wsid',
't3ver_state',
't3ver_stage',
't3ver_count',
't3ver_tstamp',
't3ver_move_id',
't3ver_swapmode',
$this->tca['ctrl']['transOrigDiffSourceField'],
$this->tca['ctrl']['cruser_id'],
$this->tca['ctrl']['fe_cruser_id'],
@ -247,22 +289,31 @@ class TcaTableService implements TcaTableServiceInterface
return in_array($columnName, $systemFields);
}
protected function isUserField(string $columnName) : bool
/**
* @param string $columnName
* @return bool
*/
protected function isUserField(string $columnName): bool
{
$config = $this->getColumnConfig($columnName);
return isset($config['type']) && $config['type'] === 'user';
}
protected function isPassthroughField(string $columnName) : bool
/**
* @param string $columnName
* @return bool
*/
protected function isPassthroughField(string $columnName): bool
{
$config = $this->getColumnConfig($columnName);
return isset($config['type']) && $config['type'] === 'passthrough';
}
/**
* @throws InvalidArgumentException
* @param string $columnName
* @return array
*/
public function getColumnConfig(string $columnName) : array
public function getColumnConfig(string $columnName): array
{
if (!isset($this->tca['columns'][$columnName])) {
throw new InvalidArgumentException(
@ -274,7 +325,10 @@ class TcaTableService implements TcaTableServiceInterface
return $this->tca['columns'][$columnName]['config'];
}
public function getLanguageUidColumn() : string
/**
* @return string
*/
public function getLanguageUidColumn(): string
{
if (!isset($this->tca['ctrl']['languageField'])) {
return '';
@ -290,8 +344,11 @@ class TcaTableService implements TcaTableServiceInterface
* Also further TYPO3 mechanics are taken into account. Does a valid root
* line exist, is page inside a recycler, is inherited start- endtime
* excluded, etc.
*
* @param array $record
* @return bool
*/
protected function isRecordBlacklistedByRootline(array &$record) : bool
protected function isRecordBlacklistedByRootline(array &$record): bool
{
$pageUid = $record['pid'];
if ($this->tableName === 'pages') {
@ -325,9 +382,9 @@ class TcaTableService implements TcaTableServiceInterface
}
if ($pageInRootLine['extendToSubpages'] && (
($pageInRootLine['endtime'] > 0 && $pageInRootLine['endtime'] <= time())
|| ($pageInRootLine['starttime'] > 0 && $pageInRootLine['starttime'] >= time())
)) {
($pageInRootLine['endtime'] > 0 && $pageInRootLine['endtime'] <= time())
|| ($pageInRootLine['starttime'] > 0 && $pageInRootLine['starttime'] >= time())
)) {
$this->logger->info(
sprintf(
'Record %u is black listed due to configured timing of parent page %u.',
@ -346,9 +403,9 @@ class TcaTableService implements TcaTableServiceInterface
/**
* Checks whether any page uids are black listed.
*/
protected function isBlackListedRootLineConfigured() : bool
protected function isBlackListedRootLineConfigured(): bool
{
return (bool) $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist');
return (bool)$this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist');
}
/**
@ -356,7 +413,7 @@ class TcaTableService implements TcaTableServiceInterface
*
* @return array<Int>
*/
protected function getBlackListedRootLine() : array
protected function getBlackListedRootLine(): array
{
return GeneralUtility::intExplode(
',',
@ -364,7 +421,10 @@ class TcaTableService implements TcaTableServiceInterface
);
}
public function getQuery() : QueryBuilder
/**
* @return QueryBuilder
*/
public function getQuery(): QueryBuilder
{
$queryBuilder = $this->getDatabaseConnection()->getQueryBuilderForTable($this->getTableName());
$where = $this->getWhereClause();
@ -381,7 +441,10 @@ class TcaTableService implements TcaTableServiceInterface
return $query;
}
protected function getDatabaseConnection() : ConnectionPool
/**
* @return ConnectionPool
*/
protected function getDatabaseConnection(): ConnectionPool
{
return GeneralUtility::makeInstance(ConnectionPool::class);
}

View file

@ -1,378 +0,0 @@
<?php
namespace Codappix\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 Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Domain\Index\IndexingException;
use Codappix\SearchCore\Domain\Index\TcaIndexer\InvalidArgumentException;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
/**
* Encapsulate logik related to TCA configuration.
*/
class TcaTableService76 implements TcaTableServiceInterface
{
/**
* TCA for current table.
* !REFERENCE! To save memory.
* @var array
*/
protected $tca;
/**
* @var string
*/
protected $tableName;
/**
* @var ConfigurationContainerInterface
*/
protected $configuration;
/**
* @var \TYPO3\CMS\Core\Log\Logger
*/
protected $logger;
/**
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* 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 ObjectManagerInterface $objectManager
*/
public function injectObjectManager(ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param string $tableName
* @param ConfigurationContainerInterface $configuration
*/
public function __construct(
$tableName,
ConfigurationContainerInterface $configuration
) {
if (!isset($GLOBALS['TCA'][$tableName])) {
throw new IndexingException(
'Table "' . $tableName . '" is not configured in TCA.',
IndexingException::CODE_UNKOWN_TCA_TABLE
);
}
$this->tableName = $tableName;
$this->tca = &$GLOBALS['TCA'][$this->tableName];
$this->configuration = $configuration;
}
public function getTableName() : string
{
return $this->tableName;
}
public function getTableClause() : string
{
if ($this->tableName === 'pages') {
return $this->tableName;
}
return $this->tableName . ' LEFT JOIN pages on ' . $this->tableName . '.pid = pages.uid';
}
public function getRecords(int $offset, int $limit) : array
{
$records = $this->getConnection()->exec_SELECTgetRows(
$this->getFields(),
$this->getTableClause(),
$this->getWhereClause(),
'',
'',
(int) $offset . ',' . (int) $limit
);
return $records ?: [];
}
public function getRecord(int $identifier) : array
{
$record = $this->getConnection()->exec_SELECTgetSingleRow(
$this->getFields(),
$this->getTableClause(),
$this->getWhereClause()
. ' AND ' . $this->getTableName() . '.uid = ' . (int) $identifier
);
return $record ?: [];
}
public function filterRecordsByRootLineBlacklist(array &$records)
{
$records = array_filter(
$records,
function ($record) {
return ! $this->isRecordBlacklistedByRootline($record);
}
);
}
public function prepareRecord(array &$record)
{
if (isset($record['uid']) && !isset($record['search_identifier'])) {
$record['search_identifier'] = $record['uid'];
}
if (isset($record[$this->tca['ctrl']['label']]) && !isset($record['search_title'])) {
$record['search_title'] = $record[$this->tca['ctrl']['label']];
}
}
public function getWhereClause() : string
{
$whereClause = '1=1'
. BackendUtility::BEenableFields($this->tableName)
. BackendUtility::deleteClause($this->tableName)
. ' AND pages.no_search = 0'
;
if ($this->tableName !== 'pages') {
$whereClause .= BackendUtility::BEenableFields('pages')
. BackendUtility::deleteClause('pages')
;
}
$userDefinedWhere = $this->configuration->getIfExists(
'indexing.' . $this->getTableName() . '.additionalWhereClause'
);
if (is_string($userDefinedWhere)) {
$whereClause .= ' AND ' . $userDefinedWhere;
}
if ($this->isBlacklistedRootLineConfigured()) {
$whereClause .= ' AND pages.uid NOT IN ('
. implode(',', $this->getBlacklistedRootLine())
. ')'
. ' AND pages.pid NOT IN ('
. implode(',', $this->getBlacklistedRootLine())
. ')';
}
$this->logger->debug('Generated where clause.', [$this->tableName, $whereClause]);
return $whereClause;
}
public function getFields() : string
{
$fields = array_merge(
['uid','pid'],
array_filter(
array_keys($this->tca['columns']),
function ($columnName) {
return !$this->isSystemField($columnName)
&& !$this->isUserField($columnName)
&& !$this->isPassthroughField($columnName)
;
}
)
);
foreach ($fields as $key => $field) {
$fields[$key] = $this->tableName . '.' . $field;
}
$this->logger->debug('Generated fields.', [$this->tableName, $fields]);
return implode(',', $fields);
}
/**
* Generate SQL for TYPO3 as a system, to make sure only available records
* are fetched.
*/
protected function getSystemWhereClause() : string
{
$whereClause = '1=1'
. BackendUtility::BEenableFields($this->tableName)
. BackendUtility::deleteClause($this->tableName)
. ' AND pages.no_search = 0'
;
if ($this->tableName !== 'pages') {
$whereClause .= BackendUtility::BEenableFields('pages')
. BackendUtility::deleteClause('pages')
;
}
return $whereClause;
}
protected function isSystemField(string $columnName) : bool
{
$systemFields = [
// Versioning fields,
// https://docs.typo3.org/typo3cms/TCAReference/Reference/Ctrl/Index.html#versioningws
't3ver_oid', 't3ver_id', 't3ver_label', 't3ver_wsid',
't3ver_state', 't3ver_stage', 't3ver_count', 't3ver_tstamp',
't3ver_move_id', 't3ver_swapmode',
$this->tca['ctrl']['transOrigDiffSourceField'],
$this->tca['ctrl']['cruser_id'],
$this->tca['ctrl']['fe_cruser_id'],
$this->tca['ctrl']['fe_crgroup_id'],
$this->tca['ctrl']['origUid'],
];
return in_array($columnName, $systemFields);
}
protected function isUserField(string $columnName) : bool
{
$config = $this->getColumnConfig($columnName);
return isset($config['type']) && $config['type'] === 'user';
}
protected function isPassthroughField(string $columnName) : bool
{
$config = $this->getColumnConfig($columnName);
return isset($config['type']) && $config['type'] === 'passthrough';
}
/**
* @throws InvalidArgumentException
*/
public function getColumnConfig(string $columnName) : array
{
if (!isset($this->tca['columns'][$columnName])) {
throw new InvalidArgumentException(
'Column does not exist.',
InvalidArgumentException::COLUMN_DOES_NOT_EXIST
);
}
return $this->tca['columns'][$columnName]['config'];
}
public function getLanguageUidColumn() : string
{
if (!isset($this->tca['ctrl']['languageField'])) {
return '';
}
return $this->tca['ctrl']['languageField'];
}
/**
* Checks whether the given record was blacklisted by root line.
* This can be configured by typoscript as whole root lines can be black listed.
*
* Also further TYPO3 mechanics are taken into account. Does a valid root
* line exist, is page inside a recycler, is inherited start- endtime
* excluded, etc.
*/
protected function isRecordBlacklistedByRootline(array &$record) : bool
{
$pageUid = $record['pid'];
if ($this->tableName === 'pages') {
$pageUid = $record['uid'];
}
try {
$rootline = $this->objectManager->get(RootlineUtility::class, $pageUid)->get();
} catch (\RuntimeException $e) {
$this->logger->notice(
sprintf('Could not fetch rootline for page %u, because: %s', $pageUid, $e->getMessage()),
[$record, $e]
);
return true;
}
foreach ($rootline as $pageInRootLine) {
// Check configured black list if present.
if ($this->isBlackListedRootLineConfigured()
&& in_array($pageInRootLine['uid'], $this->getBlackListedRootLine())
) {
$this->logger->info(
sprintf(
'Record %u is black listed due to configured root line configuration of page %u.',
$record['uid'],
$pageInRootLine['uid']
),
[$record, $pageInRootLine]
);
return true;
}
if ($pageInRootLine['extendToSubpages'] && (
($pageInRootLine['endtime'] > 0 && $pageInRootLine['endtime'] <= time())
|| ($pageInRootLine['starttime'] > 0 && $pageInRootLine['starttime'] >= time())
)) {
$this->logger->info(
sprintf(
'Record %u is black listed due to configured timing of parent page %u.',
$record['uid'],
$pageInRootLine['uid']
),
[$record, $pageInRootLine]
);
return true;
}
}
return false;
}
/**
* Checks whether any page uids are black listed.
*/
protected function isBlackListedRootLineConfigured() : bool
{
return (bool) $this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist');
}
/**
* Get the list of black listed root line page uids.
*
* @return array<Int>
*/
protected function getBlackListedRootLine() : array
{
return GeneralUtility::intExplode(
',',
$this->configuration->getIfExists('indexing.' . $this->getTableName() . '.rootLineBlacklist')
);
}
protected function getConnection() : \TYPO3\CMS\Core\Database\DatabaseConnection
{
return $GLOBALS['TYPO3_DB'];
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
/*
@ -22,25 +23,50 @@ namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
interface TcaTableServiceInterface
{
public function getTableName() : string;
/**
* @return string
*/
public function getTableName(): string;
public function getTableClause() : string;
/**
* @return string
*/
public function getTableClause(): string;
/**
* Filter the given records by root line blacklist settings.
* @param array $records
* @return void
*/
public function filterRecordsByRootLineBlacklist(array &$records);
/**
* @param array $record
* @return mixed
*/
public function prepareRecord(array &$record);
/**
* @throws InvalidArgumentException
* @param string $columnName
* @return array
*/
public function getColumnConfig(string $columnName) : array;
public function getColumnConfig(string $columnName): array;
public function getRecords(int $offset, int $limit) : array;
/**
* @param integer $offset
* @param integer $limit
* @return array
*/
public function getRecords(int $offset, int $limit): array;
public function getRecord(int $identifier) : array;
/**
* @param integer $identifier
* @return array
*/
public function getRecord(int $identifier): array;
public function getLanguageUidColumn() : string;
/**
* @return string
*/
public function getLanguageUidColumn(): string;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Model;
/*
@ -37,6 +38,8 @@ class FacetRequest implements FacetRequestInterface
/**
* As the facets come from configuration this might be a good idea to help
* integrators find issues.
* @param string $identifier
* @param array $config
*/
public function __construct(string $identifier, array $config)
{
@ -44,12 +47,18 @@ class FacetRequest implements FacetRequestInterface
$this->config = $config;
}
public function getIdentifier() : string
/**
* @return string
*/
public function getIdentifier(): string
{
return $this->identifier;
}
public function getConfig() : array
/**
* @return array
*/
public function getConfig(): array
{
return $this->config;
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Model;
/*
@ -28,32 +29,56 @@ namespace Codappix\SearchCore\Domain\Model;
*/
trait QueryResultInterfaceStub
{
/**
* @throws \BadMethodCallException
*/
public function getFirst()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502195121);
}
/**
* @throws \BadMethodCallException
*/
public function toArray()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502195135);
}
/**
* @param $offset
* @return boolean
* @throws \BadMethodCallException
*/
public function offsetExists($offset)
{
// Return false to allow Fluid to use appropriate getter methods.
return false;
}
/**
* @param $offset
* @throws \BadMethodCallException
*/
public function offsetGet($offset)
{
throw new \BadMethodCallException('Use getter to fetch properties.', 1502196933);
}
/**
* @param $offset
* @param $value
* @throws \BadMethodCallException
*/
public function offsetSet($offset, $value)
{
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196934);
}
/**
* @param $offset
* @throws \BadMethodCallException
*/
public function offsetUnset($offset)
{
throw new \BadMethodCallException('You are not allowed to modify the result.', 1502196936);

View file

@ -1,6 +1,9 @@
<?php
namespace Codappix\SearchCore\Domain\Model;
use Codappix\SearchCore\Connection\ResultItemInterface;
/*
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
*
@ -20,8 +23,6 @@ namespace Codappix\SearchCore\Domain\Model;
* 02110-1301, USA.
*/
use Codappix\SearchCore\Connection\ResultItemInterface;
class ResultItem implements ResultItemInterface
{
/**
@ -34,37 +35,65 @@ class ResultItem implements ResultItemInterface
*/
protected $type = '';
/**
* ResultItem constructor.
* @param array $result
* @param string $type
*/
public function __construct(array $result, string $type)
{
$this->data = $result;
$this->type = $type;
}
public function getType() : string
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
public function getPlainData() : array
/**
* @return array
*/
public function getPlainData(): array
{
return $this->data;
}
/**
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->data[$offset];
}
/**
* @param mixed $offset
* @param mixed $value
* @throws \BadMethodCallException
*/
public function offsetSet($offset, $value)
{
throw new \BadMethodCallException('It\'s not possible to change the search result.', 1499179077);
}
/**
* @param mixed $offset
* @throws \BadMethodCallException
*/
public function offsetUnset($offset)
{
throw new \BadMethodCallException('It\'s not possible to change the search result.', 1499179077);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Model;
/*
@ -24,6 +25,7 @@ use Codappix\SearchCore\Connection\ConnectionInterface;
use Codappix\SearchCore\Connection\FacetRequestInterface;
use Codappix\SearchCore\Connection\SearchRequestInterface;
use Codappix\SearchCore\Domain\Search\SearchService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
/**
* Represents a search request used to process an actual search.
@ -62,12 +64,12 @@ class SearchRequest implements SearchRequestInterface
*
* @var ConnectionInterface
*/
protected $connection = null;
protected $connection;
/**
* @var SearchService
*/
protected $searchService = null;
protected $searchService;
/**
* @param string $query
@ -77,12 +79,18 @@ class SearchRequest implements SearchRequestInterface
$this->query = $query;
}
public function getQuery() : string
/**
* @return string
*/
public function getQuery(): string
{
return $this->query;
}
public function getSearchTerm() : string
/**
* @return string
*/
public function getSearchTerm(): string
{
return $this->query;
}
@ -92,22 +100,30 @@ class SearchRequest implements SearchRequestInterface
*/
public function setFilter(array $filter)
{
$filter = \TYPO3\CMS\Core\Utility\ArrayUtility::removeArrayEntryByValue($filter, '');
$this->filter = \TYPO3\CMS\Extbase\Utility\ArrayUtility::removeEmptyElementsRecursively($filter);
$filter = ArrayUtility::removeArrayEntryByValue($filter, '');
$this->filter = ArrayUtility::removeNullValuesRecursive($filter);
}
public function hasFilter() : bool
/**
* @return bool
*/
public function hasFilter(): bool
{
return count($this->filter) > 0;
}
public function getFilter() : array
/**
* @return array
*/
public function getFilter(): array
{
return $this->filter;
}
/**
* Add a facet to gather in this search request.
*
* @param FacetRequestInterface $facet
*/
public function addFacet(FacetRequestInterface $facet)
{
@ -117,7 +133,7 @@ class SearchRequest implements SearchRequestInterface
/**
* Returns all configured facets to fetch in this search request.
*/
public function getFacets() : array
public function getFacets(): array
{
return $this->facets;
}
@ -125,28 +141,39 @@ class SearchRequest implements SearchRequestInterface
/**
* Define connection to use for this request.
* Necessary to allow implementation of execute for interface.
*
* @param ConnectionInterface $connection
*/
public function setConnection(ConnectionInterface $connection)
{
$this->connection = $connection;
}
/**
* @param SearchService $searchService
*/
public function setSearchService(SearchService $searchService)
{
$this->searchService = $searchService;
}
// Extbase QueryInterface
// Current implementation covers only paginate widget support.
/**
* Extbase QueryInterface
* Current implementation covers only paginate widget support.
*
* @param bool $returnRawQueryResult
* @return array|\Codappix\SearchCore\Connection\SearchResultInterface|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
* @throws \InvalidArgumentException
*/
public function execute($returnRawQueryResult = false)
{
if (! ($this->connection instanceof ConnectionInterface)) {
if (!($this->connection instanceof ConnectionInterface)) {
throw new \InvalidArgumentException(
'Connection was not set before, therefore execute can not work. Use `setConnection` before.',
1502197732
);
}
if (! ($this->searchService instanceof SearchService)) {
if (!($this->searchService instanceof SearchService)) {
throw new \InvalidArgumentException(
'SearchService was not set before, therefore execute can not work. Use `setSearchService` before.',
1520325175
@ -156,140 +183,270 @@ class SearchRequest implements SearchRequestInterface
return $this->searchService->processResult($this->connection->search($this));
}
/**
* @param integer $limit
* @return $this
*/
public function setLimit($limit)
{
$this->limit = (int) $limit;
$this->limit = (int)$limit;
return $this;
}
/**
* @param integer $offset
* @return $this
*/
public function setOffset($offset)
{
$this->offset = (int) $offset;
$this->offset = (int)$offset;
return $this;
}
/**
* @return integer
*/
public function getLimit()
{
return $this->limit;
}
/**
* @return integer
*/
public function getOffset()
{
return $this->offset;
}
/**
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface|void
* @throws \BadMethodCallException
*/
public function getSource()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196146);
}
/**
* @param array $orderings
* @return \TYPO3\CMS\Extbase\Persistence\QueryInterface|void
* @throws \BadMethodCallException
*/
public function setOrderings(array $orderings)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196163);
}
/**
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint
* @return \TYPO3\CMS\Extbase\Persistence\QueryInterface|void
* @throws \BadMethodCallException
*/
public function matching($constraint)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196197);
}
/**
* @param mixed $constraint1
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface|void
* @throws \BadMethodCallException
*/
public function logicalAnd($constraint1)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
}
/**
* @param mixed $constraint1
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface|void
* @throws \BadMethodCallException
*/
public function logicalOr($constraint1)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196198);
}
/**
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface|void
* @throws \BadMethodCallException
*/
public function logicalNot(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
}
/**
* @param string $propertyName
* @param mixed $operand
* @param bool $caseSensitive
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function equals($propertyName, $operand, $caseSensitive = true)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196199);
}
/**
* @param string $propertyName
* @param string $operand
* @param bool $caseSensitive
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function like($propertyName, $operand, $caseSensitive = true)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196167);
}
/**
* @param string $propertyName
* @param mixed $operand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function contains($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196200);
}
/**
* @param string $propertyName
* @param mixed $operand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function in($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196167);
}
/**
* @param string $propertyName
* @param mixed $operand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function lessThan($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196201);
}
/**
* @param string $propertyName
* @param mixed $operand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function lessThanOrEqual($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
/**
* @param string $propertyName
* @param mixed $operand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function greaterThan($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196202);
}
/**
* @param string $propertyName
* @param mixed $operand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|void
* @throws \BadMethodCallException
*/
public function greaterThanOrEqual($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
/**
* @return string|void
* @throws \BadMethodCallException
*/
public function getType()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196203);
}
/**
* @param \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings
* @throws \BadMethodCallException
*/
public function setQuerySettings(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
/**
* @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface|void
* @throws \BadMethodCallException
*/
public function getQuerySettings()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196205);
}
/**
* @return integer|void
* @throws \BadMethodCallException
*/
public function count()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196169);
}
/**
* @return array|void
* @throws \BadMethodCallException
*/
public function getOrderings()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196206);
}
/**
* @return null|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|void
* @throws \BadMethodCallException
*/
public function getConstraint()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196171);
}
/**
* @param string $propertyName
* @return bool|void
* @throws \BadMethodCallException
*/
public function isEmpty($propertyName)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196207);
}
/**
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source
* @throws \BadMethodCallException
*/
public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196172);
}
/**
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement|void
* @throws \BadMethodCallException
*/
public function getStatement()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196208);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Model;
/*
@ -20,9 +21,7 @@ namespace Codappix\SearchCore\Domain\Model;
* 02110-1301, USA.
*/
use Codappix\SearchCore\Connection\ResultItemInterface;
use Codappix\SearchCore\Connection\SearchResultInterface;
use Codappix\SearchCore\Domain\Model\QueryResultInterfaceStub;
/**
* Generic model for mapping a concrete search result from a connection.
@ -44,7 +43,7 @@ class SearchResult implements SearchResultInterface
/**
* @var array
*/
protected $results = [];
protected $results;
/**
* For Iterator interface.
@ -53,6 +52,11 @@ class SearchResult implements SearchResultInterface
*/
protected $position = 0;
/**
* SearchResult constructor.
* @param SearchResultInterface $originalSearchResult
* @param array $resultItems
*/
public function __construct(SearchResultInterface $originalSearchResult, array $resultItems)
{
$this->originalSearchResult = $originalSearchResult;
@ -62,44 +66,60 @@ class SearchResult implements SearchResultInterface
/**
* @return array<ResultItemInterface>
*/
public function getResults() : array
public function getResults(): array
{
$this->initResults();
return $this->results;
}
/**
* @return void
*/
protected function initResults()
{
if ($this->results !== []) {
return;
}
foreach ($this->resultItems as $item) {
$this->results[] = new ResultItem($item['data'], $item['type']);
if ($this->results === null) {
foreach ($this->resultItems as $item) {
$this->results[] = new ResultItem($item['data'], $item['type']);
}
}
}
public function getFacets() : array
/**
* @return array
*/
public function getFacets(): array
{
return $this->originalSearchResult->getFacets();
}
public function getCurrentCount() : int
/**
* @return integer
*/
public function getCurrentCount(): int
{
return $this->originalSearchResult->getCurrentCount();
}
/**
* @return integer
*/
public function count()
{
return $this->originalSearchResult->count();
}
/**
* @return mixed
*/
public function current()
{
return $this->getResults()[$this->position];
}
/**
* @return mixed
*/
public function next()
{
++$this->position;
@ -107,21 +127,33 @@ class SearchResult implements SearchResultInterface
return $this->current();
}
/**
* @return integer|mixed
*/
public function key()
{
return $this->position;
}
/**
* @return bool
*/
public function valid()
{
return isset($this->getResults()[$this->position]);
}
/**
* @return void
*/
public function rewind()
{
$this->position = 0;
}
/**
* @return \TYPO3\CMS\Extbase\Persistence\QueryInterface
*/
public function getQuery()
{
return $this->originalSearchResult->getQuery();

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Search;
/*
@ -25,7 +26,7 @@ use Codappix\SearchCore\Configuration\ConfigurationUtility;
use Codappix\SearchCore\Configuration\InvalidArgumentException;
use Codappix\SearchCore\Connection\SearchRequestInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\ArrayUtility;
class QueryFactory
{
@ -44,6 +45,12 @@ class QueryFactory
*/
protected $configurationUtility;
/**
* QueryFactory constructor.
* @param \TYPO3\CMS\Core\Log\LogManager $logManager
* @param ConfigurationContainerInterface $configuration
* @param ConfigurationUtility $configurationUtility
*/
public function __construct(
\TYPO3\CMS\Core\Log\LogManager $logManager,
ConfigurationContainerInterface $configuration,
@ -58,13 +65,20 @@ class QueryFactory
* TODO: This is not in scope Elasticsearch, therefore it should not return
* \Elastica\Query, but decide to use a more specific QueryFactory like
* ElasticaQueryFactory, once the second query is added?
*
* @param SearchRequestInterface $searchRequest
* @return \Elastica\Query
*/
public function create(SearchRequestInterface $searchRequest) : \Elastica\Query
public function create(SearchRequestInterface $searchRequest): \Elastica\Query
{
return $this->createElasticaQuery($searchRequest);
}
protected function createElasticaQuery(SearchRequestInterface $searchRequest) : \Elastica\Query
/**
* @param SearchRequestInterface $searchRequest
* @return \Elastica\Query
*/
protected function createElasticaQuery(SearchRequestInterface $searchRequest): \Elastica\Query
{
$query = [];
$this->addSize($searchRequest, $query);
@ -83,14 +97,22 @@ class QueryFactory
return new \Elastica\Query($query);
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
*/
protected function addSize(SearchRequestInterface $searchRequest, array &$query)
{
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'from' => $searchRequest->getOffset(),
'size' => $searchRequest->getLimit(),
]);
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
*/
protected function addSearch(SearchRequestInterface $searchRequest, array &$query)
{
if (trim($searchRequest->getSearchTerm()) === '') {
@ -108,9 +130,13 @@ class QueryFactory
$matchExpression['minimum_should_match'] = $minimumShouldMatch;
}
$query = ArrayUtility::setValueByPath($query, 'query.bool.must.0.multi_match', $matchExpression);
$query = ArrayUtility::setValueByPath($query, 'query.bool.must.0.multi_match', $matchExpression, '.');
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
*/
protected function addBoosts(SearchRequestInterface $searchRequest, array &$query)
{
try {
@ -137,7 +163,7 @@ class QueryFactory
}
if (!empty($boostQueryParts)) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'query' => [
'bool' => [
'should' => $boostQueryParts,
@ -147,6 +173,10 @@ class QueryFactory
}
}
/**
* @param array $query
* @return void
*/
protected function addFactorBoost(array &$query)
{
try {
@ -161,10 +191,15 @@ class QueryFactory
}
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
* @return void
*/
protected function addFields(SearchRequestInterface $searchRequest, array &$query)
{
try {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'stored_fields' => GeneralUtility::trimExplode(
',',
$this->configuration->get('searching.fields.stored_fields'),
@ -183,26 +218,36 @@ class QueryFactory
);
$scriptFields = $this->configurationUtility->filterByCondition($scriptFields);
if ($scriptFields !== []) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, ['script_fields' => $scriptFields]);
ArrayUtility::mergeRecursiveWithOverrule($query, ['script_fields' => $scriptFields]);
}
} catch (InvalidArgumentException $e) {
// Nothing configured
}
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
* @return void
*/
protected function addSort(SearchRequestInterface $searchRequest, array &$query)
{
$sorting = $this->configuration->getIfExists('searching.sort') ?: [];
$sorting = $this->configurationUtility->replaceArrayValuesWithRequestContent($searchRequest, $sorting);
$sorting = $this->configurationUtility->filterByCondition($sorting);
if ($sorting !== []) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, ['sort' => $sorting]);
ArrayUtility::mergeRecursiveWithOverrule($query, ['sort' => $sorting]);
}
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
* @return void
*/
protected function addFilter(SearchRequestInterface $searchRequest, array &$query)
{
if (! $searchRequest->hasFilter()) {
if (!$searchRequest->hasFilter()) {
return;
}
@ -215,7 +260,7 @@ class QueryFactory
);
}
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'query' => [
'bool' => [
'filter' => $filter,
@ -224,7 +269,13 @@ class QueryFactory
]);
}
protected function buildFilter(string $name, $value, array $config) : array
/**
* @param string $name
* @param $value
* @param array $config
* @return array
*/
protected function buildFilter(string $name, $value, array $config): array
{
if ($config === []) {
return [
@ -257,10 +308,15 @@ class QueryFactory
return [$config['field'] => $filter];
}
/**
* @param SearchRequestInterface $searchRequest
* @param array $query
* @return void
*/
protected function addFacets(SearchRequestInterface $searchRequest, array &$query)
{
foreach ($searchRequest->getFacets() as $facet) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'aggs' => [
$facet->getIdentifier() => $facet->getConfig(),
],

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Search;
/*
@ -74,7 +75,11 @@ class SearchService
$this->dataProcessorService = $dataProcessorService;
}
public function search(SearchRequestInterface $searchRequest) : SearchResultInterface
/**
* @param SearchRequestInterface $searchRequest
* @return SearchResultInterface
*/
public function search(SearchRequestInterface $searchRequest): SearchResultInterface
{
$this->addSize($searchRequest);
$this->addConfiguredFacets($searchRequest);
@ -89,6 +94,8 @@ class SearchService
/**
* Add configured size of search result items to request.
*
* @param SearchRequestInterface $searchRequest
*/
protected function addSize(SearchRequestInterface $searchRequest)
{
@ -99,6 +106,8 @@ class SearchService
/**
* Add facets from configuration to request.
*
* @param SearchRequestInterface $searchRequest
*/
protected function addConfiguredFacets(SearchRequestInterface $searchRequest)
{
@ -118,6 +127,8 @@ class SearchService
/**
* Add filters from configuration, e.g. flexform or TypoScript.
*
* @param SearchRequestInterface $searchRequest
*/
protected function addConfiguredFilters(SearchRequestInterface $searchRequest)
{
@ -137,8 +148,11 @@ class SearchService
/**
* Processes the result, e.g. applies configured data processing to result.
*
* @param SearchResultInterface $searchResult
* @return SearchResultInterface
*/
public function processResult(SearchResultInterface $searchResult) : SearchResultInterface
public function processResult(SearchResultInterface $searchResult): SearchResultInterface
{
try {
$newSearchResultItems = [];

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Domain\Service;
/*
@ -73,6 +74,7 @@ class DataHandler implements Singleton
}
/**
* DataHandler constructor.
* @param ConfigurationContainerInterface $configuration
* @param IndexerFactory $indexerFactory
*/
@ -82,12 +84,23 @@ class DataHandler implements Singleton
$this->indexerFactory = $indexerFactory;
}
/**
* @param string $table
* @param array $record
* @return void
* @throws NoMatchingIndexerException
*/
public function update(string $table, array $record)
{
$this->logger->debug('Record received for update.', [$table, $record]);
$this->getIndexer($table)->indexDocument($record['uid']);
}
/**
* @param string $table
* @param string $identifier
* @return void
*/
public function delete(string $table, string $identifier)
{
$this->logger->debug('Record received for delete.', [$table, $identifier]);
@ -95,14 +108,20 @@ class DataHandler implements Singleton
}
/**
* @param string $table
* @return IndexerInterface
* @throws NoMatchingIndexerException
*/
protected function getIndexer(string $table) : IndexerInterface
protected function getIndexer(string $table): IndexerInterface
{
return $this->indexerFactory->getIndexer($table);
}
public function supportsTable(string $table) : bool
/**
* @param string $table
* @return boolean
*/
public function supportsTable(string $table): bool
{
try {
$this->getIndexer($table);

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Hook;
/*
@ -24,8 +25,8 @@ use Codappix\SearchCore\Configuration\NoConfigurationException;
use Codappix\SearchCore\Domain\Service\DataHandler as OwnDataHandler;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\DataHandling\DataHandler as CoreDataHandler;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Log\Logger;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\SingletonInterface as Singleton;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
@ -48,6 +49,8 @@ class DataHandler implements Singleton
/**
* Dependency injection as TYPO3 doesn't provide it on it's own.
* Still you can submit your own dataHandler.
* @param OwnDataHandler $dataHandler
* @param Logger $logger
*/
public function __construct(OwnDataHandler $dataHandler = null, Logger $logger = null)
{
@ -71,18 +74,27 @@ class DataHandler implements Singleton
/**
* Called by CoreDataHandler on deletion of records.
*
* @param string $table
* @param string $uid
* @return bool
*/
public function processCmdmap_deleteAction(string $table, string $uid) : bool
public function processCmdmap_deleteAction(string $table, string $uid): bool
{
if (! $this->shouldProcessHookForTable($table)) {
if (!$this->shouldProcessHookForTable($table)) {
$this->logger->debug('Delete not processed.', [$table, $uid]);
return false;
}
$this->dataHandler->delete($table, (string) $uid);
$this->dataHandler->delete($table, $uid);
return true;
}
/**
* @param CoreDataHandler $dataHandler
* @return void
* @throws \Codappix\SearchCore\Domain\Index\NoMatchingIndexerException
*/
public function processDatamap_afterAllOperations(CoreDataHandler $dataHandler)
{
foreach ($dataHandler->datamap as $table => $record) {
@ -103,6 +115,12 @@ class DataHandler implements Singleton
}
}
/**
* @param array $parameters
* @param CoreDataHandler $dataHandler
* @return void
* @throws \Codappix\SearchCore\Domain\Index\NoMatchingIndexerException
*/
public function clearCachePostProc(array $parameters, CoreDataHandler $dataHandler)
{
$pageUid = 0;
@ -117,13 +135,19 @@ class DataHandler implements Singleton
}
if ($pageUid > 0) {
$this->processRecord('pages', (int) $pageUid);
$this->processRecord('pages', (int)$pageUid);
}
}
protected function processRecord(string $table, int $uid) : bool
/**
* @param string $table
* @param integer $uid
* @return bool
* @throws \Codappix\SearchCore\Domain\Index\NoMatchingIndexerException
*/
protected function processRecord(string $table, int $uid): bool
{
if (! $this->shouldProcessHookForTable($table)) {
if (!$this->shouldProcessHookForTable($table)) {
$this->logger->debug('Indexing of record not processed.', [$table, $uid]);
return false;
}
@ -138,13 +162,17 @@ class DataHandler implements Singleton
return false;
}
protected function shouldProcessHookForTable(string $table) : bool
/**
* @param string $table
* @return bool
*/
protected function shouldProcessHookForTable(string $table): bool
{
if ($this->dataHandler === null) {
$this->logger->debug('Datahandler could not be setup.');
return false;
}
if (! $this->dataHandler->supportsTable($table)) {
if (!$this->dataHandler->supportsTable($table)) {
$this->logger->debug('Table is not allowed.', [$table]);
return false;
}
@ -155,6 +183,8 @@ class DataHandler implements Singleton
/**
* Wrapper to allow unit testing.
*
* @param string $table
* @param integer $uid
* @return array|null
*/
protected function getRecord(string $table, int $uid)

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Integration\Form\Finisher;
/*
@ -46,10 +47,15 @@ class DataHandlerFinisher extends AbstractFinisher
'action' => '',
];
/**
* @return null|string|void
* @throws FinisherException
* @throws \Codappix\SearchCore\Domain\Index\NoMatchingIndexerException
*/
protected function executeInternal()
{
$action = $this->parseOption('action');
$record = ['uid' => (int) $this->parseOption('recordUid')];
$record = ['uid' => (int)$this->parseOption('recordUid')];
$tableName = $this->parseOption('indexIdentifier');
if ($action === '' || $tableName === '' || !is_string($tableName) || $record['uid'] === 0) {
@ -62,7 +68,7 @@ class DataHandlerFinisher extends AbstractFinisher
$this->dataHandler->update($tableName, $record);
break;
case 'delete':
$this->dataHandler->delete($tableName, (string) $record['uid']);
$this->dataHandler->delete($tableName, (string)$record['uid']);
break;
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace Codappix\SearchCore\Utility;
/*
@ -29,7 +30,10 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
*/
class FrontendUtility extends BackendUtility
{
protected static function getLanguageService() : TypoScriptFrontendController
/**
* @return TypoScriptFrontendController
*/
protected static function getLanguageService(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}

View file

@ -0,0 +1,7 @@
<?php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
'search_core',
'Configuration/TypoScript/',
'Search Core'
);

View file

@ -1,3 +1,12 @@
<?php
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['searchcore_search'] = 'recursive,pages';
call_user_func(function ($extension, $table) {
$plugin = TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
'Codappix.' . $extension,
'search',
'Search Core',
'EXT:search_core/Resources/Public/Icons/Plugin.svg'
) ?? 'searchcore_search';
$GLOBALS['TCA'][$table]['types']['list']['subtypes_excludelist'][$plugin] = 'recursive,pages';
}, 'search_core', 'tt_content');

View file

@ -10,6 +10,7 @@ DataProcessor and Connection. The other is how to contribute.
:maxdepth: 1
:glob:
development/configuration
development/indexer
development/dataProcessor
development/connection

View file

@ -0,0 +1,47 @@
.. _development_configuration:
Using custom (non-typoscript) configuration
===========================================
Configure your custom ext_localconf.php::
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class)
->registerImplementation(
\Codappix\SearchCore\Configuration\ConfigurationContainerInterface::class,
\YourNamespace\Configuration\SearchCoreConfigurationContainer::class
);
SearchCoreConfigurationContainer.php::
<?php
namespace YourNamespace\Configuration;
use Codappix\SearchCore\Configuration\ConfigurationContainer;
use Codappix\SearchCore\Configuration\NoConfigurationException;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* Class SearchCoreConfigurationContainer
* @package YourNamespace\Configuration
*/
class SearchCoreConfigurationContainer extends ConfigurationContainer
{
/**
* Inject settings via ConfigurationManager.
*
* @throws NoConfigurationException
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
parent::injectConfigurationManager($configurationManager);
// Now override settings with LocalConfiguration
if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['search_core'])) {
ArrayUtility::mergeRecursiveWithOverrule($this->settings, $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['search_core']);
}
// Or manipulate it your own custom way.
}
}

View file

@ -0,0 +1,10 @@
<svg viewBox="0 0 700 700" xmlns="http://www.w3.org/2000/svg">
<path d="M498.406 297.9c0 24.717-15.866 46.983-38.942 55.303a44.454 44.454 0 0 1 2.783 15.521c0 24.546-19.97 44.51-44.516 44.51-9.91 0-19.468-3.37-27.284-9.506-15.702 21.921-40.818 34.93-67.9 34.93-46.137 0-83.668-37.533-83.668-83.67 0-5.408.54-10.757 1.53-15.942-23.316-8.4-38.815-30.247-38.815-55.25 0-24.719 15.867-46.978 38.942-55.305a44.41 44.41 0 0 1-2.783-15.513c0-24.547 19.97-44.516 44.516-44.516a44.35 44.35 0 0 1 27.247 9.324C325.308 155.956 350.439 143 377.453 143c46.249 0 83.87 37.622 83.87 83.864 0 5.461-.531 10.705-1.552 15.95 23.21 8.461 38.635 30.21 38.635 55.085z" fill="#FEFEFE"/>
<path d="M318.287 270.333l64.946 29.608 65.527-57.415a72.526 72.526 0 0 0 1.412-14.4c0-40.397-32.86-73.264-73.256-73.264-24.167 0-46.708 11.9-60.41 31.852l-10.898 56.548 12.679 27.07z" fill="#FED10C"/>
<path d="M251.06 339.108a74.537 74.537 0 0 0-1.435 14.621c0 40.5 32.955 73.455 73.462 73.455 24.365 0 47.017-12.01 60.696-32.168l10.818-56.356-14.437-27.587-65.203-29.718-63.902 57.753z" fill="#22B9AF"/>
<path d="M250.648 226.714l44.508 10.51 9.752-50.606a35.146 35.146 0 0 0-21.313-7.193c-19.342 0-35.072 15.74-35.072 35.081 0 4.272.713 8.37 2.125 12.208" fill="#EE5097"/>
<path d="M246.784 237.315c-19.886 6.589-33.712 25.652-33.712 46.619 0 20.416 12.627 38.647 31.579 45.862l62.446-56.445-11.465-24.497-48.848-11.54z" fill="#15A6DF"/>
<path d="M395.2 395.014c6.104 4.692 13.576 7.251 21.203 7.251 19.34 0 35.072-15.73 35.072-35.072 0-4.265-.713-8.361-2.125-12.2l-44.472-10.407-9.678 50.428z" fill="#91C63F"/>
<path d="M404.249 332.929l48.958 11.45c19.894-6.574 33.72-25.644 33.72-46.618 0-20.38-12.657-38.59-31.646-45.781l-64.034 56.098 13.002 24.85z" fill="#0979A0"/>
<path d="M224.658 517.907c0-8.176-1.312-13.952-3.933-17.33-2.622-3.377-6.888-5.066-12.797-5.066-5.912 0-10.354 1.779-13.33 5.333-2.98 3.555-4.513 9.243-4.6 17.063h34.66zm3.6 28.927l3.865-.4.267 7.867c-10.13 1.419-18.796 2.132-25.995 2.132-9.598 0-16.397-2.776-20.397-8.332C182 542.548 180 533.904 180 522.173c0-23.373 9.285-35.06 27.861-35.06 8.976 0 15.685 2.51 20.13 7.53 4.443 5.023 6.665 12.912 6.665 23.663l-.533 7.599h-43.992c0 7.378 1.333 12.844 4 16.397 2.666 3.556 7.308 5.333 13.93 5.333 6.62 0 13.352-.267 20.196-.801zm20.341 9.084h9.998v-87.742h-9.998zm31.902-20.148c0 8.178 3.377 12.265 10.131 12.265 6.043 0 11.998-1.021 17.864-3.066l3.066-1.068v-21.995l-19.73 1.866c-3.999.357-6.888 1.512-8.664 3.466-1.78 1.956-2.667 4.799-2.667 8.532m41.06-27.195v33.623c0 3.332 8.296 3.199 8.296 3.199l-.51 8.833c-7.022 0-12.836.583-16.328-2.794-7.999 3.555-15.987 4.997-23.986 4.997-6.133 0-10.798-1.733-13.997-5.2-3.2-3.465-4.8-8.442-4.8-14.93 0-6.486 1.643-11.264 4.932-14.331 3.288-3.066 8.443-4.952 15.464-5.665l20.93-2v-5.732c0-4.532-.979-7.776-2.933-9.732-1.956-1.953-4.622-2.932-7.998-2.932l-26.27.022v-8.821h25.603c7.553 0 13.041 1.734 16.464 5.199 3.42 3.466 5.132 8.889 5.132 16.264m39.023-12.531c-9.688 0-14.531 3.378-14.531 10.13 0 3.114 1.11 5.313 3.333 6.6 2.22 1.29 7.286 2.623 15.197 3.999 7.909 1.379 13.508 3.313 16.797 5.799 3.287 2.49 4.933 7.156 4.933 13.998 0 6.844-2.2 11.864-6.6 15.063-4.4 3.2-10.82 4.8-19.262 4.8-5.512 0-23.901-2.04-23.901-2.04l.533-8.665c10.57 1.019 18.212 1.773 23.368 1.773 5.153 0 9.085-.821 11.797-2.466 2.71-1.644 4.066-4.4 4.066-8.265 0-3.866-1.156-6.487-3.465-7.865-2.313-1.377-7.38-2.688-15.198-3.933-7.821-1.243-13.377-3.066-16.663-5.466-3.29-2.4-4.933-6.842-4.933-13.331 0-6.486 2.287-11.285 6.865-14.397 4.577-3.11 10.286-4.666 17.13-4.666 5.42 0 24.23 1.379 24.23 1.379v8.717c-9.894-.452-18.01-1.164-23.696-1.164m73.949 1.066h-21.196v31.861c0 7.644.554 12.664 1.667 15.063 1.11 2.401 3.753 3.6 7.931 3.6l11.865-.8.667 8.265c-5.956.977-10.488 1.467-13.599 1.467-6.93 0-11.73-1.687-14.397-5.066-2.666-3.376-3.999-9.819-3.999-19.33v-35.06h-9.465v-8.665h9.465v-20.397h9.865v20.397h21.196v8.665zm13.9 58.808h9.999v-67.473h-9.999v67.473zm0-75.988h9.999v-11.598h-9.999v11.598zm53.304 7.182c2.935 0 7.91.534 14.932 1.6l3.198.4-.4 8.131c-7.11-.799-12.353-1.199-15.73-1.199-7.554 0-12.686 1.8-15.397 5.4-2.712 3.598-4.065 10.263-4.065 19.995 0 9.731 1.267 16.51 3.798 20.33 2.534 3.822 7.8 5.732 15.798 5.732l15.73-1.2.4 8.265c-8.264 1.243-14.442 1.866-18.53 1.866-10.399 0-17.576-2.666-21.529-7.998-3.955-5.332-5.932-14.33-5.932-26.995 0-12.664 2.132-21.55 6.399-26.662 4.265-5.109 11.375-7.665 21.328-7.665" fill="#221E1F"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,10 @@
<svg viewBox="0 0 325 325" xmlns="http://www.w3.org/2000/svg">
<path fill="#231F20" d="M0 0h325v325H0z"/>
<path d="M281.685 168.041c0 19.85-12.742 37.733-31.274 44.414a35.701 35.701 0 0 1 2.235 12.465c0 19.713-16.038 35.747-35.751 35.747-7.959 0-15.635-2.707-21.912-7.635-12.61 17.605-32.781 28.053-54.53 28.053-37.053 0-67.195-30.143-67.195-67.196 0-4.343.434-8.639 1.229-12.803-18.725-6.746-31.172-24.292-31.172-44.372 0-19.851 12.742-37.728 31.274-44.415a35.666 35.666 0 0 1-2.235-12.459c0-19.714 16.038-35.75 35.751-35.75a35.618 35.618 0 0 1 21.882 7.487c12.683-17.531 32.866-27.936 54.56-27.936 37.143 0 67.357 30.214 67.357 67.351 0 4.386-.427 8.597-1.246 12.81 18.64 6.795 31.027 24.261 31.027 44.239z" fill="#FEFEFE"/>
<path d="M137.031 145.902l52.159 23.779 52.625-46.11a58.246 58.246 0 0 0 1.134-11.565c0-32.443-26.39-58.839-58.833-58.839-19.408 0-37.511 9.557-48.515 25.58l-8.752 45.414 10.182 21.74v.001z" fill="#FED10C"/>
<path d="M83.04 201.136a59.861 59.861 0 0 0-1.152 11.742c0 32.526 26.467 58.992 58.998 58.992a58.803 58.803 0 0 0 48.745-25.834l8.688-45.26-11.594-22.155-52.365-23.867-51.32 46.382z" fill="#22B9AF"/>
<path d="M82.71 110.872l35.745 8.44 7.831-40.642a28.226 28.226 0 0 0-17.116-5.776c-15.534 0-28.167 12.64-28.167 28.173 0 3.431.573 6.722 1.707 9.805" fill="#EE5097"/>
<path d="M79.607 119.385c-15.97 5.292-27.074 20.602-27.074 37.44 0 16.396 10.14 31.038 25.36 36.832l50.151-45.331-9.207-19.674-39.23-9.267z" fill="#15A6DF"/>
<path d="M198.8 246.034c4.902 3.768 10.903 5.823 17.029 5.823 15.532 0 28.166-12.632 28.166-28.166 0-3.425-.573-6.715-1.707-9.798l-35.715-8.358-7.773 40.499z" fill="#91C63F"/>
<path d="M206.068 196.173l39.318 9.196c15.977-5.28 27.08-20.595 27.08-37.44 0-16.366-10.164-30.991-25.414-36.766l-51.426 45.052 10.442 19.958z" fill="#0979A0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,10 @@
<svg viewBox="0 0 325 325" xmlns="http://www.w3.org/2000/svg">
<circle fill="#231F20" cx="162.5" cy="162.5" r="150"/>
<path d="M281.685 168.041c0 19.85-12.742 37.733-31.274 44.414a35.701 35.701 0 0 1 2.235 12.465c0 19.713-16.038 35.747-35.751 35.747-7.959 0-15.635-2.707-21.912-7.635-12.61 17.605-32.781 28.053-54.53 28.053-37.053 0-67.195-30.143-67.195-67.196 0-4.343.434-8.639 1.229-12.803-18.725-6.746-31.172-24.292-31.172-44.372 0-19.851 12.742-37.728 31.274-44.415a35.666 35.666 0 0 1-2.235-12.459c0-19.714 16.038-35.75 35.751-35.75a35.618 35.618 0 0 1 21.882 7.487c12.683-17.531 32.866-27.936 54.56-27.936 37.143 0 67.357 30.214 67.357 67.351 0 4.386-.427 8.597-1.246 12.81 18.64 6.795 31.027 24.261 31.027 44.239z" fill="#FEFEFE"/>
<path d="M137.031 145.902l52.159 23.779 52.625-46.11a58.246 58.246 0 0 0 1.134-11.565c0-32.443-26.39-58.839-58.833-58.839-19.408 0-37.511 9.557-48.515 25.58l-8.752 45.414 10.182 21.74v.001z" fill="#FED10C"/>
<path d="M83.04 201.136a59.861 59.861 0 0 0-1.152 11.742c0 32.526 26.467 58.992 58.998 58.992a58.803 58.803 0 0 0 48.745-25.834l8.688-45.26-11.594-22.155-52.365-23.867-51.32 46.382z" fill="#22B9AF"/>
<path d="M82.71 110.872l35.745 8.44 7.831-40.642a28.226 28.226 0 0 0-17.116-5.776c-15.534 0-28.167 12.64-28.167 28.173 0 3.431.573 6.722 1.707 9.805" fill="#EE5097"/>
<path d="M79.607 119.385c-15.97 5.292-27.074 20.602-27.074 37.44 0 16.396 10.14 31.038 25.36 36.832l50.151-45.331-9.207-19.674-39.23-9.267z" fill="#15A6DF"/>
<path d="M198.8 246.034c4.902 3.768 10.903 5.823 17.029 5.823 15.532 0 28.166-12.632 28.166-28.166 0-3.425-.573-6.715-1.707-9.798l-35.715-8.358-7.773 40.499z" fill="#91C63F"/>
<path d="M206.068 196.173l39.318 9.196c15.977-5.28 27.08-20.595 27.08-37.44 0-16.366-10.164-30.991-25.414-36.766l-51.426 45.052 10.442 19.958z" fill="#0979A0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -26,7 +26,7 @@ class NonAllowedTablesWithMultipleTablesConfiguredTest extends NonAllowedTablesT
{
return array_merge(
parent::getTypoScriptFilesForFrontendRootPage(),
['EXT:search_core/ Tests/Functional/Fixtures/Hooks/DataHandler/MultipleAllowedTables.ts']
['EXT:search_core/Tests/Functional/Fixtures/Hooks/DataHandler/MultipleAllowedTables.ts']
);
}
}

View file

@ -31,7 +31,7 @@ class ProcessesAllowedTablesWithMultipleTablesConfiguredTest extends ProcessesAl
{
return array_merge(
parent::getTypoScriptFilesForFrontendRootPage(),
['EXT:search_core/ Tests/Functional/Fixtures/Hooks/DataHandler/MultipleAllowedTables.ts']
['EXT:search_core/Tests/Functional/Fixtures/Hooks/DataHandler/MultipleAllowedTables.ts']
);
}
}

View file

@ -68,6 +68,8 @@ class PagesIndexerTest extends AbstractFunctionalTestCase
* @test
* @dataProvider rootLineDataSets
* @param string $dataSetPath
* @throws \Codappix\SearchCore\Domain\Index\NoMatchingIndexerException
* @throws \TYPO3\TestingFramework\Core\Exception
*/
public function rootLineIsRespectedDuringIndexing($dataSetPath)
{

View file

@ -30,6 +30,9 @@ class ConfigurationUtilityTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider possibleRequestAndConfigurationForFluidtemplate
* @param SearchRequestInterface $searchRequest
* @param array $array
* @param array $expected
*/
public function recursiveEntriesAreProcessedAsFluidtemplate(
SearchRequestInterface $searchRequest,
@ -92,6 +95,8 @@ class ConfigurationUtilityTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider possibleConditionEntries
* @param array $entries
* @param array $expected
*/
public function conditionsAreHandledAsExpected(array $entries, array $expected)
{

View file

@ -28,6 +28,9 @@ class CopyToProcessorTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider getPossibleDataConfigurationCombinations
* @param array $record
* @param array $configuration
* @param array $expectedData
*/
public function fieldsAreCopiedAsConfigured(array $record, array $configuration, array $expectedData)
{

View file

@ -28,6 +28,9 @@ class GeoPointProcessorTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider getPossibleDataConfigurationCombinations
* @param array $record
* @param array $configuration
* @param array $expectedData
*/
public function geoPointsAreAddedAsConfigured(array $record, array $configuration, array $expectedData)
{

View file

@ -28,6 +28,9 @@ class RemoveProcessorTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider getPossibleDataConfigurationCombinations
* @param array $record
* @param array $configuration
* @param array $expectedData
*/
public function fieldsAreCopiedAsConfigured(array $record, array $configuration, array $expectedData)
{

View file

@ -31,6 +31,7 @@ class SearchRequestTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider possibleEmptyFilter
* @param array $filter
*/
public function emptyFilterWillNotBeSet(array $filter)
{

View file

@ -100,7 +100,7 @@ class SearchServiceTest extends AbstractUnitTestCase
->method('getIfExists')
->withConsecutive(['searching.size'], ['searching.facets'])
->will($this->onConsecutiveCalls(45, null));
$this->configuration->expects($this->any())
$this->configuration->expects($this->any())
->method('get')
->will($this->throwException(new InvalidArgumentException));
$this->connection->expects($this->once())

View file

@ -30,6 +30,8 @@ class DataHandlerToProcessorTest extends AbstractUnitTestCase
/**
* @test
* @dataProvider getPossibleCallCombinations
* @param array $parameters
* @param bool $expectCall
*/
public function fieldsAreCopiedAsConfigured(array $parameters, bool $expectCall)
{

View file

@ -63,6 +63,9 @@ class DataHandlerFinisherTest extends AbstractUnitTestCase
* @test
* @requires function \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher::setOptions
* @dataProvider possibleFinisherSetup
* @param string $action
* @param array $nonCalledActions
* @param $expectedSecondArgument
*/
public function validConfiguration(string $action, array $nonCalledActions, $expectedSecondArgument)
{
@ -101,6 +104,7 @@ class DataHandlerFinisherTest extends AbstractUnitTestCase
* @test
* @requires function \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher::setOptions
* @dataProvider invalidFinisherSetup
* @param array $options
*/
public function nothingHappensIfUnknownActionIsConfigured(array $options)
{

View file

@ -18,7 +18,7 @@ $EM_CONF[$_EXTKEY] = [
],
],
'state' => 'beta',
'version' => '0.0.7',
'version' => '0.1.0',
'author' => 'Daniel Siepmann',
'author_email' => 'coding@daniel-siepmann.de',
];

View file

@ -1,59 +1,51 @@
<?php
call_user_func(
function ($extensionKey) {
// TODO: Add hook for Extbase -> to handle records modified through
// Frontend and backend modules not using datahandler
$GLOBALS['TYPO3_CONF_VARS'] = TYPO3\CMS\Extbase\Utility\ArrayUtility::arrayMergeRecursiveOverrule(
$GLOBALS['TYPO3_CONF_VARS'],
[
'SC_OPTIONS' => [
'extbase' => [
'commandControllers' => [
Codappix\SearchCore\Command\IndexCommandController::class,
],
],
't3lib/class.t3lib_tcemain.php' => [
'clearCachePostProc' => [
$extensionKey => \Codappix\SearchCore\Hook\DataHandler::class . '->clearCachePostProc',
],
'processCmdmapClass' => [
$extensionKey => \Codappix\SearchCore\Hook\DataHandler::class,
],
'processDatamapClass' => [
$extensionKey => \Codappix\SearchCore\Hook\DataHandler::class,
],
call_user_func(function ($extension) {
// TODO: Add hook for Extbase -> to handle records modified through
// Frontend and backend modules not using datahandler
\TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule(
$GLOBALS['TYPO3_CONF_VARS'],
[
'SC_OPTIONS' => [
'extbase' => [
'commandControllers' => [
$extension => Codappix\SearchCore\Command\IndexCommandController::class,
],
],
't3lib/class.t3lib_tcemain.php' => [
'clearCachePostProc' => [
$extension => \Codappix\SearchCore\Hook\DataHandler::class . '->clearCachePostProc',
],
'processCmdmapClass' => [
$extension => \Codappix\SearchCore\Hook\DataHandler::class,
],
'processDatamapClass' => [
$extension => \Codappix\SearchCore\Hook\DataHandler::class,
],
],
]
);
TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Codappix.' . $extensionKey,
'search',
[
'Search' => 'search'
],
[
'Search' => 'search'
]
);
]
);
\Codappix\SearchCore\Compatibility\ImplementationRegistrationService::registerImplementations();
TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Codappix.' . $extension,
'search',
['Search' => 'search'],
['Search' => 'search']
);
// API does make use of object manager, therefore use GLOBALS
$extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$extensionKey]);
if ($extensionConfiguration === false
|| !isset($extensionConfiguration['disable.']['elasticsearch'])
|| $extensionConfiguration['disable.']['elasticsearch'] !== '1'
) {
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class)
->registerImplementation(
\Codappix\SearchCore\Connection\ConnectionInterface::class,
\Codappix\SearchCore\Connection\Elasticsearch::class
);
}
},
$_EXTKEY
);
\Codappix\SearchCore\Compatibility\ImplementationRegistrationService::registerImplementations();
// API does make use of object manager, therefore use GLOBALS
$extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$extension]);
if ($extensionConfiguration === false
|| !isset($extensionConfiguration['disable.']['elasticsearch'])
|| $extensionConfiguration['disable.']['elasticsearch'] !== '1'
) {
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class)
->registerImplementation(
\Codappix\SearchCore\Connection\ConnectionInterface::class,
\Codappix\SearchCore\Connection\Elasticsearch::class
);
}
}, $_EXTKEY);

View file

@ -1,13 +0,0 @@
<?php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
$_EXTKEY,
'Configuration/TypoScript/',
'Search Core'
);
TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
'Codappix.' . $_EXTKEY,
'search',
'Search Core'
);