...
 
Commits (36)
rebuild_items_per_batch: 100
transformation_settings:
remove_beginning_words:
enabled: true
settings:
- The
- A
- An
- La
- Le
- Il
remove_words:
enabled: true
settings:
- and
- or
- of
remove_symbols:
enabled: true
settings: "#\"'\\()[]"
numbers:
enabled: true
settings: []
days_of_the_week:
enabled: false
dependencies:
module:
- views_natural_sort
views_natural_sort.settings:
type: config_object
label: "Views Natural Sort Settings"
mapping:
dependencies:
type: config_dependencies
label: 'Dependencies'
rebuild_items_per_batch:
type: integer
label: "Number of records to rebuild per batch"
transformation_settings:
type: sequence
label: "All Transformation Settings"
sequence:
type: mapping
mapping:
enabled:
type: boolean
label: "Transformation Enabled"
settings:
type: ignore
label: "Transformation Settings"
<?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;
}
<?php
namespace Drupal\views_natural_sort\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Index record source plugin item annotation object.
*
* @see \Drupal\views_natural_sort\Plugin\IndexRecordSourcePluginManager
* @see plugin_api
*
* @Annotation
*/
class IndexRecordSourcePlugin extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The label of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}
<?php
namespace Drupal\views_natural_sort\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views_natural_sort\ViewsNaturalSortService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Defines a form that configures Views Natural Sort's settings.
*/
class ConfigurationForm extends ConfigFormBase {
protected $viewsNaturalSort;
public function __construct(ViewsNaturalSortService $viewsNaturalSort) {
$this->viewsNaturalSort = $viewsNaturalSort;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('views_natural_sort.service')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'view_natural_sort_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'views_natural_sort.settings',
];
}
/**
* {@inheritdoc}
*/
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'] = [
'#type' => 'textfield',
'#title' => 'Words to filter from the beginning of a phrase',
'#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'] = [
'#type' => 'textfield',
'#title' => 'Words to filter from anywhere in a phrase',
'#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'] = [
'#type' => 'textfield',
'#title' => 'Symbols to filter from anywhere in a phrase',
'#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'] = [
'#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('transformation_settings.days_of_the_week.enabled'),
];
$form['rebuild_items_per_batch'] = [
'#type' => 'number',
'#title' => 'Items per Batch',
'#default_value' => $config->get('rebuild_items_per_batch'),
'#min' => 0,
'#description' => $this->t('The number of items a batch process will work through at a given time. Raising this number will make the batch go quicker, however, raising it too high can cause timeouts and/or memory limit errors.'),
];
$form['rebuild'] = [
'#type' => 'details',
'#title' => $this->t('Incase of Emergency'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'button' => [
'#type' => 'submit',
'#description' => 'Incase of an emergency.',
'#value' => $this->t('Rebuild Index'),
'#submit' => [[$this, 'submitFormReindexOnly']],
],
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
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,
function (&$val) {
$val = trim($val);
}
);
$words_remove = explode(',', $values['words_remove']);
array_walk(
$words_remove,
function (&$val) {
$val = trim($val);
}
);
$symbols_remove = trim($values['symbols_remove']);
$this->config('views_natural_sort.settings')
->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.'));
$this->submitFormReindexOnly($form, $form_state);
}
/**
* Submission action for the "Rebuild Index" button.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function submitFormReindexOnly(array &$form, FormStateInterface $form_state) {
views_natural_sort_queue_data_for_rebuild();
}
}
<?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;
$this->generateType();
}
public function getEntityType() {
return $this->entityType;
}
public function setField($field) {
$this->field = $field;
$this->generateType();
}
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 mb_substr($transformed_content, 0, 255);
}
private function generateType() {
$this->type = new IndexRecordType($this->entityType, $this->field);
}
public function getType() {
return $this->type;
}
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;
use Drupal\views_natural_sort\Plugin\IndexRecordSourcePluginInterface as EntrySourcePluginInterface;
class IndexRecordType {
protected $entityType;
protected $field;
protected $entrySourcePlugin;
public function __construct($entity_type_id, $field_machine_name, $entry_source_plugin) {
$this->setEntityType($entity_type_id)
->setField($field_machine_name)
->setEntrySourcePlugin($entry_source_plugin);
}
public function getEntityType() {
return $this->entityType;
}
public function setEntityType($entity_type_id) {
$this->entityType = $entity_type_id;
return $this;
}
public function getField() {
return $this->field;
}
public function setField($field_machine_name) {
$this->field = $field_machine_name;
return $this;
}
public function getEntrySourcePlugin() {
return $this->entrySourcePlugin;
}
public function setEntrySourcePlugin(EntrySourcePluginInterface $entry_source_plugin) {
$this->entrySourcePlugin = $entry_source_plugin;
return $this;
}
}
<?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+/iu',
'',
$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) . ']/u',
'',
$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(
[
'/\s(' . implode('|', $words) . ')\s+/iu',
'/^(' . implode('|', $words) . ')\s+/iu',
],
[
' ',
'',
],
$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\Plugin\IndexRecordSourcePlugin;
use Drupal\views_natural_sort\Plugin\IndexRecordSourcePluginBase as EntrySourcePlugin;
use Drupal\views\ViewsData;
use Drupal\views_natural_sort\IndexRecordType as EntryType;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @IndexRecordSourcePlugin (
* id = "entity_property",
* label = @Translation("Entity Property")
* )
*/
class EntityProperty extends EntrySourcePlugin {
protected $viewsData;
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('views.views_data')
);
}
public function __construct(array $configuration, $plugin_id, $plugin_definition, ViewsData $views_data) {
$this->viewsData = $views_data;
}
public function getEntryTypes() {
static $types = [];
if (empty($types)) {
foreach ($this->getViewsSupportedEntityProperties() as $entity_type => $properties) {
foreach ($properties as $property => $schema_info) {
$entry_types[] = new EntryType($entity_type, $property, $this);
}
}
}
return $types;
}
public function getSupportedEntityProperties() {
static $supported_properties = [];
if (empty($supported_properties)) {
foreach ($this->entityFieldManager->getFieldMap() as $entity_type => $info) {
foreach ($info as $field_name => $field_info) {
if ($field_info['type'] == 'string' || $field_info['type'] == 'string_long') {
$fieldConfigs = $this->entityFieldManager->getFieldDefinitions($entity_type, reset($field_info['bundles']));
$fieldConfig = $fieldConfigs[$field_name];
if (empty($supported_properties[$entity_type])) {
$supported_properties[$entity_type] = [];
}
$base_table = $this->getViewsBaseTable($fieldConfig);
if (empty($base_table)) {
continue;
}
$supported_properties[$entity_type][$field_name] = [
'base_table' => $base_table,
// This may not be techincally correct. Research Further.
'schema_field' => $field_name,
];
}
}
}
}
return $supported_properties;
}
public function getViewsSupportedEntityProperties() {
static $views_supported_properties = [];
if (empty($views_supported_properties)) {
$supported_entity_properties = $this->getSupportedEntityProperties();
$views_data = $this->viewsData->getAll();
if (empty($views_data)) {
return FALSE;
}
foreach ($supported_entity_properties as $entity => $properties) {
foreach ($properties as $property => $schema_info) {
if (!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]) &&
!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']) &&
!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']['id']) &&
$views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']['id'] == 'natural') {
$views_supported_properties[$entity][$property] = $schema_info;
}
}
}
}
return $views_supported_properties;
}
/**
* @see EntityViewsData::getViewsData()
*/
public function getViewsBaseTable($fieldDefinition) {
$entityType = $this->entityTypeManager->getDefinition($fieldDefinition->getTargetEntityTypeId());
$base_table = $entityType->getBaseTable() ?: $entityType->id();
$views_revision_base_table = NULL;
$revisionable = $entityType->isRevisionable();
$base_field = $entityType->getKey('id');
$revision_table = '';
if ($revisionable) {
$revision_table = $entityType->getRevisionTable() ?: $entityType->id() . '_revision';
}
$translatable = $entityType->isTranslatable();
$data_table = '';
if ($translatable) {
$data_table = $entityType->getDataTable() ?: $entityType->id() . '_field_data';
}
// Some entity types do not have a revision data table defined, but still
// have a revision table name set in
// \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout() so we
// apply the same kind of logic.
$revision_data_table = '';
if ($revisionable && $translatable) {
$revision_data_table = $entityType->getRevisionDataTable() ?: $entityType->id() . '_field_revision';
}
$revision_field = $entityType->getKey('revision');
$views_base_table = $base_table;
if ($data_table) {
$views_base_table = $data_table;
}
//TODO Add support for finding Fields API Fields base tables. See views.views.inc.
return $views_base_table;
}
}
<?php
namespace Drupal\views_natural_sort\Plugin;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
/**
* Base class for Index record source plugin plugins.
*/
abstract class IndexRecordSourcePluginBase extends PluginBase implements IndexRecordSourcePluginInterface, ContainerFactoryPluginInterface {
// Add common methods and abstract methods for your plugin type here.
}
<?php
namespace Drupal\views_natural_sort\Plugin;
use Drupal\Component\Plugin\PluginInspectionInterface;
/**
* Defines an interface for Index record source plugin plugins.
*/
interface IndexRecordSourcePluginInterface extends PluginInspectionInterface {
/**
* Gets all the types that use this source plugin.
*
* @return array
* Array of IndexRecordType objects.
*/
public function getEntryTypes();
}
<?php
namespace Drupal\views_natural_sort\Plugin;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
/**
* Provides the Index record source plugin plugin manager.
*/
class IndexRecordSourcePluginManager extends DefaultPluginManager {
/**
* Constructs a new IndexRecordSourcePluginManager object.
*
* @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/IndexRecordSourcePlugin', $namespaces, $module_handler, 'Drupal\views_natural_sort\Plugin\IndexRecordSourcePluginInterface', 'Drupal\views_natural_sort\Annotation\IndexRecordSourcePlugin');
$this->alterInfo('views_natural_sort_index_record_source_plugin_info');
$this->setCacheBackend($cache_backend, 'views_natural_sort_index_record_source_plugin_plugins');
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\QueueWorker;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\views_natural_sort\ViewsNaturalSortService;
/**
* Provides base functionality for the VNS Entith Index Queue Workers.
*
* @QueueWorker(
* id = "views_natural_sort_entity_index",
* title = @Translation("Views Natural Sort Entity Index"),
* )
*/
class EntityIndexer extends QueueWorkerBase implements ContainerFactoryPluginInterface {
protected $entityTypeManager;
public function __construct(EntityTypeManager $entityTypeManager, ViewsNaturalSortService $viewsNaturalSortService) {
$this->entityTypeManager = $entityTypeManager;
$this->viewsNaturalSortService = $viewsNaturalSortService;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('entity_type.manager'),
$container->get('views_natural_sort.service')
);
}
/**
* {@inheritdoc}
*/
public function processItem($data) {
$entity = $this->entityTypeManager
->getStorage($data['entity_type'])
->load($data['entity_id']);
if ($entity) {
$this->viewsNaturalSortService->storeIndexRecordsFromEntity($entity);
}
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\views\sort;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Drupal\views\Plugin\views\sort\SortPluginBase;
/**
* Sort plugin used to allow Natural Sorting.
*
* @ingroup views_sort_handlers
*
* @ViewsSort("natural")
*/
class Natural extends SortPluginBase {
/**
* Flag defining this particular sort as Natural or not.
*
* @var bool
*/
protected $isNaturalSort;
/**
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->setNaturalSort(substr($this->options['order'], 0, 1) == 'N');
}
/**
* {@inheritdoc}
*/
public function query() {
// If this field isn't being used as a Natural Sort Field, move along
// nothing to see here.
if (!$this->isNaturalSort()) {
parent::query();
return;
}
// Add the Views Natural Sort table for this field.
$vns_alias = 'vns_' . $this->tableAlias;
if (empty($this->query->relationships[$vns_alias])) {
$this->ensureMyTable();
$vns_alias = $this->query->addRelationship('vns_' . $this->tableAlias, $this->naturalSortJoin(), $this->table, $this->relationship);
}
$this->query->addOrderBy($vns_alias, 'content', substr($this->options['order'], 1));
}
/**
* Adds the views_natural_sort table to the query.
*
* @return Drupal\views\Plugin\views\join\Standard
* Join object containing views_natural_sort table.
*/
public function naturalSortJoin() {
$storage = Views::viewsData()->getAll();
$table_data = $storage[$this->table];
$configuration = [
'table' => 'views_natural_sort',
'field' => 'eid',
'left_field' => $table_data['table']['base']['field'],
'left_table' => $this->table,
'extra' => [
[
'field' => 'entity_type',
'value' => $table_data['table']['entity type'],
],
[
'field' => 'field',
'value' => $this->realField,
],
],
];
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
return $join;
}
/**
* {@inheritdoc}
*/
protected function sortOptions() {
$options = parent::sortOptions();
$options['NASC'] = $this->t('Sort ascending naturally');
$options['NDESC'] = $this->t('Sort descending naturally');
return $options;
}
/**
* {@inheritdoc}
*/
public function adminSummary() {
if (!empty($this->options['exposed'])) {
return $this->t('Exposed');
}
$label = parent::adminSummary();
switch ($this->options['order']) {
case 'NASC':
return $this->t('natural asc');
case 'NDESC':
return $this->t('natural asc');
default:
return $label;
}
}
/**
* Determines if this query is natural sort.
*
* @return bool
* True if natural sort, False otherwise.
*/
public function isNaturalSort() {
return $this->isNaturalSort;
}
/**
* Sets the natural sort flag.
*
* @param bool $value
* The value.
*/
protected function setNaturalSort($value) {
$this->isNaturalSort = $value;
}
}
<?php
namespace Drupal\views_natural_sort\Plugin\views\sort;
use Drupal\views\Views;
/**
* Sort plugin used to allow Natural Sorting.
*
* @ingroup views_sort_handlers
*
* @ViewsSort("natural_field")
*/
class NaturalField extends Natural {
public function naturalSortJoin() {
//TODO DEBUG the stupid query.
$other_join = $this->getJoin();
$storage = Views::viewsData()->getAll();
$table_data = $storage[$other_join->leftTable];
$configuration = [
'table' => 'views_natural_sort',
'field' => 'eid',
'left_field' => 'entity_id',
'left_table' => $this->tableAlias,
'extra' => [
[
'field' => 'delta',
'value' => $this->tableAlias . '.delta',
],
[
'field' => 'entity_type',
'value' => $table_data['table']['entity type'],
],
[
'field' => 'field',
'value' => preg_replace('/_value$/', '', $this->field),
],
],
];
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
return $join;
}
}
/*
SELECT vns_node__field_vns_sort_text.content AS vns_node__field_vns_sort_text_content, node_field_data.nid AS nid
FROM
{node_field_data} node_field_data
LEFT JOIN {node__field_vns_sort_text} node__field_vns_sort_text ON node_field_data.nid = node__field_vns_sort_text.entity_id AND (node__field_vns_sort_text.deleted = '0' AND node__field_vns_sort_text.langcode = node_field_data.langcode)
LEFT JOIN {views_natural_sort} vns_node__field_vns_sort_text ON node__field_vns_sort_text.entity_id = vns_node__field_vns_sort_text.eid AND (vns_node__field_vns_sort_text.delta = 'node__field_vns_sort_text.delta' AND vns_node__field_vns_sort_text.entity_type = 'node' AND vns_node__field_vns_sort_text.field = 'field_vns_sort_text')
WHERE node_field_data.type IN ('views_natural_sort_test_content')
ORDER BY vns_node__field_vns_sort_text_content DESC
*/
<?php
namespace Drupal\views_natural_sort;
use Drupal\Core\Config\ConfigFactory;
<<<<<<< HEAD
use Drupal\Core\Database\Connection;
=======
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
>>>>>>> 8.x-2.x
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldTypePluginManager;
use Drupal\Core\Logger\LoggerChannelFactory;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\views\ViewsData;
use Drupal\views_natural_sort\Plugin\IndexRecordContentTransformationManager as TransformationManager;
use Drupal\views_natural_sort\Plugin\IndexRecordSourcePluginManager as EntrySourcePluginManager;
/**
* Service that manages Views Natural Sort records.
*/
class ViewsNaturalSortService {
/**
* Constructor.
*/
<<<<<<< HEAD
public function __construct(TransformationManager $transformationManager, ConfigFactory $configFactory, ModuleHandlerInterface $moduleHandler, LoggerChannelFactory $loggerFactory, Connection $database, ViewsData $viewsData, QueueFactory $queue, QueueWorkerManagerInterface $queueManager, EntityFieldManagerInterface $entityFieldManager, EntityTypeManagerInterface $entityTypeManager, FieldTypePluginManager $fieldTypeManager, EntrySourcePluginManager $entry_source_plugin_manager) {
=======
public function __construct(TransformationManager $transformationManager, ConfigFactory $configFactory, ModuleHandlerInterface $moduleHandler, LoggerChannelFactoryInterface $loggerFactory, Connection $database, ViewsData $viewsData, QueueFactory $queue, QueueWorkerManagerInterface $queueManager, EntityFieldManagerInterface $entityFieldManager, EntityTypeManagerInterface $entityTypeManager) {
>>>>>>> 8.x-2.x
$this->configFactory = $configFactory;
$this->moduleHandler = $moduleHandler;
$this->loggerFactory = $loggerFactory->get('views_natural_sort');
$this->transformationManager = $transformationManager;
$this->database = $database;
$this->viewsData = $viewsData;
$this->queue = $queue;
$this->queueManager = $queueManager;
$this->entityFieldManager = $entityFieldManager;
$this->entityTypeManager = $entityTypeManager;
$this->fieldTypeManager = $fieldTypeManager;
$this->entrySourcePluginManager = $entry_source_plugin_manager;
}
/**
* Get the full list of transformations to run when saving an index record.
*
* @param \Drupal\views_natural_sort\IndexRecord $record
* The original entry to be written to the views_natural_sort table.
*
* @return array
* The final list of transformations.
*/
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 getEntryTypes() {
static $entry_types = [];
if (empty($entry_types)) {
$entry_sources = $this->entrySourcePluginManager->getDefinitions();
foreach ($entry_sources as $plugin_id => $definition) {
$entry_source_plugin = $this->entrySourcePluginManager->createInstance($plugin_id);
foreach ($entry_source_plugin->getEntryTypes() as $entry_type) {
$entry_types[$entry_type->getEntityType() . '|' . $entry_type->getField()] = $entry_type;
}
}
$this->moduleHandler->alter('views_natural_sort_get_entry_types', $entry_types);
}
return $entry_types;
}
/**
* Retrieve the full list of entities and properties that can be supported.
*
* @return array
* An array of property information keyed by entity machine name. Example:
* [
* 'node' => [
* 'type' => [
* 'base_table' => 'node',
* 'schema_field' => 'type',
* ]
* 'title' => [
* 'base_table' => 'node',
* 'schema_field' => 'title',
* ]
* 'language' => [
* 'base_table' => 'node',
* 'schema_field' => 'language',
* ]
* ]
* 'user' => [
* 'name' => [
* 'base_table' => 'users',
* 'schema_field' => 'name',
* ]
* 'mail' => [
* 'base_table' => 'users',
* 'schema_field' => 'mail',
* ]
* 'theme' => [
* 'base_table' => 'users',
* 'schema_field' => 'theme',
* ]
* ]
* 'file' => [
* 'name' => [
* 'base_table' => 'file_managed',
* 'schema_field' => 'filename',
* ]
* 'mime' => [
* 'base_table' => 'file_managed',
* 'schema_field' => 'filemime',
* ]
* ]
* )
*/
public function getViewsSupportedEntityProperties() {
static $views_supported_properties = [];
if (empty($views_supported_properties)) {
$supported_entity_properties = $this->getSupportedEntityProperties();
$views_data = $this->viewsData->getAll();
if (empty($views_data)) {
return FALSE;
}
foreach ($supported_entity_properties as $entity => $properties) {
foreach ($properties as $property => $schema_info) {
if (!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']])) {
if (isset($views_data[$schema_info['base_table']][$schema_info['schema_field']]['field']['real field'])) {
$schema_info['schema_field'] = $views_data[$schema_info['base_table']][$schema_info['schema_field']]['field']['real field'];
}
if (!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']) &&
!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']['id']) &&
$views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']['id'] == 'natural') {
$views_supported_properties[$entity][$property] = $schema_info;
}
}
}
}
}
return $views_supported_properties;
}
public function storeIndexRecordsFromEntity(EntityInterface $entity) {
// TODO: Consider abstracting this out. The creation and storage of records
// should be handled by a converter class that interacts with specific
// IndexRecordTypes and creates IndexRecords. Those would probably be called
// directly and have nothign to do with this service.
$entity_type = $entity->getEntityTypeId();
$supported_entity_properties = $this->getViewsSupportedEntityProperties();
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 queueDataForRebuild(array $entry_types = []) {
if (empty($entry_types)) {
$entry_types = $this->moduleHandler->invokeAll('views_natural_sort_get_entry_types');
}
$queues = [];
foreach ($entry_types as $entry_type) {
$queues = array_unique(array_merge($queues, array_filter($this->moduleHandler->invokeAll('views_natural_sort_queue_rebuild_data', $entry_type))));
}
$operations = [];
foreach ($queues as $queue) {
$operations[] = [
[$this, 'rebuildIndex'],
[$queue],
];
}
$batch = [
'operations' => $operations,
'title' => t('Rebuilding Views Natural Sort Indexing Entries'),
'finished' => [$this, 'finishRebuild'],
];
batch_set($batch);
}
public function finishRebuild($success, $results, $operations) {
if ($success) {
drupal_set_message($this->t('Index rebuild has completed.'));
drupal_set_message($this->t('Indexed %count.', [
'%count' => format_plural($results['entries'], '1 entry', '@count entries'),
]));
}
}
public function createIndexRecord(array $values = []) {
$record = new IndexRecord($this->database, $values);
$transformations = $this->getTransformations($record);
$record->setTransformations($transformations);
return $record;
}
}
//SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '= vns_node__field_vns_sort_text.eid AND (vns_node__field_vns_sort_text.entity_ty' at line 3: SELECT vns_node_field_data.content AS vns_node_field_data_content, vns_node__field_vns_sort_text.content AS vns_node__field_vns_sort_text_content, node_field_data.nid AS nid FROM {node_field_data} node_field_data LEFT JOIN {views_natural_sort} vns_node_field_data ON node_field_data.nid = vns_node_field_data.eid AND (vns_node_field_data.entity_type = :views_join_condition_0 AND vns_node_field_data.field = :views_join_condition_1) LEFT JOIN {node__field_vns_sort_text} node__field_vns_sort_text ON node_field_data.nid = node__field_vns_sort_text.entity_id AND (node__field_vns_sort_text.deleted = :views_join_condition_2 AND node__field_vns_sort_text.langcode = node_field_data.langcode) LEFT JOIN {views_natural_sort} vns_node__field_vns_sort_text ON node__field_vns_sort_text. = vns_node__field_vns_sort_text.eid AND (vns_node__field_vns_sort_text.entity_type = :views_join_condition_4 AND vns_node__field_vns_sort_text.field = :views_join_condition_5) WHERE node_field_data.type IN (:db_condition_placeholder_6) ORDER BY vns_node_field_data_content ASC, vns_node__field_vns_sort_text_content ASC; Array ( [:db_condition_placeholder_6] => views_natural_sort_test_content [:views_join_condition_0] => node [:views_join_condition_1] => title [:views_join_condition_2] => 0 [:views_join_condition_4] => [:views_join_condition_5] => field_vns_sort_text_value )
langcode: en
status: true
name: 'Views Natural Sort Test Content'
type: views_natural_sort_test_content
description: 'Content type used for testing the basic functionality of the Views Natural Sort module.'
help: ''
new_revision: false
preview_mode: 1
display_submitted: true
langcode: en
status: true
dependencies:
config:
- node.type.views_natural_sort_test_content
module:
- node
- user
- views_natural_sort
id: views_natural_sort_test
label: 'Views Natural Sort Test'
module: views
description: 'View used to test the basic functionality of the Views Natural Sort module.'
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
offset: 0
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false