PhpSelection.php 3.8 KB

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