5 * Contains \Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher.
8 namespace Drupal\linkit\Plugin\Linkit\Matcher;
10 use Drupal\Component\Utility\Html;
11 use Drupal\Core\Database\Connection;
12 use Drupal\Core\Entity\EntityManagerInterface;
13 use Drupal\Core\Extension\ModuleHandlerInterface;
14 use Drupal\Core\Form\FormStateInterface;
15 use Drupal\Core\Session\AccountInterface;
16 use Drupal\linkit\ConfigurableMatcherBase;
17 use Drupal\linkit\MatcherTokensTrait;
18 use Drupal\linkit\Utility\LinkitXss;
19 use Symfony\Component\DependencyInjection\ContainerInterface;
24 * label = @Translation("Entity"),
25 * deriver = "\Drupal\linkit\Plugin\Derivative\EntityMatcherDeriver"
28 class EntityMatcher extends ConfigurableMatcherBase {
30 use MatcherTokensTrait;
33 * The database connection.
35 * @var \Drupal\Core\Database\Connection
42 * @var \Drupal\Core\Entity\EntityManagerInterface
44 protected $entityManager;
47 * The module handler service.
49 * @var \Drupal\Core\Extension\ModuleHandlerInterface
51 protected $moduleHandler;
56 * @var \Drupal\Core\Session\AccountInterface
58 protected $currentUser;
61 * The target entity type id
65 protected $target_type;
70 public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
71 parent::__construct($configuration, $plugin_id, $plugin_definition);
73 if (empty($plugin_definition['target_entity'])) {
74 throw new \InvalidArgumentException("Missing required 'target_entity' property for a matcher.");
76 $this->database = $database;
77 $this->entityManager = $entity_manager;
78 $this->moduleHandler = $module_handler;
79 $this->currentUser = $current_user;
80 $this->target_type = $plugin_definition['target_entity'];
86 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
91 $container->get('database'),
92 $container->get('entity.manager'),
93 $container->get('module_handler'),
94 $container->get('current_user')
101 public function getSummary() {
102 $summery = parent::getSummary();
103 $entity_type = $this->entityManager->getDefinition($this->target_type);
105 $result_description = $this->configuration['result_description'];
106 if (!empty($result_description)) {
107 $summery[] = $this->t('Result description: @result_description', [
108 '@result_description' => $result_description
112 if ($entity_type->hasKey('bundle')) {
113 $has_bundle_filter = !empty($this->configuration['bundles']);
116 if ($has_bundle_filter) {
117 $bundles_info = $this->entityManager->getBundleInfo($this->target_type);
118 foreach ($this->configuration['bundles'] as $bundle) {
119 $bundles[] = $bundles_info[$bundle]['label'];
123 $summery[] = $this->t('Bundle filter: @bundle_filter', [
124 '@bundle_filter' => $has_bundle_filter ? implode(', ', $bundles) : t('None'),
127 $summery[] = $this->t('Group by bundle: @bundle_grouping', [
128 '@bundle_grouping' => $this->configuration['group_by_bundle'] ? $this->t('Yes') : $this->t('No'),
138 public function defaultConfiguration() {
139 return parent::defaultConfiguration() + [
140 'result_description' => '',
142 'group_by_bundle' => FALSE,
149 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
150 $entity_type = $this->entityManager->getDefinition($this->target_type);
151 $form['result_description'] = [
152 '#title' => $this->t('Result description'),
153 '#type' => 'textfield',
154 '#default_value' => $this->configuration['result_description'],
160 $this->insertTokenList($form, [$this->target_type]);
162 // Filter the possible bundles to use if the entity has bundles.
163 if ($entity_type->hasKey('bundle')) {
164 $bundle_options = [];
165 foreach ($this->entityManager->getBundleInfo($this->target_type) as $bundle_name => $bundle_info) {
166 $bundle_options[$bundle_name] = $bundle_info['label'];
170 '#type' => 'checkboxes',
171 '#title' => $this->t('Restrict to the selected bundles'),
172 '#options' => $bundle_options,
173 '#default_value' => $this->configuration['bundles'],
174 '#description' => $this->t('If none of the checkboxes is checked, allow all bundles.'),
175 '#element_validate' => [[get_class($this), 'elementValidateFilter']],
179 // Group the results by bundle.
180 $form['group_by_bundle'] = [
181 '#type' => 'checkbox',
182 '#title' => $this->t('Group by bundle'),
183 '#default_value' => $this->configuration['group_by_bundle'],
194 public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
200 public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
201 $this->configuration['result_description'] = $form_state->getValue('result_description');
202 $this->configuration['bundles'] = $form_state->getValue('bundles');
203 $this->configuration['group_by_bundle'] = $form_state->getValue('group_by_bundle');
207 * Form element validation handler; Filters the #value property of an element.
209 public static function elementValidateFilter(&$element, FormStateInterface $form_state) {
210 $element['#value'] = array_filter($element['#value']);
211 $form_state->setValueForElement($element, $element['#value']);
217 public function getMatches($string) {
218 $query = $this->buildEntityQuery($string);
219 $result = $query->execute();
221 if (empty($result)) {
226 $entities = $this->entityManager->getStorage($this->target_type)->loadMultiple($result);
228 foreach ($entities as $entity_id => $entity) {
229 // Check the access against the defined entity access handler.
230 /** @var \Drupal\Core\Access\AccessResultInterface $access */
231 $access = $entity->access('view', $this->currentUser, TRUE);
232 if (!$access->isAllowed()) {
237 'title' => $this->buildLabel($entity),
238 'description' => $this->buildDescription($entity),
239 'path' => $this->buildPath($entity),
240 'group' => $this->buildGroup($entity),
248 * Builds an EntityQuery to get entities.
251 * Text to match the label against.
253 * @return \Drupal\Core\Entity\Query\QueryInterface
254 * The EntityQuery object with the basic conditions and sorting applied to
257 protected function buildEntityQuery($match) {
258 $match = $this->database->escapeLike($match);
260 $entity_type = $this->entityManager->getDefinition($this->target_type);
261 $query = $this->entityManager->getStorage($this->target_type)->getQuery();
262 $label_key = $entity_type->getKey('label');
265 $query->condition($label_key, '%' . $match . '%', 'LIKE');
266 $query->sort($label_key, 'ASC');
270 if (!empty($this->configuration['bundles']) && $bundle_key = $entity_type->getKey('bundle')) {
271 $query->condition($bundle_key, $this->configuration['bundles'], 'IN');
274 // Add tags to let other modules alter the query.
275 $query->addTag('linkit_entity_autocomplete');
276 $query->addTag('linkit_entity_' . $this->target_type . '_autocomplete');
278 // Add access tag for the query.
279 $query->addTag('entity_access');
280 $query->addTag($this->target_type . '_access');
286 * Builds the label string used in the match array.
288 * @param \Drupal\Core\Entity\EntityInterface $entity
289 * The matched entity.
292 * The label for this entity.
294 protected function buildLabel($entity) {
295 return Html::escape($entity->label());
299 * Builds the description string used in the match array.
301 * @param \Drupal\Core\Entity\EntityInterface $entity
302 * The matched entity.
305 * The description for this entity.
307 protected function buildDescription($entity) {
308 $description = \Drupal::token()->replace($this->configuration['result_description'], [$this->target_type => $entity], []);
309 return LinkitXss::descriptionFilter($description);
313 * Builds the path string used in the match array.
315 * @param \Drupal\Core\Entity\EntityInterface $entity
316 * The matched entity.
319 * The URL for this entity.
321 protected function buildPath($entity) {
322 return $entity->toUrl()->toString();
326 * Builds the group string used in the match array.
328 * @param \Drupal\Core\Entity\EntityInterface $entity
329 * The matched entity.
332 * The match group for this entity.
334 protected function buildGroup($entity) {
335 $group = $entity->getEntityType()->getLabel();
337 // If the entities by this entity should be grouped by bundle, get the
338 // name and append it to the group.
339 if ($this->configuration['group_by_bundle']) {
340 $bundles = $this->entityManager->getBundleInfo($entity->getEntityTypeId());
341 $bundle_label = $bundles[$entity->bundle()]['label'];
342 $group .= ' - ' . $bundle_label;