Commit d6ff0975 authored by generalredneck's avatar generalredneck

Added plugins for the transformation system and refactored VNS entries into an object.

parent 96c2cce6
<?php
namespace Drupal\views_natural_sort\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Views Natural Sort Index Record Content Transformation item annotation object.
*
* @see \Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationManager
* @see plugin_api
*
* @Annotation
*/
class IndexRecordContentTransformation extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The label of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}
......@@ -32,31 +32,32 @@ class ConfigurationForm extends ConfigFormBase {
*/
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL) {
$config = $this->config('views_natural_sort.settings');
// TODO: Change this to be handled by the transformation plugins.
$form['beginning_words_remove'] = array(
'#type' => 'textfield',
'#title' => 'Words to filter from the beginning of a phrase',
'#default_value' => implode(',', $config->get('beginning_words_remove')),
'#default_value' => implode(',', $config->get('transformation_settings.remove_beginning_words.settings')),
'#description' => $this->t('Commonly, the words "A", "The", and "An" are removed when sorting book titles if they appear at the beginning of the title. Those would be great candidates for this field. Separate words with a comma.'),
);
$form['words_remove'] = array(
'#type' => 'textfield',
'#title' => 'Words to filter from anywhere in a phrase',
'#default_value' => implode(',', $config->get('words_remove')),
'#default_value' => implode(',', $config->get('transformation_settings.remove_words.settings')),
'#description' => $this->t('Commonly used words like "of", "and", and "or" are removed when sorting book titles. Words you would like filtered go here. Separate words with a comma.'),
);
$form['symbols_remove'] = array(
'#type' => 'textfield',
'#title' => 'Symbols to filter from anywhere in a phrase',
'#default_value' => $config->get('symbols_remove'),
'#default_value' => $config->get('transformation_settings.remove_symbols.settings'),
'#description' => $this->t('Most symbols are ignored when performing a sort naturally. Those symbols you want ignored go here. Do not use a separator. EX: &$".'),
);
$form['days_of_the_week_enabled'] = array(
'#type' => 'checkbox',
'#title' => 'Sort days of the week and their abbreviations',
'#description' => "Checking this setting will allow sorting of days of the week in their proper order starting with the day of the week that is configurable by you and for each language.",
'#efault_value' => $config->get('days_of_the_week_enabled'),
'#efault_value' => $config->get('transformation_settings.days_of_the_week.enabled'),
);
$form['rebuild_items_per_batch'] = array(
'#type' => 'number',
......@@ -86,6 +87,7 @@ class ConfigurationForm extends ConfigFormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
// TODO: Change this to be handled by the transformation plugins.
$beginning_words_remove = explode(',', $values['beginning_words_remove']);
array_walk(
$beginning_words_remove,
......@@ -102,10 +104,10 @@ class ConfigurationForm extends ConfigFormBase {
);
$symbols_remove = trim($values['symbols_remove']);
$this->config('views_natural_sort.settings')
->set('beginning_words_remove', $beginning_words_remove)
->set('words_remove', $words_remove)
->set('symbols_remove', $symbols_remove)
->set('days_of_the_week_enabled', $values['days_of_the_week_enabled'])
->set('transformation_settings.remove_beginning_words.settings', $beginning_words_remove)
->set('transformation_settings.remove_words.settings', $words_remove)
->set('transformation_settings.remove_symbols.settings', $symbols_remove)
->set('transformation_settings.days_of_the_week.enabled', $values['days_of_the_week_enabled'])
->set('rebuild_items_per_batch', $values['rebuild_items_per_batch'])
->save();
drupal_set_message($this->t('The configuration options have been saved.'));
......
<?php
namespace Drupal\views_natural_sort;
use Drupal\Core\Database\Connection;
class IndexRecord {
protected $eid;
protected $entityType;
protected $field;
protected $delta;
protected $content;
protected $transformations = [];
private $database;
public function __construct(Connection $database, array $values = []) {
$this->database = $database;
$this->setEntityId($values['eid']);
$this->setEntityType($values['entity_type']);
$this->setField($values['field']);
$this->setDelta($values['delta']);
$this->setContent($values['content']);
}
public function setEntityId($eid) {
$this->eid = $eid;
}
public function getEntityId() {
return $this->eid;
}
public function setEntityType($entity_type) {
$this->entityType = $entity_type;
}
public function getEntityType() {
return $this->entityType;
}
public function setField($field) {
$this->field = $field;
}
public function getField() {
return $this->field;
}
public function setDelta($delta) {
$this->delta = $delta;
}
public function getDelta() {
return $this->delta;
}
public function setContent($string) {
$this->content = $string;
}
public function getContent() {
return $this->content;
}
public function setTransformations(array $transformations) {
$this->transformations = $transformations;
}
public function getTransformations() {
return $this->transformations;
}
public function getTransformedContent() {
$transformed_content = $this->content;
foreach ($this->transformations as $transformation) {
$transformed_content = $transformation->transform($transformed_content);
}
return substr($transformed_content, 0, 255);
}
public function save() {
$this->database->merge('views_natural_sort')
->key([
'eid' => $this->eid,
'entity_type' => $this->entityType,
'field' => $this->field,
'delta' => $this->delta,
])
->fields([
'eid' => $this->eid,
'entity_type' => $this->entityType,
'field' => $this->field,
'delta' => $this->delta,
'content' => $this->getTransformedContent(),
])
->execute();
}
public function delete() {
$this->database->delete('views_natural_sort')
->condition('eid', $this->eid)
->condition('entity_type', $this->entityType)
->condition('field', $this->field)
->condition('delta', $this->delta)
->execute();
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\IndexRecordContentTransformation;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationBase as TransformationBase;
/**
* @IndexRecordContentTransformation (
* id = "days_of_the_week",
* label = @Translation("Days of the Week")
* )
*/
class DaysOfTheWeek extends TransformationBase {
public function transform($string) {
return $string;
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\IndexRecordContentTransformation;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationBase as TransformationBase;
/**
* @IndexRecordContentTransformation (
* id = "numbers",
* label = @Translation("Numbers")
* )
*/
class Numbers extends TransformationBase {
/**
* Transform numbers in a string into a natural sortable string.
*
* Rules are as follows:
* - Embeded numbers will sort in numerical order. The following
* possibilities are supported
* - A leading dash indicates a negative number, unless it is preceded by a
* non-whitespace character, which case it is considered just a dash.
* - Leading zeros are properly ignored so as to not influence sort order
* - Decimal numbers are supported using a period as the decimal character
* - Thousands separates are ignored, using the comma as the thous.
* character
* - Numbers may be up to 99 digits before the decimal, up to the precision
* of the processor.
*
* @param string $string
* The string we wish to transform.
*/
public function transform($string) {
// Find an optional leading dash (either preceded by whitespace or the first
// character) followed by either:
// - an optional series of digits (with optional embedded commas), then a
// period, then an optional series of digits
// - a series of digits (with optional embedded commas)
return preg_replace_callback(
'/(\s-|^-)?(?:(\d[\d,]*)?\.(\d+)|(\d[\d,]*))/',
[$this, 'numberTransformMatch'],
$string
);
}
/**
* Transforms a string representing numbers into a special format.
*
* This special format can be sorted as if it was a number but in reality is
* being sorted alphanumerically.
*
* @param array $match
* Array of matches passed from preg_replace_callback
* $match[0] is the entire matching string
* $match[1] if present, is the optional dash, preceded by optional
* whitespace
* $match[2] if present, is whole number portion of the decimal number
* $match[3] if present, is the fractional portion of the decimal number
* $match[4] if present, is the integer (when no fraction is matched).
*
* @return string
* String representing a numerical value that will sort numerically in an
* alphanumeric search.
*/
private function numberTransformMatch(array $match) {
// Remove commas and leading zeros from whole number.
$whole = (string) (int) str_replace(',', '', (isset($match[4]) && strlen($match[4]) > 0) ? $match[4] : $match[2]);
// Remove traililng 0's from fraction, then add the decimal and one trailing
// 0 and a space. The space serves as a way to always sort shorter decimal
// numbers that match exactly as less than longer ones.
// Ex: 3.05 and 3.05011.
$fraction = trim('.' . $match[3], '0') . '0 ';
$encode = sprintf('%02u', strlen($whole)) . $whole . $fraction;
if (strlen($match[1])) {
// Negative number. Make 10's complement. Put back any leading white space
// and the dash requires intermediate to avoid double-replacing the same
// digit. str_replace() seems to work by copying the source to the result,
// then successively replacing within it, rather than replacing from the
// source to the result.
// In this case since rules are reverced we also have to use a character
// that would be sorted higher than a space when a number is being
// compared against a longer one that is identical in negative numbers.
// This is so that longer numbers are always LESS than sorter numbers that
// have identical beginnings. Ex: -3.05 and -3.05011.
$digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '];
$intermediate = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'];
$rev_digits = ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', ':'];
$encode = $match[1] . str_replace($intermediate, $rev_digits, str_replace($digits, $intermediate, $encode));
}
return $encode;
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\IndexRecordContentTransformation;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationBase as TransformationBase;
/**
* @IndexRecordContentTransformation (
* id = "remove_beginning_words",
* label = @Translation("Remove Beginning Words")
* )
*/
class RemoveBeginningWords extends TransformationBase {
public function transform($string) {
$beginning_words = $this->configuration['settings'];
if (empty($beginning_words)) {
return $string;
}
array_walk($beginning_words, 'preg_quote');
return preg_replace(
'/^(' . implode('|', $beginning_words) . ')\s+/i',
'',
$string
);
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\IndexRecordContentTransformation;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationBase as TransformationBase;
/**
* @IndexRecordContentTransformation (
* id = "remove_symbols",
* label = @Translation("Remove Symbols")
* )
*/
class RemoveSymbols extends TransformationBase {
public function transform($string) {
$symbols = $this->configuration['settings'];
if (strlen($symbols) == 0) {
return $string;
}
return preg_replace(
'/[' . preg_quote($symbols) . ']/',
'',
$string
);
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\IndexRecordContentTransformation;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationBase as TransformationBase;
/**
* @IndexRecordContentTransformation (
* id = "remove_words",
* label = @Translation("Remove Words")
* )
*/
class RemoveWords extends TransformationBase {
public function transform($string) {
$words = $this->configuration['settings'];
if (empty($words)) {
return $string;
}
array_walk($words, 'preg_quote');
return preg_replace(
array(
'/\s(' . implode('|', $words) . ')\s+/i',
'/^(' . implode('|', $words) . ')\s+/i',
),
array(
' ',
'',
),
$string
);
}
}
<?php
namespace Drupal\views_natural_sort\Plugin;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base class for Views Natural Sort Index Record Content Transformation plugins.
*/
abstract class IndexRecordContentTransformationBase extends PluginBase implements IndexRecordContentTransformationInterface, ContainerFactoryPluginInterface {
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition
);
}
}
<?php
namespace Drupal\views_natural_sort\Plugin;
use Drupal\Component\Plugin\PluginInspectionInterface;
/**
* Defines an interface for Views Natural Sort Index Record Content Transformation plugins.
*/
interface IndexRecordContentTransformationInterface extends PluginInspectionInterface {
// Add get/set methods for your plugin type here.
public function transform($string);
}
<?php
namespace Drupal\views_natural_sort\Plugin;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
/**
* Provides the Views Natural Sort Index Record Content Transformation plugin manager.
*/
class IndexRecordContentTransformationManager extends DefaultPluginManager {
/**
* Constructor for IndexRecordContentTransformationManager objects.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/IndexRecordContentTransformation', $namespaces, $module_handler, 'Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationInterface', 'Drupal\views_natural_sort\Annotation\IndexRecordContentTransformation');
$this->alterInfo('views_natural_sort_vns_transformation_info');
$this->setCacheBackend($cache_backend, 'views_natural_sort_vns_transformation_plugins');
}
}
<?php
namespace Drupal\views_natural_sort;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Logger\LoggerChannelFactory;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Database\Connection;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationManager as TransformationManager;
/**
* Service that manages Views Natural Sort records.
*/
class ViewsNaturalSortService {
public $config;
/**
* Constructor.
*/
public function __construct(TransformationManager $transformationManager, ConfigFactory $configFactory, ModuleHandlerInterface $moduleHandler, LoggerChannelFactory $loggerFactory, Connection $database) {
$this->configFactory = $configFactory;
$this->moduleHandler = $moduleHandler;
$this->loggerFactory = $loggerFactory->get('views_natural_sort');
$this->transformationManager = $transformationManager;
$this->database = $database;
}
public function getTransformations(IndexRecord $record) {
$transformations = $this->getDefaultTransformations();
$this->moduleHandler->alter('views_natural_sort_transformations', $transformations, $record);
return $transformations;
}
public function getDefaultTransformations() {
$default_transformations = [
'remove_beginning_words',
'remove_words',
'remove_symbols',
'numbers',
'days_of_the_week',
];
$config = $this->configFactory->get('views_natural_sort.settings');
$transformations = [];
foreach ($default_transformations as $plugin_id) {
if ($config->get('transformation_settings.' . $plugin_id . '.enabled')) {
$transformations[] = $this->transformationManager->createInstance($plugin_id, $config->get('transformation_settings.' . $plugin_id));
}
}
return $transformations;
}
public function getSupportedEntityProperties() {
static $supported_properties = [];
if (empty($supported_properties)) {
$supported_properties = [
'node' => [
'title' => [
'base_table' => 'node_field_data',
'schema_field' => 'title',
],
],
];
}
return $supported_properties;
}
public function storeIndexRecordsFromEntity(EntityInterface $entity) {
$entity_type = $entity->getEntityTypeId();
$supported_entity_properties = $this->getSupportedEntityProperties();
foreach ($supported_entity_properties[$entity_type] as $field => $field_info) {
if (!isset($entity->{$field})) {
continue;
}
foreach ($entity->get($field)->getValue() as $delta => $value) {
$record = $this->createIndexRecord([
'eid' => $entity->id(),
'entity_type' => $entity_type,
'field' => $field,
'delta' => $delta,
// This may have to be passed in if it's not always ['value'].
'content' => $value['value'],
]);
$record->save();
}
}
}
public function createIndexRecord(array $values = []) {
$record = new IndexRecord($this->database, $values);
$transformations = $this->getTransformations($record);
$record->setTransformations($transformations);
return $record;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment