ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
Data.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
38 
39 class Data extends Table implements T\Data, JSBindable
40 {
42 
43  public const VIEWCONTROL_KEY_PAGINATION = 'range';
44  public const VIEWCONTROL_KEY_ORDERING = 'order';
45  public const VIEWCONTROL_KEY_FIELDSELECTION = 'selected_optional';
46 
47  public const STORAGE_ID_PREFIX = self::class . '_';
48 
52  protected array $columns = [];
53 
57  protected array $actions_single = [];
58 
62  protected array $actions_multi = [];
63 
67  protected array $actions_std = [];
68 
72  protected ?ServerRequestInterface $request = null;
73  protected int $number_of_rows = 25;
77  protected ?array $selected_optional_column_ids = null;
78  protected ?Range $range = null;
79  protected ?Order $order = null;
80  protected ?array $filter = null;
81  protected ?array $additional_parameters = null;
82  protected ?string $id = null;
83 
87  public function __construct(
88  SignalGeneratorInterface $signal_generator,
89  protected ViewControl\Factory $view_control_factory,
90  protected ViewControlContainer\Factory $view_control_container_factory,
91  protected DataFactory $data_factory,
92  protected DataRowBuilder $data_row_builder,
93  string $title,
94  array $columns,
95  protected T\DataRetrieval $data_retrieval,
96  protected \ArrayAccess $storage
97  ) {
98  $this->checkArgListElements('columns', $columns, [Column::class]);
99  if ($columns === []) {
100  throw new \InvalidArgumentException('cannot construct a table without columns.');
101  }
102 
103  parent::__construct($title);
104  $this->multi_action_signal = $signal_generator->create();
105  $this->selection_signal = $signal_generator->create();
106  $this->async_action_signal = $signal_generator->create();
107 
108  $this->columns = $this->enumerateColumns($columns);
109  }
110 
115  private function enumerateColumns(array $columns): array
116  {
117  $ret = [];
118  $idx = 0;
119  foreach ($columns as $id => $col) {
120  $ret[$id] = $col->withIndex($idx++);
121  }
122  return $ret;
123  }
124 
125  private function initialOrder(): string
126  {
127  $visible_cols = $this->getVisibleColumns();
128  $sortable_visible_cols = array_filter(
129  $visible_cols,
130  static fn($c): bool => $c->isSortable()
131  );
132  if ($sortable_visible_cols === []) {
133  return array_key_first($visible_cols);
134  }
135  return array_key_first($sortable_visible_cols);
136  }
137 
141  public function getColumns(): array
142  {
143  return $this->columns;
144  }
145 
146  public function getDataRetrieval(): T\DataRetrieval
147  {
148  return $this->data_retrieval;
149  }
150 
154  public function withActions(array $actions): self
155  {
156  $this->checkArgListElements('actions', $actions, [T\Action\Action::class]);
157  $clone = clone $this;
158 
159  foreach ($actions as $id => $action) {
160  switch (true) {
161  case ($action instanceof T\Action\Single):
162  $clone->actions_single[$id] = $action;
163  break;
164  case ($action instanceof T\Action\Multi):
165  $clone->actions_multi[$id] = $action;
166  break;
167  case ($action instanceof T\Action\Standard):
168  $clone->actions_std[$id] = $action;
169  break;
170  }
171  }
172  return $clone;
173  }
174 
175  public function withRequest(ServerRequestInterface $request): self
176  {
177  $clone = clone $this;
178  $clone->request = $request;
179  return $clone;
180  }
181 
182  public function getRequest(): ?ServerRequestInterface
183  {
184  return $this->request;
185  }
186 
187  public function withNumberOfRows(int $number_of_rows): self
188  {
189  $clone = clone $this;
190  $clone->number_of_rows = $number_of_rows;
191  return $clone;
192  }
193 
194  public function getNumberOfRows(): int
195  {
196  return $this->number_of_rows;
197  }
198 
199  public function withOrder(?Order $order): self
200  {
201  $clone = clone $this;
202  $clone->order = $order;
203  return $clone;
204  }
205 
206  public function getOrder(): Order
207  {
208  return $this->order ?? $this->data_factory->order($this->initialOrder(), Order::ASC);
209  }
210 
211  public function withRange(?Range $range): self
212  {
213  $clone = clone $this;
214  $clone->range = $range;
215  return $clone;
216  }
217 
218  public function getRange(): Range
219  {
220  return $this->range ?? $this->data_factory->range(0, $this->number_of_rows);
221  }
222 
223  public function withFilter(?array $filter): self
224  {
225  $clone = clone $this;
226  $clone->filter = $filter;
227  return $clone;
228  }
229 
230  public function getFilter(): ?array
231  {
232  return $this->filter;
233  }
234 
235  public function withAdditionalParameters(?array $additional_parameters): self
236  {
237  $clone = clone $this;
238  $clone->additional_parameters = $additional_parameters;
239  return $clone;
240  }
241 
242  public function getAdditionalParameters(): ?array
243  {
245  }
246 
247  public function getMultiActionSignal(): Signal
248  {
250  }
251 
252  public function getSelectionSignal(): Signal
253  {
255  }
256 
257  public function getAsyncActionSignal(): Signal
258  {
260  }
261 
262  public function hasSingleActions(): bool
263  {
264  return $this->getSingleActions() !== [];
265  }
266 
267  public function hasMultiActions(): bool
268  {
269  return $this->getMultiActions() !== [];
270  }
271 
275  public function getMultiActions(): array
276  {
277  return array_merge($this->actions_multi, $this->actions_std);
278  }
279 
283  public function getSingleActions(): array
284  {
285  return array_merge($this->actions_single, $this->actions_std);
286  }
287 
291  public function getAllActions(): array
292  {
293  return array_merge($this->actions_single, $this->actions_multi, $this->actions_std);
294  }
295 
296  public function getColumnCount(): int
297  {
298  return count($this->columns);
299  }
300 
304  public function withSelectedOptionalColumns(?array $selected_optional_column_ids): self
305  {
306  $clone = clone $this;
307  $clone->selected_optional_column_ids = $selected_optional_column_ids;
308  return $clone;
309  }
310 
314  public function getSelectedOptionalColumns(): array
315  {
316  if (is_null($this->selected_optional_column_ids)) {
317  return array_keys($this->getInitiallyVisibleColumns());
318  }
320  }
321 
325  protected function getOptionalColumns(): array
326  {
327  return array_filter(
328  $this->getColumns(),
329  static fn($c): bool => $c->isOptional()
330  );
331  }
332 
336  protected function getInitiallyVisibleColumns(): array
337  {
338  return array_filter(
339  $this->getOptionalColumns(),
340  static fn($c): bool => $c->isInitiallyVisible()
341  );
342  }
343 
347  public function getVisibleColumns(): array
348  {
349  $visible_optional_columns = $this->getSelectedOptionalColumns();
350  return array_filter(
351  $this->getColumns(),
352  fn(Column $col, string $col_id): bool => !$col->isOptional() || in_array($col_id, $visible_optional_columns, true),
353  ARRAY_FILTER_USE_BOTH
354  );
355  }
356 
357  public function getRowBuilder(): DataRowBuilder
358  {
359  return $this->data_row_builder
361  ->withSingleActions($this->getSingleActions())
362  ->withVisibleColumns($this->getVisibleColumns());
363  }
364 
365  protected function getStorageData(): ?array
366  {
367  if (null !== ($storage_id = $this->getStorageId())) {
368  return $this->storage[$storage_id] ?? null;
369  }
370  return null;
371  }
372 
373  protected function setStorageData(array $data): void
374  {
375  if (null !== ($storage_id = $this->getStorageId())) {
376  $this->storage[$storage_id] = $data;
377  }
378  }
379 
380  protected function applyValuesToViewcontrols(
381  ViewControlContainer\ViewControl $view_controls,
382  ServerRequestInterface $request
383  ): ViewControlContainer\ViewControl {
384  $stored_values = new ArrayInputData($this->getStorageData() ?? []);
385  $view_controls = $view_controls
386  ->withStoredInput($stored_values)
387  ->withRequest($request);
388  $this->setStorageData($view_controls->getComponentInternalValues());
389  return $view_controls;
390  }
391 
395  public function applyViewControls(
396  array $filter_data,
397  ?array $additional_parameters = []
398  ): array {
399  $table = $this;
400  $total_count = $this->getDataRetrieval()->getTotalRowCount($filter_data, $additional_parameters);
401  $view_controls = $this->getViewControls($total_count);
402 
403  if ($request = $this->getRequest()) {
404  # This retrieves container data from the request
405  $data = $this->applyValuesToViewcontrols($view_controls, $request)->getData();
406  $range = $data[self::VIEWCONTROL_KEY_PAGINATION];
407  $range = $range instanceof Range ? $range : null;
408  if ($range instanceof Range) {
409  $range = $range
410  ->withStart($range->getStart() < $total_count ? $range->getStart() : 0)
411  ->croppedTo($total_count ?? PHP_INT_MAX);
412  }
413 
414  $table = $table
415  ->withRange($range)
416  ->withOrder($data[self::VIEWCONTROL_KEY_ORDERING] ?? null)
417  ->withSelectedOptionalColumns($data[self::VIEWCONTROL_KEY_FIELDSELECTION] ?? null);
418 
419  # This retrieves the view controls that should be displayed
420  $view_controls = $table->applyValuesToViewcontrols($table->getViewControls($total_count), $request);
421  }
422 
423  return [
424  $table->withFilter($filter_data),
425  $view_controls
426  ];
427  }
428 
429  protected function getViewControls(?int $total_count = null): ViewControlContainer\ViewControl
430  {
431  $view_controls = [
432  self::VIEWCONTROL_KEY_PAGINATION => $this->getViewControlPagination($total_count),
433  self::VIEWCONTROL_KEY_ORDERING => $this->getViewControlOrdering(),
434  self::VIEWCONTROL_KEY_FIELDSELECTION => $this->getViewControlFieldSelection(),
435  ];
436  $view_controls = array_filter($view_controls);
437  return $this->view_control_container_factory->standard($view_controls);
438  }
439 
440  protected function getViewControlPagination(?int $total_count = null): ViewControl\Pagination|ViewControl\Group
441  {
442  $smallest_option = current(Pagination::DEFAULT_LIMITS);
443  if (is_null($total_count) || $total_count >= $smallest_option) {
444  $range = $this->getRange();
445  return
446  $this->view_control_factory->pagination()
447  ->withTotalCount($total_count)
448  ->withValue([
449  Pagination::FNAME_OFFSET => $range->getStart(),
450  Pagination::FNAME_LIMIT => $range->getLength()
451  ]);
452  }
453  return $this->view_control_factory->group([
454  $this->view_control_factory->nullControl(),
455  $this->view_control_factory->nullControl()
456  ]);
457  }
458 
459  protected function getViewControlOrdering(): ?ViewControl\Sortation
460  {
461  $sortable_visible_cols = array_filter(
462  $this->getVisibleColumns(),
463  static fn($c): bool => $c->isSortable()
464  );
465 
466  if ($sortable_visible_cols === []) {
467  return null;
468  }
469 
470  $sort_options = [];
471  foreach ($sortable_visible_cols as $col_id => $col) {
472 
473  $order_asc = $this->data_factory->order($col_id, Order::ASC);
474  $order_desc = $this->data_factory->order($col_id, Order::DESC);
475 
476  $labels = $col->getOrderingLabels();
477  $sort_options[$labels[0]] = $order_asc;
478  $sort_options[$labels[1]] = $order_desc;
479  }
480  return $this->view_control_factory->sortation($sort_options);
481  }
482 
483  protected function getViewControlFieldSelection(): ?ViewControl\FieldSelection
484  {
485  $optional_cols = $this->getOptionalColumns();
486  if ($optional_cols === []) {
487  return null;
488  }
489 
490  return $this->view_control_factory
491  ->fieldSelection(array_map(
492  static fn($c): string => $c->getTitle(),
493  $optional_cols
494  ))
495  ->withValue($this->getSelectedOptionalColumns());
496  }
497 
498  public function withId(string $id): self
499  {
500  $clone = clone $this;
501  $clone->id = $id;
502  return $clone;
503  }
504 
505  protected function getStorageId(): ?string
506  {
507  if (null !== ($id = $this->getId())) {
508  return self::STORAGE_ID_PREFIX . $id;
509  }
510  return null;
511  }
512 
513  protected function getId(): ?string
514  {
515  return $this->id;
516  }
517 }
This describes a Data Table.
Definition: Data.php:31
getViewControlPagination(?int $total_count=null)
Definition: Data.php:440
withRequest(ServerRequestInterface $request)
Rendering the Table must be done using the current Request: it (the request) will be forwarded to the...
Definition: Data.php:175
trait JavaScriptBindable
Trait for components implementing JavaScriptBindable providing standard implementation.
applyValuesToViewcontrols(ViewControlContainer\ViewControl $view_controls, ServerRequestInterface $request)
Definition: Data.php:380
ServerRequestInterface $request
Definition: Data.php:72
__construct(SignalGeneratorInterface $signal_generator, protected ViewControl\Factory $view_control_factory, protected ViewControlContainer\Factory $view_control_container_factory, protected DataFactory $data_factory, protected DataRowBuilder $data_row_builder, string $title, array $columns, protected T\DataRetrieval $data_retrieval, protected \ArrayAccess $storage)
Definition: Data.php:87
withId(string $id)
The DataTable comes with a storage to keep e.g.
Definition: Data.php:498
Both the subject and the direction need to be specified when expressing an order. ...
Definition: Order.php:12
withAdditionalParameters(?array $additional_parameters)
Definition: Data.php:235
This describes a Field Selection View Control.
__construct(VocabulariesInterface $vocabularies)
withNumberOfRows(int $number_of_rows)
Number of Rows is the amount of rows shown per page.
Definition: Data.php:187
getViewControls(?int $total_count=null)
Definition: Data.php:429
This describes a Sortation View Control.
Definition: Sortation.php:28
This describes a Pagination View Control.
Definition: Pagination.php:28
applyViewControls(array $filter_data, ?array $additional_parameters=[])
Definition: Data.php:395
create(string $class='')
Create a signal, each created signal MUST have a unique ID.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Factory.php:21
withStart(int $start)
Definition: Range.php:85
const DESC
Definition: Order.php:15
withSelectedOptionalColumns(?array $selected_optional_column_ids)
Definition: Data.php:304
A simple class to express a range of whole positive numbers.
Definition: Range.php:30
A Column describes the form of presentation for a certain aspect of data, i.e.
Definition: Column.php:27