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