-
-
Save hussainweb/960b43f7b32ba54942ffcb85ca453996 to your computer and use it in GitHub Desktop.
| <?php | |
| namespace Drupal\axl_ks_topics\Plugin\EntityReferenceSelection; | |
| use Drupal\Component\Utility\Html; | |
| use Drupal\Core\Database\Connection; | |
| use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase; | |
| use Drupal\Core\Entity\EntityTypeManagerInterface; | |
| use Drupal\Core\Entity\Query\QueryInterface; | |
| use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| /** | |
| * Provides an entity reference selection for topics. | |
| * | |
| * @EntityReferenceSelection( | |
| * id = "axl_ks_topics", | |
| * label = @Translation("Unscheduled topics for an event"), | |
| * entity_types = {"node"}, | |
| * group = "axl_ks_topics", | |
| * weight = 0 | |
| * ) | |
| */ | |
| class TopicsSelection extends SelectionPluginBase implements ContainerFactoryPluginInterface { | |
| /** | |
| * The entity type manager. | |
| * | |
| * @var \Drupal\Core\Entity\EntityTypeManagerInterface | |
| */ | |
| protected $entityTypeManager; | |
| /** | |
| * Active database connection. | |
| * | |
| * @var \Drupal\Core\Database\Connection | |
| */ | |
| protected $database; | |
| /** | |
| * Constructs a new selection object. | |
| * | |
| * @param array $configuration | |
| * A configuration array containing information about the plugin instance. | |
| * @param string $plugin_id | |
| * The plugin_id for the plugin instance. | |
| * @param mixed $plugin_definition | |
| * The plugin implementation definition. | |
| * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager | |
| * The entity manager service. | |
| * @param \Drupal\Core\Database\Connection $database | |
| * The database connection to be used. | |
| */ | |
| public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entityTypeManager, Connection $database) { | |
| parent::__construct($configuration, $plugin_id, $plugin_definition); | |
| $this->entityTypeManager = $entityTypeManager; | |
| $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_type.manager'), | |
| $container->get('database') | |
| ); | |
| } | |
| /** | |
| * Builds an EntityQuery to get referenceable topic entities. | |
| * | |
| * @param string|null $match | |
| * Text to match the label against. | |
| * @param string $match_operator | |
| * The operation the matching should be done with. | |
| * @param int $eventId | |
| * The current enitity id. | |
| * | |
| * @return \Drupal\Core\Entity\Query\QueryInterface | |
| * The query object that can query the given entity type. | |
| */ | |
| protected function buildEntityQueryForTopics($match = NULL, $match_operator = 'CONTAINS', int $eventId = 0): QueryInterface { | |
| $query = $this->entityTypeManager->getStorage('node')->getQuery(); | |
| $query->condition('type', 'topic'); | |
| $topics = $this->getReferenceableTopics($eventId); | |
| if (!$topics) { | |
| // If there are no topics, force the query to return an empty result. | |
| $query->condition('nid', NULL, '='); | |
| return $query; | |
| } | |
| $query->condition('nid', $topics, 'IN'); | |
| if (isset($match)) { | |
| $query->condition('title', $match, $match_operator); | |
| } | |
| // Add entity-access tag. | |
| $query->addTag('node_access'); | |
| // Add the Selection handler for system_query_entity_reference_alter(). | |
| $query->addTag('entity_reference'); | |
| $query->addMetaData('entity_reference_selection_handler', $this); | |
| return $query; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { | |
| $query = $this->buildEntityQueryForTopics($match, $match_operator, $this->getParentEntityId()); | |
| if ($limit > 0) { | |
| $query->range(0, $limit); | |
| } | |
| $result = $query->execute(); | |
| if (empty($result)) { | |
| return []; | |
| } | |
| $options = []; | |
| $entities = $this->entityTypeManager->getStorage('node')->loadMultiple($result); | |
| $termStorage = $this->entityTypeManager->getStorage('taxonomy_term'); | |
| $userStorage = $this->entityTypeManager->getStorage('user'); | |
| foreach ($entities as $entity_id => $entity) { | |
| $topic_type = $termStorage->load($entity->get('field_topic_type')->target_id); | |
| $presenter = $userStorage->load($entity->get('field_topic_presenters')->target_id); | |
| $duration = $entity->get('field_topic_duration')->value; | |
| $options['topic'][$entity_id] = Html::escape(sprintf("%s - %s (%d mins, %s)", $entity->label(), $presenter->label(), $duration, $topic_type->label())); | |
| } | |
| return $options; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') { | |
| $query = $this->buildEntityQueryForTopics($match, $match_operator, $this->getParentEntityId()); | |
| return $query | |
| ->count() | |
| ->execute(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function validateReferenceableEntities(array $ids) { | |
| $result = []; | |
| if ($ids) { | |
| $query = $this->buildEntityQueryForTopics(NULL, NULL, $this->getParentEntityId()); | |
| $result = $query | |
| ->condition('nid', $ids, 'IN') | |
| ->execute(); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * Gets the list of topic referenceable entities. | |
| * | |
| * @param int $eventId | |
| * The event ID for which topics are allowed to be referenced. | |
| * | |
| * @return mixed | |
| * The query result. | |
| */ | |
| protected function getReferenceableTopics(int $eventId) { | |
| // @todo: Convert this to a dynamic query. | |
| $eventTypeTid = $this->getEventTypeTermId(); | |
| if ($eventTypeTid) { | |
| $eventTypeSql = " AND topic_type.field_topic_type_target_id = '$eventTypeTid' "; | |
| } | |
| $sql = <<<SQL | |
| SELECT topic.nid FROM {node_field_data} topic | |
| LEFT JOIN {node__field_topic_type} topic_type ON topic_type.entity_id = topic.nid | |
| LEFT JOIN {node__field_event_topics} event_topic ON event_topic.field_event_topics_target_id = topic.nid | |
| WHERE topic.type = 'topic' | |
| $eventTypeSql | |
| AND (event_topic.field_event_topics_target_id IS NULL OR event_topic.entity_id = :event_id) | |
| SQL; | |
| $result = $this->database->query($sql, [':event_id' => $eventId]); | |
| $nids = $result->fetchCol(); | |
| return $nids; | |
| } | |
| /** | |
| * Get the entity ID of the node with the ER plugin. | |
| * | |
| * @return int | |
| * Entity ID of the parent or 0 if there is no stored entity yet. | |
| */ | |
| protected function getParentEntityId(): int { | |
| $config = $this->getConfiguration(); | |
| /** @var \Drupal\Core\Entity\EntityInterface $entity */ | |
| $entity = $config['entity']; | |
| return $entity ? (int) $entity->id() : 0; | |
| } | |
| /** | |
| * Get the term ID for the event type of the currently loaded event. | |
| * | |
| * @return int|null | |
| * Event type term-id of the current entity. | |
| */ | |
| protected function getEventTypeTermId(): ?int { | |
| $config = $this->getConfiguration(); | |
| /** @var \Drupal\Core\Entity\EntityInterface $entity */ | |
| $entity = $config['entity']; | |
| if (!$entity) { | |
| return NULL; | |
| } | |
| $event_topic_type = $entity->get('field_event_topic_type')->getValue(); | |
| return $event_topic_type[0]['target_id'] ?? NULL; | |
| } | |
| } |
NIce code, But following this on my local system is not working. My requirement was to filter out unpublished nodes, but my plugin is not getting called.
@behlhrsh, since this plugin is something that's visible when configuring the entity reference field. You should first check there. Try to read the explanation at https://www.axelerant.com/resources/team-blog/writing-entity-reference-selection-plugin again if it doesn't help.
Hi @hussainweb,
My requirement was different I had to filter out the unpublished nodes on all fields using entity reference.
- Either use views option as mentioned.But, there are multiple fields & changing configs for all fields is not possible.
- Alter/override the NodeSelection plugin. SO, I created new plugin & changed the handler for all fields(entity_autocomplete) to pick my new plugin and it worked.
Thanks, for the great Article, It was of good help.
Harsh
For Ref:
Create a custom plugin, and override default Plugin.
`<?php
namespace Drupal\my_custom\Plugin\EntityReferenceSelection;
use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;
use Drupal\node\NodeInterface;
/**
- Provides an entity reference selection for topics.
- @EntityReferenceSelection(
- id = "default:my_custom",
- label = @translation("Remove unpublished node"),
- entity_types = {"node"},
- group = "default",
- weight = 10
- )
*/
class CustomNodeSelection extends NodeSelection {
/**
- {@inheritdoc}
*/
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
$query = parent::buildEntityQuery($match, $match_operator);
$query->condition('status', NodeInterface::PUBLISHED);
return $query;
}
}
`
& then,
`/**
- Implements hook_field_widget_form_alter().
*/
function my_custom_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
if ($context['items']->getFieldDefinition()->getType() == 'entity_reference' && $context['items']->getFieldDefinition()->getSettings()['handler'] == 'default:node') {
if ($element['target_id']['#type'] == 'entity_autocomplete') {
$element['target_id']['#selection_handler'] = 'default:my_custom';
}
}
}
`
I hope this will help someone. Please refer attached files.
Thanks
Harsh
Yes, some years later, but your article was really helpful. I have one key question:
Like you, I need context for my use case to work. I see you pull that from $context['entity'], but where does that value get set? Who puts that convenient entity into $context so you can read it?
Many thanks.
Thanks a lot https://gist.github.com/hussainweb/960b43f7b32ba54942ffcb85ca453996?permalink_comment_id=3502552#gistcomment-3502552 your code saved my day 👏
NIce code, But following this on my local system is not working. My requirement was to filter out unpublished nodes, but my plugin is not getting called.