EntityListBuilder.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. <?php
  2. namespace Drupal\Core\Entity;
  3. use Drupal\Core\Messenger\MessengerTrait;
  4. use Drupal\Core\Routing\RedirectDestinationTrait;
  5. use Drupal\Core\Url;
  6. use Symfony\Component\DependencyInjection\ContainerInterface;
  7. /**
  8. * Defines a generic implementation to build a listing of entities.
  9. *
  10. * @ingroup entity_api
  11. */
  12. class EntityListBuilder extends EntityHandlerBase implements EntityListBuilderInterface, EntityHandlerInterface {
  13. use MessengerTrait;
  14. use RedirectDestinationTrait;
  15. /**
  16. * The entity storage class.
  17. *
  18. * @var \Drupal\Core\Entity\EntityStorageInterface
  19. */
  20. protected $storage;
  21. /**
  22. * The entity type ID.
  23. *
  24. * @var string
  25. */
  26. protected $entityTypeId;
  27. /**
  28. * Information about the entity type.
  29. *
  30. * @var \Drupal\Core\Entity\EntityTypeInterface
  31. */
  32. protected $entityType;
  33. /**
  34. * The number of entities to list per page, or FALSE to list all entities.
  35. *
  36. * For example, set this to FALSE if the list uses client-side filters that
  37. * require all entities to be listed (like the views overview).
  38. *
  39. * @var int|false
  40. */
  41. protected $limit = 50;
  42. /**
  43. * {@inheritdoc}
  44. */
  45. public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
  46. return new static(
  47. $entity_type,
  48. $container->get('entity.manager')->getStorage($entity_type->id())
  49. );
  50. }
  51. /**
  52. * Constructs a new EntityListBuilder object.
  53. *
  54. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
  55. * The entity type definition.
  56. * @param \Drupal\Core\Entity\EntityStorageInterface $storage
  57. * The entity storage class.
  58. */
  59. public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage) {
  60. $this->entityTypeId = $entity_type->id();
  61. $this->storage = $storage;
  62. $this->entityType = $entity_type;
  63. }
  64. /**
  65. * {@inheritdoc}
  66. */
  67. public function getStorage() {
  68. return $this->storage;
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function load() {
  74. $entity_ids = $this->getEntityIds();
  75. return $this->storage->loadMultiple($entity_ids);
  76. }
  77. /**
  78. * Loads entity IDs using a pager sorted by the entity id.
  79. *
  80. * @return array
  81. * An array of entity IDs.
  82. */
  83. protected function getEntityIds() {
  84. $query = $this->getStorage()->getQuery()
  85. ->sort($this->entityType->getKey('id'));
  86. // Only add the pager if a limit is specified.
  87. if ($this->limit) {
  88. $query->pager($this->limit);
  89. }
  90. return $query->execute();
  91. }
  92. /**
  93. * Gets the label of an entity.
  94. *
  95. * @param \Drupal\Core\Entity\EntityInterface $entity
  96. * The entity being listed.
  97. *
  98. * @return string
  99. * The entity label.
  100. *
  101. * @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0
  102. * Use $entity->label() instead. This method used to escape the entity
  103. * label. The render system's autoescape is now relied upon.
  104. */
  105. protected function getLabel(EntityInterface $entity) {
  106. return $entity->label();
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function getOperations(EntityInterface $entity) {
  112. $operations = $this->getDefaultOperations($entity);
  113. $operations += $this->moduleHandler()->invokeAll('entity_operation', [$entity]);
  114. $this->moduleHandler->alter('entity_operation', $operations, $entity);
  115. uasort($operations, '\Drupal\Component\Utility\SortArray::sortByWeightElement');
  116. return $operations;
  117. }
  118. /**
  119. * Gets this list's default operations.
  120. *
  121. * @param \Drupal\Core\Entity\EntityInterface $entity
  122. * The entity the operations are for.
  123. *
  124. * @return array
  125. * The array structure is identical to the return value of
  126. * self::getOperations().
  127. */
  128. protected function getDefaultOperations(EntityInterface $entity) {
  129. $operations = [];
  130. if ($entity->access('update') && $entity->hasLinkTemplate('edit-form')) {
  131. $operations['edit'] = [
  132. 'title' => $this->t('Edit'),
  133. 'weight' => 10,
  134. 'url' => $this->ensureDestination($entity->toUrl('edit-form')),
  135. ];
  136. }
  137. if ($entity->access('delete') && $entity->hasLinkTemplate('delete-form')) {
  138. $operations['delete'] = [
  139. 'title' => $this->t('Delete'),
  140. 'weight' => 100,
  141. 'url' => $this->ensureDestination($entity->toUrl('delete-form')),
  142. ];
  143. }
  144. return $operations;
  145. }
  146. /**
  147. * Builds the header row for the entity listing.
  148. *
  149. * @return array
  150. * A render array structure of header strings.
  151. *
  152. * @see \Drupal\Core\Entity\EntityListBuilder::render()
  153. */
  154. public function buildHeader() {
  155. $row['operations'] = $this->t('Operations');
  156. return $row;
  157. }
  158. /**
  159. * Builds a row for an entity in the entity listing.
  160. *
  161. * @param \Drupal\Core\Entity\EntityInterface $entity
  162. * The entity for this row of the list.
  163. *
  164. * @return array
  165. * A render array structure of fields for this entity.
  166. *
  167. * @see \Drupal\Core\Entity\EntityListBuilder::render()
  168. */
  169. public function buildRow(EntityInterface $entity) {
  170. $row['operations']['data'] = $this->buildOperations($entity);
  171. return $row;
  172. }
  173. /**
  174. * Builds a renderable list of operation links for the entity.
  175. *
  176. * @param \Drupal\Core\Entity\EntityInterface $entity
  177. * The entity on which the linked operations will be performed.
  178. *
  179. * @return array
  180. * A renderable array of operation links.
  181. *
  182. * @see \Drupal\Core\Entity\EntityListBuilder::buildRow()
  183. */
  184. public function buildOperations(EntityInterface $entity) {
  185. $build = [
  186. '#type' => 'operations',
  187. '#links' => $this->getOperations($entity),
  188. ];
  189. return $build;
  190. }
  191. /**
  192. * {@inheritdoc}
  193. *
  194. * Builds the entity listing as renderable array for table.html.twig.
  195. *
  196. * @todo Add a link to add a new item to the #empty text.
  197. */
  198. public function render() {
  199. $build['table'] = [
  200. '#type' => 'table',
  201. '#header' => $this->buildHeader(),
  202. '#title' => $this->getTitle(),
  203. '#rows' => [],
  204. '#empty' => $this->t('There are no @label yet.', ['@label' => $this->entityType->getPluralLabel()]),
  205. '#cache' => [
  206. 'contexts' => $this->entityType->getListCacheContexts(),
  207. 'tags' => $this->entityType->getListCacheTags(),
  208. ],
  209. ];
  210. foreach ($this->load() as $entity) {
  211. if ($row = $this->buildRow($entity)) {
  212. $build['table']['#rows'][$entity->id()] = $row;
  213. }
  214. }
  215. // Only add the pager if a limit is specified.
  216. if ($this->limit) {
  217. $build['pager'] = [
  218. '#type' => 'pager',
  219. ];
  220. }
  221. return $build;
  222. }
  223. /**
  224. * Gets the title of the page.
  225. */
  226. protected function getTitle() {
  227. return;
  228. }
  229. /**
  230. * Ensures that a destination is present on the given URL.
  231. *
  232. * @param \Drupal\Core\Url $url
  233. * The URL object to which the destination should be added.
  234. *
  235. * @return \Drupal\Core\Url
  236. * The updated URL object.
  237. */
  238. protected function ensureDestination(Url $url) {
  239. return $url->mergeOptions(['query' => $this->getRedirectDestination()->getAsArray()]);
  240. }
  241. }