PhpSelection.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. <?php
  2. namespace Drupal\Core\Entity\Plugin\EntityReferenceSelection;
  3. use Drupal\Component\Utility\Html;
  4. use Drupal\Component\Utility\Unicode;
  5. /**
  6. * Defines an alternative to the default Entity Reference Selection plugin.
  7. *
  8. * This selection plugin uses PHP for more advanced cases when the entity query
  9. * cannot filter properly, for example when the target entity type has no
  10. * 'label' key provided in the entity type plugin definition.
  11. *
  12. * @see \Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver
  13. */
  14. class PhpSelection extends DefaultSelection {
  15. /**
  16. * {@inheritdoc}
  17. */
  18. public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
  19. // No input, return everything from the entity query.
  20. if ($match === NULL || $match === '') {
  21. return parent::getReferenceableEntities($match, $match_operator, $limit);
  22. }
  23. // Start with the selection results returned by the entity query. Don't use
  24. // any limit because we have to apply a limit after filtering the items.
  25. $options = parent::getReferenceableEntities($match, $match_operator);
  26. // Always use a case-insensitive, escaped match. Entity labels returned by
  27. // SelectionInterface::getReferenceableEntities() are already escaped, so
  28. // the incoming $match needs to be escaped as well, making the comparison
  29. // possible.
  30. // @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface::getReferenceableEntities()
  31. if (is_string($match)) {
  32. $match = Html::escape(Unicode::strtolower($match));
  33. }
  34. elseif (is_array($match)) {
  35. array_walk($match, function (&$item) {
  36. $item = Html::escape(Unicode::strtolower($item));
  37. });
  38. }
  39. $filtered = [];
  40. $count = 0;
  41. // Filter target entities by the output of their label() method.
  42. foreach ($options as $bundle => &$items) {
  43. foreach ($items as $entity_id => $label) {
  44. if ($this->matchLabel($match, $match_operator, $label)) {
  45. $filtered[$bundle][$entity_id] = $label;
  46. $count++;
  47. if ($limit && $count >= $limit) {
  48. break 2;
  49. }
  50. }
  51. }
  52. }
  53. return $filtered;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') {
  59. $count = 0;
  60. foreach ($this->getReferenceableEntities($match, $match_operator) as &$items) {
  61. $count += count($items);
  62. }
  63. return $count;
  64. }
  65. /**
  66. * Matches an entity label to an input string.
  67. *
  68. * @param mixed $match
  69. * The value to compare. This can be any valid entity query condition value.
  70. * @param string $match_operator
  71. * The comparison operator.
  72. * @param string $label
  73. * The entity label to match against.
  74. *
  75. * @return bool
  76. * TRUE when matches, FALSE otherwise.
  77. */
  78. protected function matchLabel($match, $match_operator, $label) {
  79. // Always use a case-insensitive value.
  80. $label = Unicode::strtolower($label);
  81. switch ($match_operator) {
  82. case '=':
  83. return $label == $match;
  84. case '>':
  85. return $label > $match;
  86. case '<':
  87. return $label < $match;
  88. case '>=':
  89. return $label >= $match;
  90. case '<=':
  91. return $label <= $match;
  92. case '<>':
  93. return $label != $match;
  94. case 'IN':
  95. return array_search($label, $match) !== FALSE;
  96. case 'NOT IN':
  97. return array_search($label, $match) === FALSE;
  98. case 'STARTS_WITH':
  99. return strpos($label, $match) === 0;
  100. case 'CONTAINS':
  101. return strpos($label, $match) !== FALSE;
  102. case 'ENDS_WITH':
  103. return Unicode::substr($label, -Unicode::strlen($match)) === (string) $match;
  104. case 'IS NOT NULL':
  105. return TRUE;
  106. case 'IS NULL':
  107. return FALSE;
  108. default:
  109. // Invalid match operator.
  110. return FALSE;
  111. }
  112. }
  113. }