-
-
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.
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.