admin view for taxo genres and langues, translation for taxo genres and langues, synonyms for genres
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Annotation for synonyms provider plugin instance.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class SynonymsProvider extends Plugin {
|
||||
|
||||
/**
|
||||
* Machine readable name of this plugin,
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Human readable name of this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* Synonyms behavior service ID into which this plugin provides synonyms.
|
||||
*
|
||||
* Synonyms behaviors are the services with the tag 'synonyms_behavior'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $synonyms_behavior_service;
|
||||
|
||||
/**
|
||||
* Entity type which is controlled by ths plugin.
|
||||
*
|
||||
* Entity type into which this plugin provides synonyms.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controlled_entity_type;
|
||||
|
||||
/**
|
||||
* Bundle which is controlled by this plugin.
|
||||
*
|
||||
* Bundle into which this plugin provides synonyms. If the entity type does
|
||||
* not support bundles, just put here the entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controlled_bundle;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\AutocompleteService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Synonyms friendly entity autocomplete controller.
|
||||
*/
|
||||
class EntityAutocomplete extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var AutocompleteService
|
||||
*/
|
||||
protected $autocompleteService;
|
||||
|
||||
/**
|
||||
* EntityAutocomplete constructor.
|
||||
*/
|
||||
public function __construct(AutocompleteService $autocomplete_service) {
|
||||
$this->autocompleteService = $autocomplete_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('synonyms.behavior.autocomplete')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing callback: provide suggestions for autocomplete form element.
|
||||
*/
|
||||
public function autocomplete(Request $request, EntityTypeInterface $target_type, $token) {
|
||||
$matches = [];
|
||||
|
||||
if ($input = $request->get('q')) {
|
||||
$input = Tags::explode($input);
|
||||
$input = array_pop($input);
|
||||
foreach ($this->autocompleteService->autocompleteLookup($input, $token) as $k => $suggestion) {
|
||||
$key = $suggestion['entity_label'] . ' (' . $suggestion['entity_id'] . ')';
|
||||
$key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key)))));
|
||||
|
||||
// Names containing commas or quotes must be wrapped in quotes.
|
||||
$key = Tags::encode($key);
|
||||
|
||||
$matches[] = [
|
||||
'value' => $key,
|
||||
'label' => $suggestion['wording'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Controller for admin UI of the module.
|
||||
*/
|
||||
class SynonymConfigController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* SynonymConfigController constructor.
|
||||
*/
|
||||
public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info, BehaviorService $behavior_service) {
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('synonyms.behaviors')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing callback: show overview table of synonyms configuration.
|
||||
*/
|
||||
public function overview() {
|
||||
$render = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Entity type'),
|
||||
$this->t('Bundle'),
|
||||
$this->t('Manage'),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->entityTypeManager()->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityTypeInterface) {
|
||||
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
|
||||
$links = [];
|
||||
foreach ($this->behaviorService->getBehaviorServices() as $service_id => $service) {
|
||||
$count = count($this->behaviorService->getSynonymConfigEntities($service_id, $entity_type->id(), $bundle));
|
||||
|
||||
if ($count > 0) {
|
||||
$title = $this->t('@behavior (@count)', [
|
||||
'@behavior' => $service['service']->getTitle(),
|
||||
'@count' => $count,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$title = $service['service']->getTitle();
|
||||
}
|
||||
|
||||
$links[$service_id] = [
|
||||
'title' => $title,
|
||||
'url' => Url::fromRoute('entity.synonym.overview.entity_type.bundle.behavior', [
|
||||
'synonyms_entity_type' => $entity_type->id(),
|
||||
'bundle' => $bundle,
|
||||
'synonyms_behavior_service' => $service_id,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
$render[] = [
|
||||
['#markup' => Html::escape($entity_type->getLabel())],
|
||||
['#markup' => $bundle == $entity_type->id() ? '' : Html::escape($bundle_info['label'])],
|
||||
['#type' => 'operations', '#links' => $links],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing callback to overview a particular entity type and behavior service.
|
||||
*/
|
||||
public function overviewEntityTypeBundle(EntityTypeInterface $synonyms_entity_type, $bundle, SynonymsBehaviorInterface $synonyms_behavior_service) {
|
||||
$table = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Provider'),
|
||||
$this->t('Operations'),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->entityTypeManager()->getStorage('synonym')->loadMultiple() as $synonym_config) {
|
||||
$provider_instance = $synonym_config->getProviderPluginInstance();
|
||||
$provider_definition = $provider_instance->getPluginDefinition();
|
||||
if ($provider_definition['controlled_entity_type'] == $synonyms_entity_type->id() && $provider_definition['controlled_bundle'] == $bundle && $this->behaviorService->getBehaviorService($provider_definition['synonyms_behavior_service'])['service'] == $synonyms_behavior_service) {
|
||||
$table[] = [
|
||||
['#markup' => Html::escape($synonym_config->label())],
|
||||
[
|
||||
'#type' => 'operations',
|
||||
'#links' => $this->entityTypeManager()->getListBuilder($synonym_config->getEntityTypeId())->getOperations($synonym_config),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for 'entity.synonym.overview.entity_type.bundle.behavior'.
|
||||
*/
|
||||
public function overviewEntityTypeBundleTitle(EntityTypeInterface $synonyms_entity_type, $bundle, SynonymsBehaviorInterface $synonyms_behavior_service) {
|
||||
if ($synonyms_entity_type->id() == $bundle) {
|
||||
return $this->t('Manage @behavior of @entity_type', [
|
||||
'@behavior' => $synonyms_behavior_service->getTitle(),
|
||||
'@entity_type' => $synonyms_entity_type->getLabel(),
|
||||
]);
|
||||
}
|
||||
|
||||
$bundle_info = $this->entityTypeBundleInfo->getBundleInfo($synonyms_entity_type->id());
|
||||
|
||||
return $this->t('Manage @behavior of @entity_type @bundle', [
|
||||
'@behavior' => $synonyms_behavior_service->getTitle(),
|
||||
'@entity_type' => $synonyms_entity_type->getLabel(),
|
||||
'@bundle' => $bundle_info[$bundle]['label'],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Element;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Textfield;
|
||||
use Drupal\Core\Site\Settings;
|
||||
|
||||
/**
|
||||
* Form element for synonyms-friendly entity autocomplete.
|
||||
*
|
||||
* @FormElement("synonyms_entity_autocomplete")
|
||||
*/
|
||||
class SynonymsEntityAutocomplete extends Textfield {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$info = parent::getInfo();
|
||||
|
||||
// Target entity type for suggestions.
|
||||
$info['#target_type'] = NULL;
|
||||
|
||||
// String or array of allowed target bundles. If omitted, all bundles will
|
||||
// be included in autocomplete suggestions.
|
||||
$info['#target_bundles'] = NULL;
|
||||
|
||||
// Default maximum amount of provided suggestions.
|
||||
$info['#suggestion_size'] = 10;
|
||||
|
||||
// Whether to suggest same entity at most once (in the case, when more than
|
||||
// 1 synonym triggers inclusion of that entity).
|
||||
$info['#suggest_only_unique'] = FALSE;
|
||||
|
||||
// Operator to match keyword. Allowed values are:
|
||||
// - CONTAINS
|
||||
// - STARTS_WITH
|
||||
$info['#match'] = 'CONTAINS';
|
||||
|
||||
array_unshift($info['#process'], [get_class($this), 'elementSynonymsEntityAutocomplete']);
|
||||
$info['#element_validate'][] = [get_class($this), 'validateEntityAutocomplete'];
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$entities = NULL;
|
||||
if ($input === FALSE) {
|
||||
if (isset($element['#default_value'])) {
|
||||
$entities = [];
|
||||
foreach ($element['#default_value'] as $entity) {
|
||||
$entities[] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Potentially the #value is set directly, so it contains the 'target_id'
|
||||
// array structure instead of a string.
|
||||
elseif ($input !== FALSE && is_array($input)) {
|
||||
$entity_ids = array_map(function(array $item) {
|
||||
return $item['target_id'];
|
||||
}, $input);
|
||||
$entities = \Drupal::entityTypeManager()->getStorage($element['#target_type'])->loadMultiple($entity_ids);
|
||||
}
|
||||
|
||||
if (is_array($entities)) {
|
||||
$value = [];
|
||||
foreach ($entities as $entity) {
|
||||
$value[] = $entity->label() . ' (' . $entity->id() . ')';
|
||||
}
|
||||
return Tags::implode($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element process callback for 'synonyms_entity_autocomplete' type.
|
||||
*/
|
||||
public static function elementSynonymsEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$data = [
|
||||
'target_type' => $element['#target_type'],
|
||||
'target_bundles' => $element['#target_bundles'],
|
||||
'suggestion_size' => $element['#suggestion_size'],
|
||||
'suggest_only_unique' => $element['#suggest_only_unique'],
|
||||
'match' => $element['#match'],
|
||||
];
|
||||
$token = Crypt::hmacBase64(serialize($data), Settings::getHashSalt());
|
||||
$key_value_storage = \Drupal::keyValue('synonyms_entity_autocomplete');
|
||||
|
||||
$key_value_storage->setIfNotExists($token, $data);
|
||||
|
||||
$element['#autocomplete_route_name'] = 'synonyms.entity_autocomplete';
|
||||
$element['#autocomplete_route_parameters'] = [
|
||||
'target_type' => $element['#target_type'],
|
||||
'token' => $token,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for synonyms_entity_autocomplete elements.
|
||||
*/
|
||||
public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$tokens = Tags::explode($form_state->getValue($element['#parents']));
|
||||
$value = [];
|
||||
|
||||
$autocomplete_service = \Drupal::getContainer()->get('synonyms.behavior.autocomplete');
|
||||
foreach ($tokens as $token) {
|
||||
$entity_id = self::extractEntityIdFromAutocompleteInput($token);
|
||||
if (!$entity_id) {
|
||||
$lookup = $autocomplete_service->autocompleteLookup($token, $element['#autocomplete_route_parameters']['token']);
|
||||
$lookup = array_shift($lookup);
|
||||
if ($lookup) {
|
||||
$entity_id = $lookup['entity_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity_id) {
|
||||
$value[] = ['target_id' => $entity_id];
|
||||
}
|
||||
}
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the entity ID from the autocompletion result.
|
||||
*
|
||||
* @param string $input
|
||||
* The input coming from the autocompletion result.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function extractEntityIdFromAutocompleteInput($input) {
|
||||
$match = NULL;
|
||||
|
||||
// Take "label (entity id)', match the ID from parenthesis when it's a
|
||||
// number.
|
||||
if (preg_match("/.+\s\((\d+)\)/", $input, $matches)) {
|
||||
$match = $matches[1];
|
||||
}
|
||||
return $match;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Select;
|
||||
|
||||
/**
|
||||
* Form element for synonyms-friendly entity select.
|
||||
*
|
||||
* @FormElement("synonyms_entity_select")
|
||||
*/
|
||||
class SynonymsEntitySelect extends Select {
|
||||
|
||||
/**
|
||||
* Delimiter to use when separating entity ID and its synonym.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DELIMITER = ':';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$info = parent::getInfo();
|
||||
|
||||
// Target entity type.
|
||||
$info['#target_type'] = NULL;
|
||||
|
||||
// Which SelectionInterface plugin to use for retrieving referenceable
|
||||
// entities.
|
||||
$info['#handler'] = 'default';
|
||||
|
||||
// Array of settings for the selection handler.
|
||||
$info['#handler_settings'] = [];
|
||||
|
||||
// Put actual value under this key in the associative array.
|
||||
$info['#key_column'] = 'target_id';
|
||||
|
||||
array_unshift($info['#process'], [get_class($this), 'elementSynonymsEntitySelect']);
|
||||
$info['#element_validate'][] = [get_class($this), 'validateEntitySelect'];
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$return = parent::valueCallback($element, $input, $form_state);
|
||||
|
||||
if (is_null($return) && isset($element['#default_value'])) {
|
||||
$return = $element['#default_value'];
|
||||
}
|
||||
|
||||
// Force default value (entity ID(-s)) to be strings. Otherwise we are
|
||||
// hitting the situation when all synonyms are highlighted as selected.
|
||||
// This code snippet explains the problem:
|
||||
// $a = [25];
|
||||
// $k = '25:25';
|
||||
// in_array($k, $a); // Yields TRUE, because PHP seems to compare int to int
|
||||
// and not string-wise.
|
||||
if (is_array($return)) {
|
||||
$return = array_map(function($item) {
|
||||
return (string) $item;
|
||||
}, $return);
|
||||
}
|
||||
elseif (!is_null($return)) {
|
||||
$return = (string) $return;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element process callback for 'synonyms_entity_select' type.
|
||||
*/
|
||||
public static function elementSynonymsEntitySelect(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$options = [];
|
||||
|
||||
$selection = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance([
|
||||
'target_type' => $element['#target_type'],
|
||||
'handler' => $element['#handler'],
|
||||
'handler_settings' => $element['#handler_settings'],
|
||||
'entity' => NULL,
|
||||
]);
|
||||
|
||||
$bundle_info = \Drupal::getContainer()->get('entity_type.bundle.info')->getBundleInfo($element['#target_type']);
|
||||
|
||||
$referenceable_entities = $selection->getReferenceableEntities();
|
||||
$entities = [];
|
||||
|
||||
foreach ($referenceable_entities as $bundle_entity_ids) {
|
||||
$entities = array_merge($entities, array_keys($bundle_entity_ids));
|
||||
}
|
||||
|
||||
if (!empty($entities)) {
|
||||
$entities = \Drupal::entityTypeManager()->getStorage($element['#target_type'])->loadMultiple($entities);
|
||||
}
|
||||
|
||||
foreach ($referenceable_entities as $bundle => $entity_ids) {
|
||||
$key = (string) $bundle_info[$bundle]['label'];
|
||||
$options[$key] = [];
|
||||
|
||||
$synonyms = \Drupal::getContainer()->get('synonyms.behavior.select')->getSynonymsMultiple(array_intersect_key($entities, $entity_ids));
|
||||
|
||||
foreach ($entity_ids as $entity_id => $label) {
|
||||
$options[$key][$entity_id] = $label;
|
||||
|
||||
foreach ($synonyms[$entity_id] as $synonym) {
|
||||
$options[$key][$entity_id . self::DELIMITER . $synonym['synonym']] = $synonym['wording'];
|
||||
}
|
||||
}
|
||||
|
||||
asort($options[$key]);
|
||||
}
|
||||
|
||||
if (count($options) == 1) {
|
||||
// Strip away the bundle optgroup if it's the only bundle.
|
||||
$options = reset($options);
|
||||
}
|
||||
|
||||
$element['#options'] = $options;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for synonyms_entity_select elements.
|
||||
*/
|
||||
public static function validateEntitySelect(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$value = $form_state->getValue($element['#parents']);
|
||||
if (!isset($element['#multiple']) || !$element['#multiple']) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
$unique = [];
|
||||
foreach ($value as $v) {
|
||||
if ($v !== '') {
|
||||
if (!is_numeric($v)) {
|
||||
$v = explode(self::DELIMITER, $v, 2)[0];
|
||||
}
|
||||
$unique[$v] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$items = [];
|
||||
foreach ($unique as $v) {
|
||||
$items[] = [
|
||||
$element['#key_column'] => $v,
|
||||
];
|
||||
}
|
||||
|
||||
if (!isset($element['#multiple']) || !$element['#multiple']) {
|
||||
$items = reset($items);
|
||||
}
|
||||
|
||||
$form_state->setValueForElement($element, $items);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Entity;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymProviderPluginCollection;
|
||||
use Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
/**
|
||||
* Synonym configuration entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "synonym",
|
||||
* label = @Translation("Synonym configuration"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\Core\Config\Entity\ConfigEntityListBuilder",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\synonyms\Form\SynonymForm",
|
||||
* "edit" = "Drupal\synonyms\Form\SynonymForm",
|
||||
* "delete" = "Drupal\synonyms\Form\SynonymDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* config_prefix = "synonym",
|
||||
* admin_permission = "administer synonyms",
|
||||
* entity_keys = {
|
||||
* "id" = "id"
|
||||
* },
|
||||
* links = {
|
||||
* "edit-form" = "/admin/structure/synonyms/{synonym}",
|
||||
* "delete-form" = "/admin/structure/synonyms/{synonym}/delete"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Synonym extends ConfigEntityBase implements SynonymInterface {
|
||||
|
||||
/**
|
||||
* Plugin ID that corresponds to this config entry.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $provider_plugin;
|
||||
|
||||
/**
|
||||
* Base plugin ID that corresponds to this config entry.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $base_provider_plugin;
|
||||
|
||||
/**
|
||||
* Plugin configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provider_configuration = [];
|
||||
|
||||
/**
|
||||
* Controlled behavior service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $behavior;
|
||||
|
||||
/**
|
||||
* Behavior configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $behavior_configuration = [];
|
||||
|
||||
/**
|
||||
* The plugin collection that stores synonym provider plugins.
|
||||
*
|
||||
* @var \Drupal\synonyms\SynonymProviderPluginCollection
|
||||
*/
|
||||
protected $pluginCollection;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
return $this->getProviderPluginInstance()->getPluginDefinition()['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderPluginInstance() {
|
||||
return $this->getPluginCollection()->get($this->getProviderPlugin());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderPlugin() {
|
||||
return $this->provider_plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProviderPlugin($plugin) {
|
||||
$this->provider_plugin = $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderConfiguration() {
|
||||
if ($this->getProviderPluginInstance() instanceof ConfigurablePluginInterface) {
|
||||
return $this->getPluginCollection()->get($this->getProviderPlugin())->getConfiguration();
|
||||
}
|
||||
return $this->provider_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProviderConfiguration(array $provider_configuration) {
|
||||
if ($this->getProviderPluginInstance() instanceof ConfigurablePluginInterface) {
|
||||
$this->getProviderPluginInstance()->setConfiguration($provider_configuration);
|
||||
}
|
||||
$this->provider_configuration = $provider_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBehaviorConfiguration() {
|
||||
return $this->behavior_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setBehaviorConfiguration(array $behavior_configuration) {
|
||||
$this->behavior_configuration = $behavior_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin collections used by this entity.
|
||||
*
|
||||
* @return SynonymProviderPluginCollection[]
|
||||
* An array of plugin collections, keyed by the property name they use to
|
||||
* store their configuration.
|
||||
*/
|
||||
public function getPluginCollections() {
|
||||
return ['provider_configuration' => $this->getPluginCollection()];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postLoad(EntityStorageInterface $storage, array &$entities) {
|
||||
parent::postLoad($storage, $entities);
|
||||
foreach ($entities as $entity) {
|
||||
$entity->addCacheTags([self::cacheTagConstruct($entity->get('behavior'), $entity->getProviderPluginInstance()->getPluginDefinition()['controlled_entity_type'], $entity->getProviderPluginInstance()->getPluginDefinition()['controlled_bundle'])]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
|
||||
// Update the "static" properties. We keep them only to be able to leverage
|
||||
// Schema API through them.
|
||||
$this->base_provider_plugin = $this->getProviderPluginInstance()->getBaseId();
|
||||
$this->behavior = $this->getProviderPluginInstance()->getBehaviorService();
|
||||
|
||||
// Make sure we have appropriate cache tags in this entity. If it was just
|
||||
// created and runs its first save it might not have it set up yet.
|
||||
$this->addCacheTags([self::cacheTagConstruct($this->behavior, $this->getProviderPluginInstance()->getPluginDefinition()['controlled_entity_type'], $this->getProviderPluginInstance()->getPluginDefinition()['controlled_bundle'])]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cache tag.
|
||||
*
|
||||
* Construct a cache tag that represents synonyms config for a given behavior,
|
||||
* entity type, and bundle.
|
||||
*
|
||||
* @param string $behavior
|
||||
* Synonyms behavior whose cache tag is requested
|
||||
* @param string $entity_type
|
||||
* Entity type whose cache tag is requested
|
||||
* @param string $bundle
|
||||
* Bundle whose cache tag is requested
|
||||
*
|
||||
* @return string
|
||||
* Cache tag
|
||||
*/
|
||||
public static function cacheTagConstruct($behavior, $entity_type, $bundle) {
|
||||
return 'synonyms:' . $behavior . '.' . $entity_type . '.' . $bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the creation of entity's LazyPluginCollection.
|
||||
*
|
||||
* @return SynonymProviderPluginCollection
|
||||
* The entity's plugin collection.
|
||||
*/
|
||||
protected function getPluginCollection() {
|
||||
if (!$this->pluginCollection && $this->getProviderPlugin()) {
|
||||
$this->pluginCollection = new SynonymProviderPluginCollection(\Drupal::service('plugin.manager.synonyms_provider'), $this->getProviderPlugin(), $this->provider_configuration);
|
||||
}
|
||||
return $this->pluginCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function invalidateTagsOnSave($update) {
|
||||
parent::invalidateTagsOnSave($update);
|
||||
Cache::invalidateTags($this->cacheTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static function invalidateTagsOnDelete(EntityTypeInterface $entity_type, array $entities) {
|
||||
$cacheability_metadata = new CacheableMetadata();
|
||||
foreach ($entities as $entity) {
|
||||
$cacheability_metadata->addCacheableDependency($entity);
|
||||
}
|
||||
Cache::invalidateTags($cacheability_metadata->getCacheTags());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Delete form for synonym config entity.
|
||||
*/
|
||||
class SynonymDeleteForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete synonym configuration %name?', ['%name' => $this->entity->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.synonym.overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->delete();
|
||||
drupal_set_message($this->t('Synonym configuration %label has been deleted.', ['%label' => $this->entity->label()]));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\SubformState;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymsProviderPluginManager;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorConfigurableInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Entity form for 'synonym' config entity type.
|
||||
*/
|
||||
class SynonymForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* @var SynonymInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $entityQuery;
|
||||
|
||||
/**
|
||||
* Entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Entity type bundle info.
|
||||
*
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var SynonymsProviderPluginManager
|
||||
*/
|
||||
protected $synonymsProviderPluginManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorServices;
|
||||
|
||||
/**
|
||||
* Entity type that is being edited/added.
|
||||
*
|
||||
* @var EntityTypeInterface
|
||||
*/
|
||||
protected $controlledEntityType;
|
||||
|
||||
/**
|
||||
* Bundle that is being edited/added.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $controlledBundle;
|
||||
|
||||
/**
|
||||
* Service ID that is being edited/added.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $behaviorServiceId;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function __construct(QueryFactory $entity_query, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, SynonymsProviderPluginManager $synonyms_provider_plugin_manager, BehaviorService $behavior_services, ContainerInterface $container) {
|
||||
$this->entityQuery = $entity_query;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->synonymsProviderPluginManager = $synonyms_provider_plugin_manager;
|
||||
$this->behaviorServices = $behavior_services;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.query'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('plugin.manager.synonyms_provider'),
|
||||
$container->get('synonyms.behaviors'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function init(FormStateInterface $form_state) {
|
||||
parent::init($form_state);
|
||||
|
||||
if ($this->entity->isNew()) {
|
||||
$this->controlledEntityType = $this->getRequest()->get('synonyms_entity_type')->id();
|
||||
$this->controlledBundle = $this->getRequest()->get('bundle');
|
||||
$this->behaviorServiceId = $this->getRouteMatch()->getRawParameter('synonyms_behavior_service');
|
||||
}
|
||||
else {
|
||||
$plugin_definition = $this->entity->getProviderPluginInstance()->getPluginDefinition();
|
||||
$this->controlledEntityType = $plugin_definition['controlled_entity_type'];
|
||||
$this->controlledBundle = $plugin_definition['controlled_bundle'];
|
||||
$this->behaviorServiceId = $plugin_definition['synonyms_behavior_service'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$class = get_class($this);
|
||||
|
||||
$provider_plugin = $this->entity->getProviderPlugin();
|
||||
if ($form_state->getValue('provider_plugin')) {
|
||||
$provider_plugin = $form_state->getValue('provider_plugin');
|
||||
}
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => str_replace(':', '.', $provider_plugin),
|
||||
];
|
||||
|
||||
$options = [];
|
||||
foreach ($this->synonymsProviderPluginManager->getDefinitions() as $plugin_id => $plugin) {
|
||||
if ($plugin['controlled_entity_type'] == $this->controlledEntityType && $plugin['controlled_bundle'] == $this->controlledBundle && $plugin['synonyms_behavior_service'] == $this->behaviorServiceId) {
|
||||
$options[$plugin_id] = $plugin['label'];
|
||||
}
|
||||
}
|
||||
|
||||
$form['provider_plugin'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Synonyms provider'),
|
||||
'#description' => $this->t('Select what synonyms provider it should represent.'),
|
||||
'#required' => TRUE,
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->entity->getProviderPlugin(),
|
||||
'#ajax' => [
|
||||
'wrapper' => 'synonyms-entity-configuration-ajax-wrapper',
|
||||
'event' => 'change',
|
||||
'callback' => [$class, 'ajaxForm'],
|
||||
],
|
||||
];
|
||||
|
||||
$form['ajax_wrapper'] = [
|
||||
'#prefix' => '<div id="synonyms-entity-configuration-ajax-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
|
||||
$form['ajax_wrapper']['provider_configuration'] = [
|
||||
'#tree' => TRUE,
|
||||
'#title' => $this->t('Provider settings'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
|
||||
$form['ajax_wrapper']['behavior_configuration'] = [
|
||||
'#tree' => TRUE,
|
||||
'#title' => $this->t('Behavior settings'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
|
||||
if ($provider_plugin) {
|
||||
$provider_plugin_instance = $this->entity->getProviderPluginInstance();
|
||||
|
||||
if ($provider_plugin_instance instanceof PluginFormInterface) {
|
||||
$form['ajax_wrapper']['provider_configuration']['#type'] = 'details';
|
||||
$form['ajax_wrapper']['provider_configuration'] += $provider_plugin_instance->buildConfigurationForm($form['ajax_wrapper']['provider_configuration'], $form_state);
|
||||
}
|
||||
|
||||
$behavior_service_instance = $provider_plugin_instance->getBehaviorServiceInstance();
|
||||
if ($behavior_service_instance instanceof SynonymsBehaviorConfigurableInterface) {
|
||||
$form['ajax_wrapper']['behavior_configuration']['#type'] = 'details';
|
||||
$form['ajax_wrapper']['behavior_configuration'] += $behavior_service_instance->buildConfigurationForm($form['ajax_wrapper']['behavior_configuration'], $form_state, $this->entity->getBehaviorConfiguration(), $this->entity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
if ($this->entity->getProviderPluginInstance() instanceof PluginFormInterface) {
|
||||
$this->entity->getProviderPluginInstance()->validateConfigurationForm($form['ajax_wrapper']['provider_configuration'], $this->getSubFormState('provider_configuration', $form, $form_state));
|
||||
}
|
||||
|
||||
if ($this->entity->getProviderPluginInstance()->getBehaviorServiceInstance() instanceof SynonymsBehaviorConfigurableInterface) {
|
||||
$this->entity->getProviderPluginInstance()->getBehaviorServiceInstance()->validateConfigurationForm($form['ajax_wrapper']['behavior_configuration'], $this->getSubFormState('behavior_configuration', $form, $form_state), $this->entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
if ($this->entity->getProviderPluginInstance() instanceof PluginFormInterface) {
|
||||
$this->entity->getProviderPluginInstance()->submitConfigurationForm($form['ajax_wrapper']['provider_configuration'], $this->getSubFormState('provider_configuration', $form, $form_state));
|
||||
}
|
||||
|
||||
if ($this->entity->getProviderPluginInstance()->getBehaviorServiceInstance() instanceof SynonymsBehaviorConfigurableInterface) {
|
||||
$this->entity->setBehaviorConfiguration($this->entity->getProviderPluginInstance()->getBehaviorServiceInstance()->submitConfigurationForm($form['ajax_wrapper']['behavior_configuration'], $this->getSubFormState('behavior_configuration', $form, $form_state), $this->entity));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$status = $this->entity->save();
|
||||
|
||||
if ($status) {
|
||||
drupal_set_message($this->t('Saved the %label synonym configuration.', [
|
||||
'%label' => $this->entity->label(),
|
||||
]));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('The %label synonym configuration was not saved.', [
|
||||
'%label' => $this->entity->label(),
|
||||
]), 'error');
|
||||
}
|
||||
|
||||
$form_state->setRedirect('entity.synonym.overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether entity with such ID already exists.
|
||||
*
|
||||
* @param string $id
|
||||
* Entity ID to check
|
||||
*
|
||||
* @return bool
|
||||
* Whether entity with such ID already exists.
|
||||
*/
|
||||
public function exist($id) {
|
||||
$entity = $this->entityQuery->get('synonym')
|
||||
->condition('id', $id)
|
||||
->execute();
|
||||
return (bool) $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback.
|
||||
*/
|
||||
public static function ajaxForm(array &$form, FormStateInterface $form_state, Request $request) {
|
||||
return $form['ajax_wrapper'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Supportive method to create sub-form-states.
|
||||
*
|
||||
* @param string $element_name
|
||||
* Name of the nested form element for which to create a sub form state
|
||||
* @param array $form
|
||||
* Full form array
|
||||
* @param FormStateInterface $form_state
|
||||
* Full form state out of which to create sub form state
|
||||
*
|
||||
* @return SubformState
|
||||
* Sub form state object generated based on the input arguments
|
||||
*/
|
||||
protected function getSubFormState($element_name, array $form, FormStateInterface $form_state) {
|
||||
return SubformState::createForSubform($form['ajax_wrapper'][$element_name], $form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\ParamConverter;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Param converter service for entity types.
|
||||
*/
|
||||
class EntityTypeParamConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* EntityTypeParamConverter constructor.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
return $this->entityTypeManager->getDefinition($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
return (!empty($definition['type']) && $definition['type'] == 'synonyms_entity_type');
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\ParamConverter;
|
||||
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Param converter for synonyms behavior service.
|
||||
*/
|
||||
class SynonymsBehaviorServiceParamConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* SynonymsBehaviorServiceParamConverter constructor.
|
||||
*/
|
||||
public function __construct(BehaviorService $behavior_service) {
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
return $this->behaviorService->getBehaviorService($value)['service'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
return (!empty($definition['type']) && $definition['type'] == 'synonyms_behavior_service');
|
||||
}
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Derivative for entity_reference synonyms provider plugin.
|
||||
*/
|
||||
class EntityReferenceField extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* Entity type bundle info.
|
||||
*
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
function __construct(EntityTypeManagerInterface $entity_type_manager, BehaviorService $behavior_service, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityFieldManagerInterface $entity_field_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('synonyms.behaviors'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('entity_field.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$implemented_interfaces = new \ReflectionClass($base_plugin_definition['class']);
|
||||
$implemented_interfaces = $implemented_interfaces->getInterfaceNames();
|
||||
|
||||
foreach ($this->behaviorService->getBehaviorServices() as $service_id => $behavior) {
|
||||
$required_interfaces = $behavior['required_interfaces'];
|
||||
|
||||
$diff = array_diff($required_interfaces, $implemented_interfaces);
|
||||
if (empty($diff)) {
|
||||
// This plugin has implemented all required interfaces for this
|
||||
// behavior.
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityType) {
|
||||
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions($entity_type->id(), $bundle);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($field->getType() == 'entity_reference') {
|
||||
$derivative_name = implode('_', [
|
||||
$service_id,
|
||||
$entity_type->id(),
|
||||
$bundle,
|
||||
$field->getName(),
|
||||
]);
|
||||
|
||||
$this->derivatives[$derivative_name] = $base_plugin_definition;
|
||||
$this->derivatives[$derivative_name]['label'] = $this->t('@behavior on @field', [
|
||||
'@behavior' => $behavior['service']->getTitle(),
|
||||
'@field' => $field->getLabel(),
|
||||
]);
|
||||
$this->derivatives[$derivative_name]['synonyms_behavior_service'] = $service_id;
|
||||
$this->derivatives[$derivative_name]['controlled_entity_type'] = $entity_type->id();
|
||||
$this->derivatives[$derivative_name]['controlled_bundle'] = $bundle;
|
||||
$this->derivatives[$derivative_name]['field'] = $field->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Drupal\synonyms\SynonymsService\FieldTypeToSynonyms;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Derivative for synonyms provider plugins.
|
||||
*/
|
||||
class Field extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* Entity type bundle info.
|
||||
*
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var FieldTypeToSynonyms
|
||||
*/
|
||||
protected $fieldTypeToSynonyms;
|
||||
|
||||
function __construct(EntityTypeManagerInterface $entity_type_manager, BehaviorService $behavior_service, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityFieldManagerInterface $entity_field_manager, FieldTypeToSynonyms $field_type_to_synonyms) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->fieldTypeToSynonyms = $field_type_to_synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('synonyms.behaviors'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('synonyms.provider.field_type_to_synonyms')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$implemented_interfaces = new \ReflectionClass($base_plugin_definition['class']);
|
||||
$implemented_interfaces = $implemented_interfaces->getInterfaceNames();
|
||||
|
||||
$field_type_to_property_map = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
foreach ($this->behaviorService->getBehaviorServices() as $service_id => $behavior) {
|
||||
$required_interfaces = $behavior['required_interfaces'];
|
||||
|
||||
$diff = array_diff($required_interfaces, $implemented_interfaces);
|
||||
if (empty($diff)) {
|
||||
// This plugin has implemented all required interfaces for this
|
||||
// behavior.
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityType) {
|
||||
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
|
||||
$base_fields = $this->entityFieldManager->getBaseFieldDefinitions($entity_type->id());
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions($entity_type->id(), $bundle);
|
||||
|
||||
switch ($base_plugin_definition['id']) {
|
||||
case 'base_field':
|
||||
$fields = $base_fields;
|
||||
break;
|
||||
|
||||
case 'field':
|
||||
$fields = array_diff_key($fields, $base_fields);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($field->getName() != 'synonyms' && isset($field_type_to_property_map[$field->getType()])) {
|
||||
$derivative_name = implode('_', [
|
||||
$service_id,
|
||||
$entity_type->id(),
|
||||
$bundle,
|
||||
$field->getName(),
|
||||
]);
|
||||
|
||||
$this->derivatives[$derivative_name] = $base_plugin_definition;
|
||||
$this->derivatives[$derivative_name]['label'] = $this->t('@behavior on @field', [
|
||||
'@behavior' => $behavior['service']->getTitle(),
|
||||
'@field' => $field->getLabel(),
|
||||
]);
|
||||
$this->derivatives[$derivative_name]['synonyms_behavior_service'] = $service_id;
|
||||
$this->derivatives[$derivative_name]['controlled_entity_type'] = $entity_type->id();
|
||||
$this->derivatives[$derivative_name]['controlled_bundle'] = $bundle;
|
||||
$this->derivatives[$derivative_name]['field'] = $field->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides synonyms argument validator plugin definitions for all entity types.
|
||||
*/
|
||||
class ViewsSynonymsEntityArgumentValidator extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The base plugin ID this derivative is for.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basePluginId;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs an ViewsSynonymsEntityArgumentValidator object.
|
||||
*/
|
||||
public function __construct($base_plugin_id, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
|
||||
$this->basePluginId = $base_plugin_id;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$base_plugin_id,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$entity_types = $this->entityTypeManager->getDefinitions();
|
||||
$this->derivatives = [];
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityTypeInterface) {
|
||||
$this->derivatives[$entity_type_id] = [
|
||||
'id' => 'synonyms_entity:' . $entity_type_id,
|
||||
'provider' => 'synonyms',
|
||||
'title' => $this->t('Synonyms of @entity_type', [
|
||||
'@entity_type' => $entity_type->getLowercaseLabel(),
|
||||
]),
|
||||
'help' => $this->t('Validate @label', ['@label' => $entity_type->getLabel()]),
|
||||
'entity_type' => $entity_type_id,
|
||||
'class' => $base_plugin_definition['class'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'synonyms friendly autocomplete' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "synonyms_autocomplete",
|
||||
* label = @Translation("Synonyms-friendly autocomplete"),
|
||||
* description = @Translation("An autocomplete with entities and their synonyms."),
|
||||
* field_types = {
|
||||
* "entity_reference"
|
||||
* },
|
||||
* multiple_values = TRUE
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceSynonymsAutocomplete extends WidgetBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, BehaviorService $behavior_service) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
|
||||
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$configuration['field_definition'],
|
||||
$configuration['settings'],
|
||||
$configuration['third_party_settings'],
|
||||
$container->get('synonyms.behaviors')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return [
|
||||
'suggestion_size' => 10,
|
||||
'suggest_only_unique' => FALSE,
|
||||
'match' => 'CONTAINS',
|
||||
] + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$elements = parent::settingsForm($form, $form_state);
|
||||
|
||||
$elements['suggestion_size'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Suggestions Size'),
|
||||
'#description' => $this->t('Please, enter how many suggested entities to show in the autocomplete textfield.'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => $this->getSetting('suggestion_size'),
|
||||
];
|
||||
|
||||
$elements['suggest_only_unique'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Suggest only one entry per entity'),
|
||||
'#description' => t('If you want to include only name or a single synonym, suggesting a particular entity, while disregarding all ongoing ones, please, tick this checkbox on.'),
|
||||
'#default_value' => $this->getSetting('suggest_only_unique'),
|
||||
];
|
||||
|
||||
$elements['match'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Match operator'),
|
||||
'#description' => $this->t('Choose how to match the keyword against existing data.'),
|
||||
'#options' => $this->getMatchOperatorOptions(),
|
||||
'#default_value' => $this->getSetting('match'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = parent::settingsSummary();
|
||||
|
||||
$summary[] = $this->t('Suggestion size: @size', [
|
||||
'@size' => $this->getSetting('suggestion_size'),
|
||||
]);
|
||||
|
||||
$summary[] = $this->t('Only unique: @unique', [
|
||||
'@unique' => $this->getSetting('suggest_only_unique') ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
|
||||
$summary[] = $this->t('Match: @match', [
|
||||
'@match' => $this->getMatchOperatorOptions()[$this->getSetting('match')],
|
||||
]);
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$default_value = [];
|
||||
foreach ($items as $item) {
|
||||
if ($item->entity) {
|
||||
$default_value[] = $item->entity;
|
||||
}
|
||||
}
|
||||
$element += [
|
||||
'#type' => 'synonyms_entity_autocomplete',
|
||||
'#target_type' => $this->getFieldSetting('target_type'),
|
||||
'#target_bundles' => $this->getFieldSetting('handler_settings')['target_bundles'],
|
||||
'#suggestion_size' => $this->getSetting('suggestion_size'),
|
||||
'#suggest_only_unique' => $this->getSetting('suggest_only_unique'),
|
||||
'#match' => $this->getSetting('match'),
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the options for the match operator.
|
||||
*
|
||||
* @return array
|
||||
* List of options.
|
||||
*/
|
||||
protected function getMatchOperatorOptions() {
|
||||
return [
|
||||
'STARTS_WITH' => t('Starts with'),
|
||||
'CONTAINS' => t('Contains'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'synonyms friendly select' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "synonyms_select",
|
||||
* label = @Translation("Synonyms-friendly select"),
|
||||
* description = @Translation("A dropdown with entities and their synonyms."),
|
||||
* field_types = {
|
||||
* "entity_reference"
|
||||
* },
|
||||
* multiple_values = TRUE
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceSynonymsSelect extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$default_value = [];
|
||||
foreach ($items as $item) {
|
||||
if ($item->{$this->getKeyColumn()}) {
|
||||
$default_value[] = $item->{$this->getKeyColumn()};
|
||||
}
|
||||
}
|
||||
|
||||
$element += [
|
||||
'#type' => 'synonyms_entity_select',
|
||||
'#key_column' => $this->getKeyColumn(),
|
||||
'#target_type' => $this->getFieldSetting('target_type'),
|
||||
'#handler' => $this->fieldDefinition->getSetting('handler'),
|
||||
'#handler_settings' => $this->fieldDefinition->getSetting('handler_settings') ?: [],
|
||||
'#multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(),
|
||||
'#default_value' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple() ? $default_value : reset($default_value),
|
||||
];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of the column that is managed by this widget in the field
|
||||
* .
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyColumn() {
|
||||
return $this->fieldDefinition->getFieldStorageDefinition()->getPropertyNames()[0];
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Good starting point for a synonyms provider plugin.
|
||||
*/
|
||||
abstract class AbstractProvider extends PluginBase implements SynonymsProviderInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBehaviorService() {
|
||||
return $this->getPluginDefinition()['synonyms_behavior_service'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBehaviorServiceInstance() {
|
||||
return $this->container->get($this->getPluginDefinition()['synonyms_behavior_service']);
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetTrait;
|
||||
use Drupal\synonyms\SynonymsService\FieldTypeToSynonyms;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provide synonyms from base fields.
|
||||
*
|
||||
* @SynonymsProvider(
|
||||
* id = "base_field",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\Field"
|
||||
* )
|
||||
*/
|
||||
class BaseField extends AbstractProvider implements SynonymsGetProviderInterface, SynonymsFindProviderInterface, SynonymsFormatWordingProviderInterface {
|
||||
|
||||
use SynonymsGetTrait, SynonymsFindTrait, SynonymsFormatWordingTrait;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var FieldTypeToSynonyms
|
||||
*/
|
||||
protected $fieldTypeToSynonyms;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, FieldTypeToSynonyms $field_type_to_synonyms, EntityTypeManagerInterface $entity_type_manager, Connection $database, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $container);
|
||||
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->fieldTypeToSynonyms = $field_type_to_synonyms;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('synonyms.provider.field_type_to_synonyms'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []) {
|
||||
$map = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
$field_type = $entity->getFieldDefinition($this->getPluginDefinition()['field'])->getType();
|
||||
|
||||
$synonyms = [];
|
||||
|
||||
if (isset($map[$field_type])) {
|
||||
foreach ($entity->get($this->getPluginDefinition()['field']) as $item) {
|
||||
$synonyms[] = $item->{$map[$field_type]};
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition) {
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($this->getPluginDefinition()['controlled_entity_type']);
|
||||
$entity_keys = $entity_type_definition->getKeys();
|
||||
|
||||
$query = $this->database->select($entity_type_definition->getDataTable(), 'base');
|
||||
$query->addField('base', $entity_keys['id'], 'entity_id');
|
||||
$query->addField('base', $this->getPluginDefinition()['field'], 'synonym');
|
||||
|
||||
if ($this->getPluginDefinition()['controlled_entity_type'] != $this->getPluginDefinition()['controlled_bundle'] && isset($entity_keys['bundle']) && $entity_keys['bundle']) {
|
||||
$query->condition($entity_keys['bundle'], $this->getPluginDefinition()['controlled_bundle']);
|
||||
}
|
||||
|
||||
$this->synonymsFindProcessCondition($condition, $this->getPluginDefinition()['field'], $entity_keys['id']);
|
||||
$query->condition($condition);
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle'])[$this->getPluginDefinition()['field']];
|
||||
return [
|
||||
$field->getConfigDependencyKey() => [$field->getConfigDependencyName()],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryBase;
|
||||
use Drupal\Core\Entity\Query\Sql\Condition;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provide synonyms from entity reference field type.
|
||||
*
|
||||
* @SynonymsProvider(
|
||||
* id = "entityreference_field",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\EntityReferenceField"
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceField extends AbstractProvider implements SynonymsGetProviderInterface, SynonymsFindProviderInterface, SynonymsFormatWordingProviderInterface {
|
||||
|
||||
use SynonymsGetTrait, SynonymsFindTrait, SynonymsFormatWordingTrait;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, Connection $database, QueryFactory $entity_query_factory, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $container);
|
||||
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->database = $database;
|
||||
$this->entityQueryFactory = $entity_query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database'),
|
||||
$container->get('entity.query'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []) {
|
||||
$synonyms = [];
|
||||
|
||||
foreach ($entity->get($this->getPluginDefinition()['field']) as $item) {
|
||||
$synonyms[] = $item->entity->label();
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition) {
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($this->getPluginDefinition()['controlled_entity_type']);
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle']);
|
||||
$field = $field[$this->getPluginDefinition()['field']];
|
||||
|
||||
$query = new FieldQuery($entity_type_definition, 'AND', $this->database, QueryBase::getNamespaces($this->entityQueryFactory->get($entity_type_definition->id(), 'AND')));
|
||||
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$query->condition($entity_type_definition->getKey('bundle'), $this->getPluginDefinition()['controlled_bundle']);
|
||||
}
|
||||
|
||||
$target_entity_type_definition = $this->entityTypeManager->getDefinition($field->getSetting('target_type'));
|
||||
$label_column = $target_entity_type_definition->getKey('label');
|
||||
|
||||
// Some hacks.
|
||||
if (!$label_column && $target_entity_type_definition->id() == 'user') {
|
||||
$label_column = 'name';
|
||||
}
|
||||
|
||||
if (!$label_column) {
|
||||
// No label column is defined on target entity type, there's nothing we
|
||||
// can do.
|
||||
return [];
|
||||
}
|
||||
|
||||
$synonym_column = $field->getName() . '.entity.' . $label_column;
|
||||
$this->synonymsFindProcessCondition($condition, $synonym_column, $entity_type_definition->getKey('id'));
|
||||
|
||||
$conditions_array = $condition->conditions();
|
||||
$entity_condition = new Condition($conditions_array['#conjunction'], $query);
|
||||
|
||||
// We will insert a dummy condition for a synonym column just to have the
|
||||
// actual table.column data on the synonym column.
|
||||
$hash = md5($synonym_column);
|
||||
$query->condition($synonym_column, $hash);
|
||||
|
||||
unset($conditions_array['#conjunction']);
|
||||
foreach ($conditions_array as $v) {
|
||||
$entity_condition->condition($v['field'], $v['value'], $v['operator']);
|
||||
}
|
||||
|
||||
$query->condition($entity_condition);
|
||||
|
||||
// We need run the entity query in order to force it into building a normal
|
||||
// SQL query.
|
||||
$query->execute();
|
||||
|
||||
// Now let's get "demapped" normal SQL query that will tell us explicitly
|
||||
// where all entity properties/fields are stored among the SQL tables. Such
|
||||
// data is what we need and we do not want to demap it manually.
|
||||
$sql_query = $query->getSqlQuery();
|
||||
|
||||
// Swap the entity_id column into the alias "entity_id" as we are supposed
|
||||
// to do in this method implementation.
|
||||
$select_fields = &$sql_query->getFields();
|
||||
$select_fields = ['entity_id' => $select_fields[$entity_type_definition->getKey('id')]];
|
||||
$select_fields['entity_id']['alias'] = 'entity_id';
|
||||
|
||||
// We need some dummy extra condition to force them into re-compilation.
|
||||
$sql_query->where('1 = 1');
|
||||
$conditions_array = &$sql_query->conditions();
|
||||
foreach ($conditions_array as $k => $condition_array) {
|
||||
if (isset($condition_array['value']) && $condition_array['value'] == $hash) {
|
||||
list($table_alias, $column) = explode('.', $condition_array['field']);
|
||||
unset($conditions_array[$k]);
|
||||
|
||||
// Also unsetting the table aliased by this condition.
|
||||
$tables = &$sql_query->getTables();
|
||||
$real_table = $tables[$table_alias]['table'];
|
||||
foreach ($tables as $k2 => $v2) {
|
||||
if ($k2 != $table_alias && $real_table == $v2['table']) {
|
||||
unset($tables[$table_alias]);
|
||||
$table_alias = $k2;
|
||||
}
|
||||
}
|
||||
|
||||
$sql_query->addField($table_alias, $column, 'synonym');
|
||||
break;
|
||||
}
|
||||
}
|
||||
$result = $sql_query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle'])[$this->getPluginDefinition()['field']];
|
||||
return [
|
||||
$field->getConfigDependencyKey() => [$field->getConfigDependencyName()],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryBase;
|
||||
use Drupal\Core\Entity\Query\Sql\Condition;
|
||||
use Drupal\Core\Entity\Query\Sql\Query;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetTrait;
|
||||
use Drupal\synonyms\SynonymsService\FieldTypeToSynonyms;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provide synonyms from attached simple fields.
|
||||
*
|
||||
* @SynonymsProvider(
|
||||
* id = "field",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\Field"
|
||||
* )
|
||||
*/
|
||||
class Field extends AbstractProvider implements SynonymsGetProviderInterface, SynonymsFindProviderInterface, SynonymsFormatWordingProviderInterface, DependentPluginInterface {
|
||||
|
||||
use SynonymsGetTrait, SynonymsFindTrait, SynonymsFormatWordingTrait;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var FieldTypeToSynonyms
|
||||
*/
|
||||
protected $fieldTypeToSynonyms;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, FieldTypeToSynonyms $field_type_to_synonyms, EntityTypeManagerInterface $entity_type_manager, Connection $database, QueryFactory $entity_query_factory, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $container);
|
||||
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->fieldTypeToSynonyms = $field_type_to_synonyms;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->database = $database;
|
||||
$this->entityQueryFactory = $entity_query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('synonyms.provider.field_type_to_synonyms'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database'),
|
||||
$container->get('entity.query'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []) {
|
||||
$map = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
$field_type = $entity->getFieldDefinition($this->getPluginDefinition()['field'])->getType();
|
||||
|
||||
$synonyms = [];
|
||||
|
||||
if (isset($map[$field_type])) {
|
||||
foreach ($entity->get($this->getPluginDefinition()['field']) as $item) {
|
||||
$synonyms[] = $item->{$map[$field_type]};
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition) {
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($this->getPluginDefinition()['controlled_entity_type']);
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle']);
|
||||
$field = $field[$this->getPluginDefinition()['field']];
|
||||
$field_property = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
if (isset($field_property[$field->getType()])) {
|
||||
$field_property = $field_property[$field->getType()];
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
|
||||
$query = new FieldQuery($entity_type_definition, 'AND', $this->database, QueryBase::getNamespaces($this->entityQueryFactory->get($entity_type_definition->id(), 'AND')));
|
||||
|
||||
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$query->condition($entity_type_definition->getKey('bundle'), $this->getPluginDefinition()['controlled_bundle']);
|
||||
}
|
||||
|
||||
$synonym_column = $field->getName() . '.' . $field_property;
|
||||
$this->synonymsFindProcessCondition($condition, $synonym_column, $entity_type_definition->getKey('id'));
|
||||
|
||||
$conditions_array = $condition->conditions();
|
||||
$entity_condition = new Condition($conditions_array['#conjunction'], $query);
|
||||
|
||||
// We will insert a dummy condition for a synonym column just to have the
|
||||
// actual table.column data on the synonym column.
|
||||
$hash = md5($synonym_column);
|
||||
$query->condition($synonym_column, $hash);
|
||||
|
||||
unset($conditions_array['#conjunction']);
|
||||
foreach ($conditions_array as $v) {
|
||||
$entity_condition->condition($v['field'], $v['value'], $v['operator']);
|
||||
}
|
||||
|
||||
$query->condition($entity_condition);
|
||||
|
||||
// We need run the entity query in order to force it into building a normal
|
||||
// SQL query.
|
||||
$query->execute();
|
||||
|
||||
// Now let's get "demapped" normal SQL query that will tell us explicitly
|
||||
// where all entity properties/fields are stored among the SQL tables. Such
|
||||
// data is what we need and we do not want to demap it manually.
|
||||
$sql_query = $query->getSqlQuery();
|
||||
|
||||
// Swap the entity_id column into the alias "entity_id" as we are supposed
|
||||
// to do in this method implementation.
|
||||
$select_fields = &$sql_query->getFields();
|
||||
$select_fields = ['entity_id' => $select_fields[$entity_type_definition->getKey('id')]];
|
||||
$select_fields['entity_id']['alias'] = 'entity_id';
|
||||
|
||||
// We need some dummy extra condition to force them into re-compilation.
|
||||
$sql_query->where('1 = 1');
|
||||
$conditions_array = &$sql_query->conditions();
|
||||
foreach ($conditions_array as $k => $condition_array) {
|
||||
if (isset($condition_array['value']) && $condition_array['value'] == $hash) {
|
||||
list($table_alias, $column) = explode('.', $condition_array['field']);
|
||||
unset($conditions_array[$k]);
|
||||
|
||||
// Also unsetting the table aliased by this condition.
|
||||
$tables = &$sql_query->getTables();
|
||||
$real_table = $tables[$table_alias]['table'];
|
||||
foreach ($tables as $k2 => $v2) {
|
||||
if ($k2 != $table_alias && $real_table == $v2['table']) {
|
||||
unset($tables[$table_alias]);
|
||||
$table_alias = $k2;
|
||||
}
|
||||
}
|
||||
|
||||
$sql_query->addField($table_alias, $column, 'synonym');
|
||||
break;
|
||||
}
|
||||
}
|
||||
$result = $sql_query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle'])[$this->getPluginDefinition()['field']];
|
||||
return [
|
||||
$field->getConfigDependencyKey() => [$field->getConfigDependencyName()],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hacked implementation of Entity query.
|
||||
*/
|
||||
class FieldQuery extends Query {
|
||||
|
||||
/**
|
||||
* We need to be able to extract SQL query object.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\SelectInterface
|
||||
*/
|
||||
public function getSqlQuery() {
|
||||
return $this->sqlQuery;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin;
|
||||
|
||||
use Drupal\Core\Field\FieldItemList;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
|
||||
/**
|
||||
* Field item list of "synonyms" computed base field.
|
||||
*/
|
||||
class SynonymsFieldItemList extends FieldItemList {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue($include_computed = FALSE) {
|
||||
$synonyms = [];
|
||||
|
||||
$entity = $this->getEntity();
|
||||
|
||||
$behavior_service = \Drupal::getContainer()->get('synonyms.behaviors');
|
||||
|
||||
$services = $behavior_service->getBehaviorServicesWithInterface(SynonymsGetProviderInterface::class);
|
||||
foreach ($services as $service_id => $service) {
|
||||
foreach ($behavior_service->getSynonymConfigEntities($service_id, $entity->getEntityTypeId(), $entity->bundle()) as $synonym_config) {
|
||||
$synonyms = array_merge($synonyms, $synonym_config->getProviderPluginInstance()->getSynonyms($entity, $synonym_config->getBehaviorConfiguration()));
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($synonyms);
|
||||
}
|
||||
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\views\argument_validator;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Drupal\views\Plugin\views\argument_validator\Entity;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Synonyms-friendly entity validator.
|
||||
*
|
||||
* @ViewsArgumentValidator(
|
||||
* id = "synonyms_entity",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\ViewsSynonymsEntityArgumentValidator"
|
||||
* )
|
||||
*/
|
||||
class SynonymsEntity extends Entity {
|
||||
|
||||
protected $multipleCapable = FALSE;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $entityQuery;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, EntityTypeManagerInterface $entity_type_manager, QueryFactory $entity_query, BehaviorService $behavior_service) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager);
|
||||
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityQuery = $entity_query;
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_definition,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity.query'),
|
||||
$container->get('synonyms.behaviors')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
|
||||
$form['transform'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Transform dashes in URL to spaces.'),
|
||||
'#default_value' => $this->options['transform'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateArgument($argument) {
|
||||
if ($this->options['transform']) {
|
||||
$argument = str_replace('-', ' ', $argument);
|
||||
}
|
||||
|
||||
$entity_type = $this->entityTypeManager->getDefinition($this->definition['entity_type']);
|
||||
|
||||
if ($entity_type->hasKey('label') || $entity_type->id() == 'user') {
|
||||
$query = $this->entityQuery->get($entity_type->id());
|
||||
|
||||
// User entity type does not declare its label, while it does have one.
|
||||
$label_column = $entity_type->id() == 'user' ? 'name' : $entity_type->getKey('label');
|
||||
$query->condition($label_column, $argument, '=');
|
||||
|
||||
if ($entity_type->hasKey('bundle') && !empty($this->options['bundles'])) {
|
||||
$query->condition($entity_type->getKey('bundle'), $this->options['bundles'], 'IN');
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
if (!empty($result)) {
|
||||
$entities = $this->entityTypeManager->getStorage($entity_type->id())->loadMultiple($result);
|
||||
foreach ($entities as $entity) {
|
||||
if ($this->validateEntity($entity)) {
|
||||
$this->argument->argument = $entity->id();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We've fallen through with search by entity name, now it's time to search
|
||||
// by synonyms.
|
||||
$condition = new Condition('AND');
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, $argument, '=');
|
||||
|
||||
foreach ($this->behaviorService->getBehaviorServicesWithInterface(SynonymsFindProviderInterface::class) as $service_id => $service) {
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities($service_id, $entity_type->id(), empty($this->options['bundles']) ? NULL : $this->options['bundles']) as $synonym_config) {
|
||||
foreach ($synonym_config->getProviderPluginInstance()->synonymsFind(clone $condition) as $synonym) {
|
||||
$entity = $this->entityTypeManager->getStorage($entity_type->id())->load($synonym->entity_id);
|
||||
if ($this->validateEntity($entity)) {
|
||||
$this->argument->argument = $entity->id();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['transform'] = ['default' => FALSE];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Entity\Entity;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\filter\FilterPluginBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Filter entity by its name or one of its synonyms.
|
||||
*
|
||||
* @ViewsFilter("synonyms_entity")
|
||||
*/
|
||||
class SynonymsEntity extends FilterPluginBase {
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['widget'] = [
|
||||
'default' => 'autocomplete',
|
||||
];
|
||||
|
||||
$options['target_bundles'] = [
|
||||
'default' => NULL,
|
||||
];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
switch ($this->options['widget']) {
|
||||
case 'autocomplete':
|
||||
$this->operator = 'IN';
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
$this->operator = !$this->isExposed() || $this->options['expose']['multiple'] ? 'IN' : '=';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildExtraOptionsForm($form, $form_state);
|
||||
|
||||
$form['widget'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Widget'),
|
||||
'#options' => [
|
||||
'autocomplete' => $this->t('Autocomplete'),
|
||||
'select' => $this->t('Select'),
|
||||
],
|
||||
'#default_value' => $this->options['widget'],
|
||||
'#required' => TRUE,
|
||||
'#description' => $this->t('Choose what widget to use in order to specify entity.'),
|
||||
];
|
||||
|
||||
if ($this->entityTypeManager->getDefinition($this->definition['entity_type'])->hasKey('bundle')) {
|
||||
$options = array_map(function($item) {
|
||||
return $item['label'];
|
||||
}, $this->entityTypeBundleInfo->getBundleInfo($this->definition['entity_type']));
|
||||
|
||||
$form['target_bundles'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Bundles'),
|
||||
'#description' => $this->t('Limit the possible values down to specific subset of bundles. Leave empty to have no filter by bundles.'),
|
||||
'#default_value' => $this->options['target_bundles'],
|
||||
'#options' => $options,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitExtraOptionsForm($form, FormStateInterface $form_state) {
|
||||
parent::submitExtraOptionsForm($form, $form_state);
|
||||
|
||||
if (isset($form['target_bundles'])) {
|
||||
$target_bundles = array_values(array_filter($form_state->getValue($form['target_bundles']['#parents'])));
|
||||
if (empty($target_bundles)) {
|
||||
$target_bundles = NULL;
|
||||
}
|
||||
$form_state->setValueForElement($form['target_bundles'], $target_bundles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasExtraOptions() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateExposed(&$form, FormStateInterface $form_state) {
|
||||
parent::validateExposed($form, $form_state);
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
$target_ids = $form_state->getValue($identifier);
|
||||
switch ($this->operator) {
|
||||
case 'IN':
|
||||
$target_ids = array_map(function($item) {
|
||||
return $item['target_id'];
|
||||
}, $target_ids);
|
||||
break;
|
||||
|
||||
case '=':
|
||||
$target_ids = $target_ids['target_id'];
|
||||
break;
|
||||
}
|
||||
|
||||
$form_state->setValue($identifier, $target_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function adminSummary() {
|
||||
if ($this->isExposed()) {
|
||||
return $this->t('Exposed');
|
||||
}
|
||||
|
||||
$labels = [];
|
||||
|
||||
if (!empty($this->value)) {
|
||||
foreach ($this->getEntityStorage()->loadMultiple($this->value) as $entity) {
|
||||
$labels[] = $entity->label();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->operator . ' ' . implode(', ', $labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function valueForm(&$form, FormStateInterface $form_state) {
|
||||
if (!empty($this->value)) {
|
||||
$default_value = $this->getEntityStorage()->loadMultiple($this->value);
|
||||
}
|
||||
else {
|
||||
$default_value = [];
|
||||
}
|
||||
|
||||
switch ($this->options['widget']) {
|
||||
case 'autocomplete':
|
||||
$form['value'] = [
|
||||
'#type' => 'synonyms_entity_autocomplete',
|
||||
'#title' => $this->t('Entity'),
|
||||
'#target_type' => $this->definition['entity_type'],
|
||||
'#target_bundles' => $this->options['target_bundles'],
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
$form['value'] = [
|
||||
'#type' => 'synonyms_entity_select',
|
||||
'#title' => $this->t('Entity'),
|
||||
'#target_type' => $this->definition['entity_type'],
|
||||
'#handler_settings' => [
|
||||
'target_bundles' => $this->options['target_bundles'],
|
||||
],
|
||||
'#default_value' => array_keys($default_value),
|
||||
'#multiple' => TRUE,
|
||||
'#empty_option' => '',
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function valueSubmit($form, FormStateInterface $form_state) {
|
||||
parent::valueSubmit($form, $form_state);
|
||||
|
||||
$target_ids = array_map(function($item) {
|
||||
return $item['target_id'];
|
||||
}, $form_state->getValue($form['value']['#parents']));
|
||||
|
||||
$form_state->setValueForElement($form['value'], $target_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entity storage of the entity type this filter is set up to use.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected function getEntityStorage() {
|
||||
return $this->entityTypeManager->getStorage($this->definition['entity_type']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface;
|
||||
|
||||
/**
|
||||
* Interface of synonyms configuration entity.
|
||||
*/
|
||||
interface SynonymInterface extends ConfigEntityInterface, EntityWithPluginCollectionInterface {
|
||||
|
||||
/**
|
||||
* Get ID of the synonyms provider plugin that is set up in this entity.
|
||||
*
|
||||
* @return string
|
||||
* Plugin ID of synonyms provider that corresponds to this configuration
|
||||
* entity
|
||||
*/
|
||||
public function getProviderPlugin();
|
||||
|
||||
/**
|
||||
* Get instance of the synonyms provider plugin that is set up in this entity.
|
||||
*
|
||||
* @return SynonymsProviderInterface
|
||||
* Initiated synonyms provider instance that corresponds to this
|
||||
* configuration entity
|
||||
*/
|
||||
public function getProviderPluginInstance();
|
||||
|
||||
/**
|
||||
* Set the synonyms provider plugin to use in this entity.
|
||||
*
|
||||
* @param string $plugin
|
||||
* Synonyms provider plugin ID to set in this configuration entity
|
||||
*/
|
||||
public function setProviderPlugin($plugin);
|
||||
|
||||
/**
|
||||
* Get synonyms provider plugin configuration from this entity.
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms provider plugin configuration
|
||||
*/
|
||||
public function getProviderConfiguration();
|
||||
|
||||
/**
|
||||
* Set synonyms provider plugin configuration for this entity.
|
||||
*
|
||||
* @param array $provider_configuration
|
||||
* Array of synonyms provider plugin configuration to set
|
||||
*/
|
||||
public function setProviderConfiguration(array $provider_configuration);
|
||||
|
||||
/**
|
||||
* Get synonyms behavior configuration from this entity.
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms behavior configuration
|
||||
*/
|
||||
public function getBehaviorConfiguration();
|
||||
|
||||
/**
|
||||
* Set synonyms behavior configuration for this entity.
|
||||
*
|
||||
* @param array $behavior_configuration
|
||||
* Array of synonyms behavior configuration to set
|
||||
*/
|
||||
public function setBehaviorConfiguration(array $behavior_configuration);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms;
|
||||
|
||||
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
|
||||
|
||||
/**
|
||||
* Provides a collection of synonym provider plugins.
|
||||
*/
|
||||
class SynonymProviderPluginCollection extends DefaultSingleLazyPluginCollection {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface
|
||||
*/
|
||||
public function &get($instance_id) {
|
||||
return parent::get($instance_id);
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
|
||||
/**
|
||||
* Interface to look up entities by synonyms they have.
|
||||
*/
|
||||
interface SynonymsFindProviderInterface extends SynonymsProviderInterface {
|
||||
|
||||
/**
|
||||
* Constant which denotes placeholder of a synonym column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COLUMN_SYNONYM_PLACEHOLDER = '***SYNONYM_COLUMN***';
|
||||
|
||||
/**
|
||||
* Constant which denotes placeholder of an entity ID column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COLUMN_ENTITY_ID_PLACEHOLDER = '***ENTITY_ID***';
|
||||
|
||||
/**
|
||||
* Look up entities by their synonyms within a behavior implementation.
|
||||
*
|
||||
* You are provided with a SQL condition that you should apply to the storage
|
||||
* of synonyms within the provided behavior implementation. And then return
|
||||
* result: what entities are matched by the provided condition through what
|
||||
* synonyms.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\ConditionInterface $condition
|
||||
* Condition that defines what to search for. Apart from normal SQL
|
||||
* conditions as known in Drupal, it may contain the following placeholders:
|
||||
* - SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER: to denote
|
||||
* synonyms column which you should replace with the actual column name
|
||||
* where the synonyms data for your provider is stored in plain text.
|
||||
* - SynonymsFindProviderInterface::COLUMN_ENTITY_ID_PLACEHOLDER: to denote
|
||||
* column that holds entity ID. You are supposed to replace this
|
||||
* placeholder with actual column name that holds entity ID in your case.
|
||||
* For ease of work with these placeholders, you may use the
|
||||
* SynonymsFindTrait and then just invoke the
|
||||
* $this->synonymsFindProcessCondition() method, so you won't have to worry
|
||||
* much about it
|
||||
*
|
||||
* @return \Traversable
|
||||
* Traversable result set of found synonyms and entity IDs to which those
|
||||
* belong. Each element in the result set should be an object and should
|
||||
* have the following structure:
|
||||
* - synonym: (string) Synonym that was found and which satisfies the
|
||||
* provided condition
|
||||
* - entity_id: (int) ID of the entity to which the found synonym belongs
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition);
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
|
||||
/**
|
||||
* Supportive trait to find synonyms.
|
||||
*/
|
||||
trait SynonymsFindTrait {
|
||||
|
||||
/**
|
||||
* Supportive method to process $condition argument in synonymsFind().
|
||||
*
|
||||
* This method will swap SynonymsFindProviderInterface::COLUMN_* to real
|
||||
* column names in $condition for you, so you do not have to worry about
|
||||
* internal processing of $condition object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\ConditionInterface $condition
|
||||
* Condition to be processed
|
||||
* @param string $synonym_column
|
||||
* Actual name of the column where synonyms are kept in text
|
||||
* @param string $entity_id_column
|
||||
* Actual name of the column where entity_ids are kept
|
||||
*/
|
||||
public function synonymsFindProcessCondition(ConditionInterface $condition, $synonym_column, $entity_id_column) {
|
||||
$condition_array = &$condition->conditions();
|
||||
foreach ($condition_array as &$v) {
|
||||
if (is_array($v) && isset($v['field'])) {
|
||||
if ($v['field'] instanceof ConditionInterface) {
|
||||
// Recursively process this condition too.
|
||||
$this->synonymsFindProcessCondition($v['field'], $synonym_column, $entity_id_column);
|
||||
}
|
||||
else {
|
||||
$replace = [
|
||||
SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER => $synonym_column,
|
||||
SynonymsFindProviderInterface::COLUMN_ENTITY_ID_PLACEHOLDER => $entity_id_column,
|
||||
];
|
||||
$v['field'] = str_replace(array_keys($replace), array_values($replace), $v['field']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
|
||||
/**
|
||||
* Interface to format a synonym into some kind of wording.
|
||||
*/
|
||||
interface SynonymsFormatWordingProviderInterface {
|
||||
|
||||
/**
|
||||
* Format a synonym into wording as requested by configuration.
|
||||
*
|
||||
* @param string $synonym
|
||||
* Synonym that should be formatted
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity to which this synonym belongs
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which it all happens
|
||||
*
|
||||
* @return string
|
||||
* Formatted wording
|
||||
*/
|
||||
public function synonymFormatWording($synonym, ContentEntityInterface $entity, SynonymInterface $synonym_config);
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* Array of supported tokens in wording. Keys are the tokens whereas
|
||||
* corresponding values are explanations about what each token means
|
||||
*/
|
||||
public function formatWordingAvailableTokens();
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
|
||||
/**
|
||||
* Trait to format wording of a synonym.
|
||||
*/
|
||||
trait SynonymsFormatWordingTrait {
|
||||
|
||||
/**
|
||||
* Format a synonym into wording as requested by configuration.
|
||||
*
|
||||
* @param string $synonym
|
||||
* Synonym that should be formatted
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity to which this synonym belongs
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which it all happens
|
||||
*
|
||||
* @return string
|
||||
* Formatted wording
|
||||
*/
|
||||
public function synonymFormatWording($synonym, ContentEntityInterface $entity, SynonymInterface $synonym_config) {
|
||||
// TODO: maybe we should use tokens replacement here? But then it would mean
|
||||
// an extra dependency on the tokens module. Is it worth it? For now let's
|
||||
// use stupid str_replace() and incorporate tokens only if user base really
|
||||
// asks for it.
|
||||
$map = [
|
||||
'@synonym' => $synonym,
|
||||
'@entity_label' => $entity->label(),
|
||||
];
|
||||
return str_replace(array_keys($map), array_values($map), $synonym_config->getBehaviorConfiguration()['wording']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* Array of supported tokens in wording. Keys are the tokens whereas
|
||||
* corresponding values are explanations about what each token means
|
||||
*/
|
||||
public function formatWordingAvailableTokens() {
|
||||
return [
|
||||
'@synonym' => $this->t('actual synonym value'),
|
||||
'@entity_label' => $this->t('actual label of the entity this synonym belongs to'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
use Drupal\synonyms\SynonymsBehavior\SynonymsBehaviorInterface;
|
||||
|
||||
/**
|
||||
* Interface to extract (get) synonyms from an entity.
|
||||
*/
|
||||
interface SynonymsGetProviderInterface extends SynonymsProviderInterface {
|
||||
|
||||
/**
|
||||
* Fetch synonyms from an entity.
|
||||
*
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity whose synonyms should be fetched
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return string[]
|
||||
* Array of extracted synonyms
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []);
|
||||
|
||||
/**
|
||||
* Fetch synonyms from multiple entities at once.
|
||||
*
|
||||
* @param ContentEntityInterface[] $entities
|
||||
* Array of entities whose synonyms should be fetched. The array will be
|
||||
* keyed by entity ID and all provided entities will be of the same entity
|
||||
* type and bundle
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return array
|
||||
* Array of extracted synonyms. It must be keyed by entity ID and each sub
|
||||
* array should represent a list of synonyms that were extracted from the
|
||||
* corresponding entity
|
||||
*/
|
||||
public function getSynonymsMultiple(array $entities, array $behavior_configuration = []);
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
||||
/**
|
||||
* Trait to extract synonyms from an entity.
|
||||
*/
|
||||
trait SynonymsGetTrait {
|
||||
|
||||
/**
|
||||
* Fetch synonyms from multiple entities at once.
|
||||
*
|
||||
* @param ContentEntityInterface[] $entities
|
||||
* Array of entities whose synonyms should be fetched. They array will be
|
||||
* keyed by entity ID and all provided entities will be of the same entity
|
||||
* type and bundle
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return array
|
||||
* Array of extracted synonyms. It must be keyed by entity ID and each sub
|
||||
* array should represent a list of synonyms that were extracted from the
|
||||
* corresponding entity
|
||||
*/
|
||||
public function getSynonymsMultiple(array $entities, array $behavior_configuration = []) {
|
||||
$synonyms = [];
|
||||
|
||||
foreach ($entities as $entity_id => $entity) {
|
||||
$synonyms[$entity_id] = $this->getSynonyms($entity, $behavior_configuration);
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch synonyms from an entity.
|
||||
*
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity whose synonyms should be fetched
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return string[]
|
||||
* Array of extracted synonyms
|
||||
*/
|
||||
abstract public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []);
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
|
||||
/**
|
||||
* Most generic synonyms provider interface. All providers must implement it.
|
||||
*/
|
||||
interface SynonymsProviderInterface {
|
||||
|
||||
/**
|
||||
* Fetch behavior service which corresponds to this provider.
|
||||
*
|
||||
* @return string
|
||||
* Name of synonyms behavior service which corresponds to this provider
|
||||
*/
|
||||
public function getBehaviorService();
|
||||
|
||||
/**
|
||||
* Fetch behavior service instance which corresponds to this provider.
|
||||
*
|
||||
* @return SynonymsBehaviorInterface
|
||||
*/
|
||||
public function getBehaviorServiceInstance();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\synonyms\Annotation\SynonymsProvider;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface;
|
||||
|
||||
/**
|
||||
* Plugin manager for Synonyms provider plugin type.
|
||||
*/
|
||||
class SynonymsProviderPluginManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct(
|
||||
'Plugin/Synonyms/Provider',
|
||||
$namespaces,
|
||||
$module_handler,
|
||||
SynonymsProviderInterface::class,
|
||||
SynonymsProvider::class
|
||||
);
|
||||
$this->alterInfo('synonyms_provider_info');
|
||||
$this->setCacheBackend($cache_backend, 'synonyms_provider_info_plugins');
|
||||
}
|
||||
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
|
||||
/**
|
||||
* Synonyms behavior service for autocomplete.
|
||||
*/
|
||||
class AutocompleteService implements SynonymsBehaviorConfigurableInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var KeyValueStoreInterface
|
||||
*/
|
||||
protected $keyValue;
|
||||
|
||||
/**
|
||||
* The entity reference selection handler plugin manager.
|
||||
*
|
||||
* @var SelectionPluginManagerInterface
|
||||
*/
|
||||
protected $selectionManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
public function __construct(KeyValueFactoryInterface $key_value, SelectionPluginManagerInterface $selection_plugin_manager, BehaviorService $behavior_service, Connection $database, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) {
|
||||
$this->keyValue = $key_value->get('synonyms_entity_autocomplete');
|
||||
$this->selectionManager = $selection_plugin_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->database = $database;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $configuration, SynonymInterface $synonym_config) {
|
||||
$replacements = [
|
||||
'#theme' => 'item_list',
|
||||
'#list_type' => 'ul',
|
||||
'#items' => [],
|
||||
];
|
||||
foreach ($synonym_config->getProviderPluginInstance()->formatWordingAvailableTokens() as $token => $token_info) {
|
||||
$replacements['#items'][] = Html::escape($token) . ': ' . $token_info;
|
||||
}
|
||||
|
||||
$replacements = $this->renderer->renderRoot($replacements);
|
||||
|
||||
$form['wording'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Wording for autocomplete suggestion'),
|
||||
'#default_value' => $configuration['wording'],
|
||||
'#description' => $this->t('Specify the wording with which the autocomplete suggestion should be presented. Available replacement tokens are: @replacements', [
|
||||
'@replacements' => $replacements,
|
||||
]),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
return [
|
||||
'wording' => $form_state->getValue('wording'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Autocomplete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredInterfaces() {
|
||||
return [
|
||||
SynonymsGetProviderInterface::class,
|
||||
SynonymsFindProviderInterface::class,
|
||||
SynonymsFormatWordingProviderInterface::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute synonym-friendly lookup of entities by a given keyword.
|
||||
*
|
||||
* @param string $keyword
|
||||
* Keyword to search for
|
||||
* @param string $key_value_key
|
||||
* Key under which additional settings about the lookup are stored in
|
||||
* key-value storage
|
||||
*
|
||||
* @return array
|
||||
* Array of looked up suggestions. Each array will have the following
|
||||
* structure:
|
||||
* - entity_id: (int) ID of the entity which this entry represents
|
||||
* - entity_label: (string) Label of the entity which this entry represents
|
||||
* - wording: (string) Wording with which this entry should be shown to the
|
||||
* end user on the UI
|
||||
*/
|
||||
public function autocompleteLookup($keyword, $key_value_key) {
|
||||
$suggestions = [];
|
||||
|
||||
if ($this->keyValue->has($key_value_key)) {
|
||||
$settings = $this->keyValue->get($key_value_key);
|
||||
|
||||
$suggested_entity_ids = [];
|
||||
|
||||
$target_bundles = $settings['target_bundles'];
|
||||
$handler_settings = [];
|
||||
if (!empty($target_bundles)) {
|
||||
$handler_settings['target_bundles'] = $target_bundles;
|
||||
}
|
||||
elseif (!$this->entityTypeManager->getDefinition($settings['target_type'])->hasKey('bundle')) {
|
||||
$target_bundles = [$settings['target_type']];
|
||||
}
|
||||
|
||||
$options = [
|
||||
'target_type' => $settings['target_type'],
|
||||
'handler' => 'default',
|
||||
'handler_settings' => $handler_settings,
|
||||
];
|
||||
$handler = $this->selectionManager->getInstance($options);
|
||||
|
||||
foreach ($handler->getReferenceableEntities($keyword, $settings['match'], $settings['suggestion_size']) as $suggested_entities) {
|
||||
foreach ($suggested_entities as $entity_id => $entity_label) {
|
||||
$suggestions[] = [
|
||||
'entity_id' => $entity_id,
|
||||
'entity_label' => $entity_label,
|
||||
'wording' => $entity_label,
|
||||
];
|
||||
if ($settings['suggest_only_unique']) {
|
||||
$suggested_entity_ids[] = $entity_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($suggestions) < $settings['suggestion_size']) {
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities('synonyms.behavior.autocomplete', $settings['target_type'], $target_bundles) as $behavior_service) {
|
||||
$plugin_instance = $behavior_service->getProviderPluginInstance();
|
||||
|
||||
$condition = new Condition('AND');
|
||||
switch ($settings['match']) {
|
||||
case 'CONTAINS':
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, '%' . $this->database->escapeLike($keyword) . '%', 'LIKE');
|
||||
break;
|
||||
|
||||
case 'STARTS_WITH':
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, $this->database->escapeLike($keyword) . '%', 'LIKE');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($suggested_entity_ids)) {
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_ENTITY_ID_PLACEHOLDER, $suggested_entity_ids, 'NOT IN');
|
||||
}
|
||||
|
||||
|
||||
foreach ($plugin_instance->synonymsFind($condition) as $row) {
|
||||
if (!in_array($row->entity_id, $suggested_entity_ids)) {
|
||||
$suggestions[] = [
|
||||
'entity_id' => $row->entity_id,
|
||||
'entity_label' => NULL,
|
||||
'synonym' => $row->synonym,
|
||||
'synonym_config_entity' => $behavior_service,
|
||||
'wording' => NULL,
|
||||
];
|
||||
}
|
||||
|
||||
if ($settings['suggest_only_unique']) {
|
||||
$suggested_entity_ids[] = $row->entity_id;
|
||||
}
|
||||
|
||||
if (count($suggestions) == $settings['suggestion_size']) {
|
||||
break(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($suggestions as $suggestion) {
|
||||
if (!$suggestion['entity_label']) {
|
||||
$ids[] = $suggestion['entity_id'];
|
||||
}
|
||||
}
|
||||
$ids = array_unique($ids);
|
||||
|
||||
if (!empty($ids)) {
|
||||
$entities = $this->entityTypeManager->getStorage($settings['target_type'])
|
||||
->loadMultiple($ids);
|
||||
|
||||
foreach ($suggestions as $k => $suggestion) {
|
||||
if (!$suggestion['entity_label']) {
|
||||
$suggestions[$k]['entity_label'] = $entities[$suggestion['entity_id']]->label();
|
||||
$suggestions[$k]['wording'] = $suggestion['synonym_config_entity']->getProviderPluginInstance()->synonymFormatWording($suggestion['synonym'], $entities[$suggestion['entity_id']], $suggestion['synonym_config_entity']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
|
||||
/**
|
||||
* Synonyms behavior service for select widget.
|
||||
*/
|
||||
class SelectService implements SynonymsBehaviorConfigurableInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* SelectService constructor.
|
||||
*/
|
||||
public function __construct(BehaviorService $behavior_service, RendererInterface $renderer) {
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $configuration, SynonymInterface $synonym_config) {
|
||||
$replacements = [
|
||||
'#theme' => 'item_list',
|
||||
'#list_type' => 'ul',
|
||||
'#items' => [],
|
||||
];
|
||||
foreach ($synonym_config->getProviderPluginInstance()->formatWordingAvailableTokens() as $token => $token_info) {
|
||||
$replacements['#items'][] = Html::escape($token) . ': ' . $token_info;
|
||||
}
|
||||
|
||||
$replacements = $this->renderer->renderRoot($replacements);
|
||||
|
||||
$form['wording'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Wording for select entry'),
|
||||
'#default_value' => $configuration['wording'],
|
||||
'#description' => $this->t('Specify the wording with which the select entry should be presented. Available replacement tokens are: @replacements', [
|
||||
'@replacements' => $replacements,
|
||||
]),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
return [
|
||||
'wording' => $form_state->getValue('wording'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Select');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredInterfaces() {
|
||||
return [
|
||||
SynonymsGetProviderInterface::class,
|
||||
SynonymsFormatWordingProviderInterface::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a list of synonyms from an entity.
|
||||
*
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity from which to extract the synonyms
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms. Each sub array will have the following structure:
|
||||
* - synonym: (string) Synonym itself
|
||||
* - wording: (string) Formatted wording with which this synonym should be
|
||||
* presented to the end user
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity) {
|
||||
$synonyms = $this->getSynonymsMultiple([$entity->id() => $entity]);
|
||||
return $synonyms[$entity->id()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a list of synonyms from multiple entities.
|
||||
*
|
||||
* @param ContentEntityInterface[] $entities
|
||||
* Array of entities from which to extract the synonyms. It should be keyed
|
||||
* by entity ID and may only contain entities of the same type and bundle
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms. The returned array will be keyed by entity ID and the
|
||||
* inner array will have the following structure:
|
||||
* - synonym: (string) Synonym itself
|
||||
* - wording: (string) Formatted wording with which this synonym should be
|
||||
* presented to the end user
|
||||
*/
|
||||
public function getSynonymsMultiple(array $entities) {
|
||||
if (empty($entities)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$synonym_configs = $this->behaviorService->getSynonymConfigEntities('synonyms.behavior.select', reset($entities)->getEntityTypeId(), reset($entities)->bundle());
|
||||
|
||||
$synonyms = [];
|
||||
foreach ($entities as $entity) {
|
||||
$synonyms[$entity->id()] = [];
|
||||
}
|
||||
|
||||
foreach ($synonym_configs as $synonym_config) {
|
||||
foreach ($synonym_config->getProviderPluginInstance()->getSynonymsMultiple($entities, $synonym_config->getBehaviorConfiguration()) as $entity_id => $entity_synonyms) {
|
||||
foreach ($entity_synonyms as $entity_synonym) {
|
||||
$synonyms[$entity_id][] = [
|
||||
'synonym' => $entity_synonym,
|
||||
'wording' => $synonym_config->getProviderPluginInstance()->synonymFormatWording($entity_synonym, $entities[$entity_id], $synonym_config),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
|
||||
/**
|
||||
* Interface of a configurable synonyms behavior.
|
||||
*/
|
||||
interface SynonymsBehaviorConfigurableInterface extends SynonymsBehaviorInterface {
|
||||
|
||||
/**
|
||||
* Build configuration form.
|
||||
*
|
||||
* @param array $form
|
||||
* Form into which your configuration form will be embedded. You are
|
||||
* supposed to extend this array with additional configuration form elements
|
||||
* that your behavior needs
|
||||
* @param FormStateInterface $form_state
|
||||
* Form state object that corresponds to this form
|
||||
* @param array $configuration
|
||||
* Array of existing configuration for your behavior. Normally you would use
|
||||
* it as a source of default values for your configuration form elements
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which the form is being built
|
||||
*
|
||||
* @return array
|
||||
* Extended $form that includes the form elements required for configuration
|
||||
* of your behavior
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $configuration, SynonymInterface $synonym_config);
|
||||
|
||||
/**
|
||||
* Validate submitted values into your configuration form.
|
||||
*
|
||||
* @param array $form
|
||||
* Your configuration form as it was built in
|
||||
* static::buildConfigurationForm()
|
||||
* @param FormStateInterface $form_state
|
||||
* Form state that corresponds to this form. You should rise form validation
|
||||
* errors on this form state, should you discover any in user input
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which the form is being built
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config);
|
||||
|
||||
/**
|
||||
* Process submitted values and generate new configuration.
|
||||
*
|
||||
* @param array $form
|
||||
* Your configuration form as it was built in
|
||||
* static::buildConfigurationForm()
|
||||
* @param FormStateInterface $form_state
|
||||
* Form state that corresponds to this form
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which the form is being built
|
||||
*
|
||||
* @return array
|
||||
* Array of new behavior configuration that corresponds to the submitted
|
||||
* values in the form
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config);
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
/**
|
||||
* Interface of a synonyms behavior. All behaviors must implement it.
|
||||
*/
|
||||
interface SynonymsBehaviorInterface {
|
||||
|
||||
/**
|
||||
* Get human readable title of this behavior.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Get a list of interfaces required from synonyms provider plugins.
|
||||
*
|
||||
* Get a list of PHP interfaces a synonyms provider plugin must implement in
|
||||
* order to support this behavior.
|
||||
*
|
||||
* @return array
|
||||
* Array of PHP interfaces a synonyms provider plugin must implement in
|
||||
* order to support this behavior
|
||||
*/
|
||||
public function getRequiredInterfaces();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\synonyms\Entity\Synonym;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Collect all known synonyms behavior services.
|
||||
*
|
||||
* Collect all known synonyms behavior services during dependency injection
|
||||
* container compilation.
|
||||
*/
|
||||
class BehaviorService implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $behaviorServices;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->behaviorServices = [];
|
||||
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new discovered behavior service.
|
||||
*
|
||||
* @param SynonymsBehaviorInterface $behavior_service
|
||||
* Behavior service object that was discovered and should be added into the
|
||||
* list of known ones
|
||||
* @param string $id
|
||||
* Service ID that corresponds to this behavior service
|
||||
*/
|
||||
public function addBehaviorService(SynonymsBehaviorInterface $behavior_service, $id) {
|
||||
if (!isset($this->behaviorServices[$id])) {
|
||||
$this->behaviorServices[$id] = [
|
||||
'service' => $behavior_service,
|
||||
'required_interfaces' => [],
|
||||
];
|
||||
}
|
||||
$this->behaviorServices[$id]['required_interfaces'] = $behavior_service->getRequiredInterfaces();
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of known synonyms behavior service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBehaviorServices() {
|
||||
return $this->behaviorServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a synonyms behavior service.
|
||||
*
|
||||
* @param string $behavior_service_id
|
||||
* ID of the service to retrieve
|
||||
*
|
||||
* @return array
|
||||
* Array of information about the behavior service. The array will have the
|
||||
* following structure:
|
||||
* - service: (SynonymsBehaviorInterface) Initiated behavior service
|
||||
* - required_interfaces: (array) Array of PHP interfaces it requires
|
||||
*/
|
||||
public function getBehaviorService($behavior_service_id) {
|
||||
return isset($this->behaviorServices[$behavior_service_id]) ? $this->behaviorServices[$behavior_service_id] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of behavior services that require a given interface.
|
||||
*
|
||||
* @param string $interface
|
||||
* Full name of the interface
|
||||
*
|
||||
* @return array
|
||||
* The return structure of this method is identical of the return structure
|
||||
* of static::getBehaviorServices()
|
||||
*/
|
||||
public function getBehaviorServicesWithInterface($interface) {
|
||||
$return = [];
|
||||
|
||||
foreach ($this->getBehaviorServices() as $service_id => $service) {
|
||||
if (in_array($interface, $service['required_interfaces'])) {
|
||||
$return[$service_id] = $service;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enabled synonym providers for a requested synonyms behavior.
|
||||
*
|
||||
* @param string $synonyms_behavior
|
||||
* ID of the synonyms behavior services whose enabled providers should be
|
||||
* returned
|
||||
* @param string $entity_type
|
||||
* Entity type for which to conduct the search
|
||||
* @param string|array $bundle
|
||||
* Single bundle or an array of them for which to conduct the search. If
|
||||
* null is given, then no restrictions are applied on bundle level
|
||||
*
|
||||
* @return Synonym[]
|
||||
*/
|
||||
public function getSynonymConfigEntities($synonyms_behavior, $entity_type, $bundle) {
|
||||
$entities = [];
|
||||
|
||||
if (is_scalar($bundle) && !is_null($bundle)) {
|
||||
$bundle = [$bundle];
|
||||
}
|
||||
|
||||
foreach ($this->entityTypeManager->getStorage('synonym')->loadMultiple() as $entity) {
|
||||
$provider_instance = $entity->getProviderPluginInstance();
|
||||
$provider_definition = $provider_instance->getPluginDefinition();
|
||||
if ($provider_definition['synonyms_behavior_service'] == $synonyms_behavior && $provider_definition['controlled_entity_type'] == $entity_type && (!is_array($bundle) || in_array($provider_definition['controlled_bundle'], $bundle))) {
|
||||
$entities[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
|
||||
/**
|
||||
* Service to look up an entity by its name or synonym.
|
||||
*/
|
||||
class EntityGet {
|
||||
|
||||
/**
|
||||
* @var SynonymsFind
|
||||
*/
|
||||
protected $synonymsFindService;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
public function __construct(SynonymsFind $synonyms_find_service, EntityTypeManagerInterface $entity_type_manager, QueryFactory $query_factory) {
|
||||
$this->synonymsFindService = $synonyms_find_service;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->queryFactory = $query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try finding an entity by its name or synonym.
|
||||
*
|
||||
* @param EntityTypeInterface $entity_type
|
||||
* What entity type is being searched
|
||||
* @param string $name
|
||||
* The look up keyword (the supposed name or synonym)
|
||||
* @param string $bundle
|
||||
* Optionally limit the search within a specific bundle name of the provided
|
||||
* entity type
|
||||
*
|
||||
* @return int
|
||||
* ID of the looked up entity. If such entity was not found, then 0 is
|
||||
* returned
|
||||
*/
|
||||
public function entityGetBySynonym(EntityTypeInterface $entity_type, $name, $bundle = NULL) {
|
||||
if ($entity_type->id() == 'user' || $entity_type->hasKey('label')) {
|
||||
$label_column = $entity_type->id() == 'user' ? 'name' : $entity_type->getKey('label');
|
||||
$query = $this->queryFactory->get($entity_type->id());
|
||||
$query->condition($label_column, $name);
|
||||
if ($entity_type->hasKey('bundle') && $bundle) {
|
||||
$query->condition($entity_type->getKey('bundle'), $bundle);
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
$result = reset($result);
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
$condition = new Condition('AND');
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, $name);
|
||||
|
||||
$synonyms_found = $this->synonymsFindService->synonymsFind($condition, $entity_type, $bundle, '*');
|
||||
if (isset($synonyms_found[0]->entity_id)) {
|
||||
return $synonyms_found[0]->entity_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
|
||||
/**
|
||||
* Service to map known field types to how synonyms are encoded in them.
|
||||
*/
|
||||
class FieldTypeToSynonyms {
|
||||
|
||||
/**
|
||||
* @var ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* FieldTypeToSynonyms constructor.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map field types to the properties within them where synonyms are stored.
|
||||
*
|
||||
* @return array
|
||||
* Map where keys are simple field types and values are the properties where
|
||||
* the corresponding field type keeps the synonyms
|
||||
*/
|
||||
public function getSimpleFieldTypeToPropertyMap() {
|
||||
$map = [
|
||||
'integer' => 'value',
|
||||
'float' => 'value',
|
||||
'decimal' => 'value',
|
||||
'string' => 'value',
|
||||
'email' => 'value',
|
||||
'telephone' => 'value',
|
||||
];
|
||||
$this->moduleHandler->alter('synonyms_field_type_to_synonym', $map);
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
|
||||
/**
|
||||
* Service that allows to look up entities by their synonyms.
|
||||
*/
|
||||
class SynonymsFind {
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* SynonymsFind constructor.
|
||||
*/
|
||||
public function __construct(BehaviorService $behavior_service, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup entity IDs by the $condition.
|
||||
*
|
||||
* @param Condition $condition
|
||||
* Condition which defines what to search for
|
||||
* @param EntityTypeInterface $entity_type
|
||||
* Entity type within which to search
|
||||
* @param string|array $bundle
|
||||
* Either single bundle string or array of such within which to search. NULL
|
||||
* stands for no filtering by bundle, i.e. searching among all bundles
|
||||
* @param string|array $service_id
|
||||
* Either a single behavior service ID or an array of them within which to
|
||||
* execute the lookup. You may also use the wildcard * to search among all
|
||||
* supported behaviors
|
||||
*
|
||||
* @return array
|
||||
* Array of looked up synonyms/entities. Each element in this array will be
|
||||
* an object with the following structure:
|
||||
* - synonym: (string) synonym that was looked up
|
||||
* - entity_id: (int) ID of the entity which this synonym belongs to
|
||||
*/
|
||||
public function synonymsFind(Condition $condition, EntityTypeInterface $entity_type, $bundle = NULL, $service_id = 'synonyms.behavior.autocomplete') {
|
||||
if (!$entity_type->getKey('bundle')) {
|
||||
$bundle = $entity_type->id();
|
||||
}
|
||||
|
||||
$lookup = [];
|
||||
|
||||
if ($service_id == '*') {
|
||||
$service_id = array_keys($this->behaviorService->getBehaviorServicesWithInterface(SynonymsFindProviderInterface::class));
|
||||
}
|
||||
|
||||
if (!is_array($service_id)) {
|
||||
$service_id = [$service_id];
|
||||
}
|
||||
|
||||
if (is_null($bundle)) {
|
||||
$bundle = array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()));
|
||||
}
|
||||
|
||||
foreach ($service_id as $service_id_value) {
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities($service_id_value, $entity_type->id(), $bundle) as $synonym_config) {
|
||||
foreach ($synonym_config->getProviderPluginInstance()->synonymsFind(clone $condition) as $synonym) {
|
||||
$lookup[] = $synonym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $lookup;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user