elasticsearch_search_api-1.0.x-dev/src/KeymatchService.php
src/KeymatchService.php
<?php
namespace Drupal\elasticsearch_search_api;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Path\PathValidatorInterface;
use Drupal\elasticsearch_search_api\Factory\KeymatchEntryFactory;
/**
* Keymatch Service class.
*/
class KeymatchService {
const SEARCH_KEYMATCH_TYPE_TERM = 'TERM';
const SEARCH_KEYMATCH_TYPE_PHRASE = 'PHRASE';
const SEARCH_KEYMATCH_TYPE_EXACT = 'EXACT';
/**
* The config instance.
*
* @var \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
*/
protected $configInstance;
/**
* The path validator.
*
* @var \Drupal\Core\Path\PathValidatorInterface
*/
protected $pathValidator;
/**
* The keymatch entry factory.
*
* @var \Drupal\elasticsearch_search_api\Factory\KeymatchEntryFactory
*/
protected $keymatchEntryFactory;
/**
* KeymatchForm constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config instance.
* @param \Drupal\Core\Path\PathValidatorInterface $path_validator
* The path validator.
* @param \Drupal\elasticsearch_search_api\Factory\KeymatchEntryFactory $keymatch_entry_factory
* The keymatchEntry factory.
*/
public function __construct(ConfigFactoryInterface $config_factory, PathValidatorInterface $path_validator, KeymatchEntryFactory $keymatch_entry_factory) {
$this->configInstance = $config_factory->get('elasticsearch_search_api.keymatch');
$this->pathValidator = $path_validator;
$this->keymatchEntryFactory = $keymatch_entry_factory;
}
/**
* Returns the available keymatch types.
*
* @return array
* Returns the types in an array.
*/
public static function allKeymatchTypes() {
return [
KeymatchService::SEARCH_KEYMATCH_TYPE_TERM => KeymatchService::SEARCH_KEYMATCH_TYPE_TERM,
KeymatchService::SEARCH_KEYMATCH_TYPE_PHRASE => KeymatchService::SEARCH_KEYMATCH_TYPE_PHRASE,
KeymatchService::SEARCH_KEYMATCH_TYPE_EXACT => KeymatchService::SEARCH_KEYMATCH_TYPE_EXACT,
];
}
/**
* Finds keymatches for the given query.
*
* @param string $search_query
* The search query to match keymatches with.
*
* @return KeymatchEntry[]
* Returns an array of KeymatchEntry objects.
*/
public function find($search_query) {
$keymatch_configuration = $this->getKeymatchConfiguration();
$ordered_keymatches = $this->getKeymatchEntriesByType($keymatch_configuration);
$keymatches_found = [];
if (isset($search_query)) {
foreach ($ordered_keymatches as $type => $keymatches) {
$type_uppercase = strtoupper($type);
/** @var KeymatchEntry $keymatch */
foreach ($keymatches as $keymatch) {
$matches = FALSE;
switch ($type_uppercase) {
case KeymatchService::SEARCH_KEYMATCH_TYPE_EXACT:
$matches = $this->matchesExact($search_query, $keymatch->getQuery());
break;
case KeymatchService::SEARCH_KEYMATCH_TYPE_TERM:
$matches = $this->matchesTerm($search_query, $keymatch->getQuery());
break;
case KeymatchService::SEARCH_KEYMATCH_TYPE_PHRASE:
$matches = $this->matchesPhrase($search_query, $keymatch->getQuery());
break;
}
if ($matches) {
$keymatches_found[] = $keymatch;
}
}
}
}
return $keymatches_found;
}
/**
* Checks an exact keymatch.
*
* @param string $query
* The search query.
* @param string $keymatch
* The keymatch.
*
* @return bool
* TRUE if query matches on specified keymatch
*/
public function matchesExact($query, $keymatch) {
return $query === $keymatch;
}
/**
* Checks a keymatch with the type 'phrase'.
*
* @param string $query
* The search query.
* @param string $keymatch
* The keymatch.
*
* @return bool
* TRUE if query matches on specified keymatch
*/
public function matchesPhrase($query, $keymatch) {
$query_lower = strtolower($query);
$keymatch_lower = strtolower($keymatch);
$regex = '/\b' . $keymatch_lower . '\b/';
preg_match($regex, $query_lower, $match);
return isset($match) && !empty($match);
}
/**
* Checks a keymatch with the type 'term'.
*
* @param string $query
* The search query.
* @param string $keymatch
* The keymatch.
*
* @return bool
* TRUE if query matches on specified keymatch
*/
public function matchesTerm($query, $keymatch) {
$keymatch_terms = explode(' ', $keymatch);
if (!isset($keymatch_terms) || $keymatch_terms === FALSE) {
return FALSE;
}
foreach ($keymatch_terms as $term) {
if (!$this->matchesPhrase($query, $term)) {
return FALSE;
};
}
return TRUE;
}
/**
* Checks whether a keymatch string is valid or not.
*
* @param string $keymatch_entry
* Keymatch entry as a string, as it is stored in the config.
* @param bool $strict
* Check if url is external or valid internal path.
*
* @return bool
* Whether the keymatch entry is valid or not.
*/
public function isValid($keymatch_entry, $strict = FALSE) {
$parts = explode(',', $keymatch_entry);
if (!$parts || !is_array($parts) || empty($parts) || count($parts) < 4) {
return FALSE;
}
$trimmed_query = trim($parts[0]);
$trimmed_url = trim($parts[2]);
$trimmed_title = trim($parts[3]);
$valid_path = TRUE;
if ($strict !== FALSE) {
$valid_path = $this->pathValidator->isValid($trimmed_url);
}
return !empty($trimmed_query) && !empty($trimmed_title) && !empty($trimmed_url) && isset($this->allKeymatchTypes()[$parts[1]]) && $valid_path !== FALSE;
}
/**
* Creates an array of keymatches as strings, either from a value or config.
*
* @param string|null $saved_keymatches
* Uses this value if provided, otherwise it gets the value stored in
* config.
*
* @return array
* Returns an array of keymatch strings.
*/
public function getKeymatchConfiguration($saved_keymatches = NULL) {
if (!isset($saved_keymatches)) {
$config_keymatches = $this->configInstance->get('keymatches');
}
// If keymatches are empty, return an empty array.
if (empty($config_keymatches)) {
return [];
}
$saved_keymatches = $config_keymatches;
// Convert newlines to array based on \r\n or \n without empty matches.
$saved_keymatches = preg_split('/(\r\n?|\n)/', $saved_keymatches, -1, PREG_SPLIT_NO_EMPTY);
// Filter out values with only spaces.
$saved_keymatches = array_filter($saved_keymatches,
function ($split) {
$split_safe = trim($split);
return !empty($split_safe);
}
);
// Make sure our array indexes are reset in case entries were skipped.
return array_values($saved_keymatches);
}
/**
* Creates KeymatchEntry objects and orders them by type.
*
* @param array $config
* An array of keymatch strings as returned by getKeymatchConfiguration.
*
* @return array
* Returns KeymatchEntry objects ordered by type.
*/
public function getKeymatchEntriesByType(array $config) {
$keymatches_by_type = [];
foreach ($config as $keymatch_entry) {
if ($this->isValid($keymatch_entry)) {
$entry = $this->keymatchEntryFactory->createEntry($keymatch_entry);
$keymatches_by_type[$entry->getType()][] = $entry;
}
}
return $keymatches_by_type;
}
}
