mirror of
https://github.com/Codappix/search_core.git
synced 2024-12-23 16:56:09 +01:00
[TASK] Add docprops to classes
+ Updated version pointer + Replaces deprecated ArrayUtility for Core utility
This commit is contained in:
parent
7940da14a7
commit
a93a7af27f
77 changed files with 1299 additions and 693 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Configuration;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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'];
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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']])
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Domain\Index;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace Codappix\SearchCore\Domain\Index\TcaIndexer;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 = '';
|
||||
|
||||
|
|
|
@ -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']
|
||||
) ?? '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(),
|
||||
],
|
||||
|
|
|
@ -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 = [];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'];
|
||||
}
|
||||
|
|
7
Configuration/TCA/Overrides/sys_template.php
Normal file
7
Configuration/TCA/Overrides/sys_template.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile(
|
||||
'search_core',
|
||||
'Configuration/TypoScript/',
|
||||
'Search Core'
|
||||
);
|
|
@ -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');
|
||||
|
|
|
@ -10,6 +10,7 @@ DataProcessor and Connection. The other is how to contribute.
|
|||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
development/configuration
|
||||
development/indexer
|
||||
development/dataProcessor
|
||||
development/connection
|
||||
|
|
47
Documentation/source/development/configuration.rst
Normal file
47
Documentation/source/development/configuration.rst
Normal 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.
|
||||
}
|
||||
}
|
||||
|
10
Resources/Public/Icons/Extension.svg
Normal file
10
Resources/Public/Icons/Extension.svg
Normal 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 |
10
Resources/Public/Icons/Module.svg
Normal file
10
Resources/Public/Icons/Module.svg
Normal 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 |
10
Resources/Public/Icons/Plugin.svg
Normal file
10
Resources/Public/Icons/Plugin.svg
Normal 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 |
|
@ -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']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ class SearchRequestTest extends AbstractUnitTestCase
|
|||
/**
|
||||
* @test
|
||||
* @dataProvider possibleEmptyFilter
|
||||
* @param array $filter
|
||||
*/
|
||||
public function emptyFilterWillNotBeSet(array $filter)
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -30,6 +30,8 @@ class DataHandlerToProcessorTest extends AbstractUnitTestCase
|
|||
/**
|
||||
* @test
|
||||
* @dataProvider getPossibleCallCombinations
|
||||
* @param array $parameters
|
||||
* @param bool $expectCall
|
||||
*/
|
||||
public function fieldsAreCopiedAsConfigured(array $parameters, bool $expectCall)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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',
|
||||
];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
);
|
Loading…
Reference in a new issue