Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
TableBuilder
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 20
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setId
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setCallbacks
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setCaption
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setHideCaption
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setColumns
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setData
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setUseRowHeaders
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setShowVerticalBorders
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setSort
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setPaginate
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setTotalRows
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setPaginationPosition
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setAttributes
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 setPager
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setFooter
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setHeaderContent
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setCurrentSortColumn
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setCurrentSortDirection
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 build
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * TableBuilder.php
4 *
5 * This file is part of the Codex design system, the official design system
6 * for Wikimedia projects. It provides the `Table` class, a builder for constructing
7 * table components using the Codex design system.
8 *
9 * Tables are used to arrange data in rows and columns, facilitating the comparison,
10 * analysis, and management of information.
11 *
12 * @category Builder
13 * @package  Codex\Builder
14 * @since    0.1.0
15 * @author   Doğu Abaris <abaris@null.net>
16 * @license  https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
17 * @link     https://doc.wikimedia.org/codex/main/ Codex Documentation
18 */
19
20namespace Wikimedia\Codex\Builder;
21
22use Wikimedia\Codex\Component\Pager;
23use Wikimedia\Codex\Component\Table;
24use Wikimedia\Codex\Contract\IWebRequestCallbacks;
25use Wikimedia\Codex\Renderer\TableRenderer;
26
27/**
28 * TableBuilder
29 *
30 * This class implements the builder pattern to construct instances of Table.
31 * It provides a fluent interface for setting various properties and building the
32 * final immutable object with predefined configurations and immutability.
33 *
34 * @category Builder
35 * @package  Codex\Builder
36 * @since    0.1.0
37 * @author   Doğu Abaris <abaris@null.net>
38 * @license  https://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
39 * @link     https://doc.wikimedia.org/codex/main/ Codex Documentation
40 */
41class TableBuilder {
42
43    /**
44     * Sort direction for ascending order.
45     */
46    public const SORT_ASCENDING = 'asc';
47
48    /**
49     * Sort direction for descending order.
50     */
51    public const SORT_DESCENDING = 'desc';
52
53    /**
54     * The ID for the table.
55     */
56    protected string $id = '';
57
58    /**
59     * Caption for the table.
60     */
61    protected string $caption = '';
62
63    /**
64     * Flag to hide or show the caption.
65     */
66    protected bool $hideCaption = false;
67
68    /**
69     * Content for the table header.
70     */
71    protected ?string $headerContent = null;
72
73    /**
74     * Array of columns in the table.
75     */
76    protected array $columns = [];
77
78    /**
79     * Array of data rows in the table.
80     */
81    protected array $data = [];
82
83    /**
84     * Flag to use row headers.
85     */
86    protected bool $useRowHeaders = false;
87
88    /**
89     * Sorting configuration.
90     */
91    protected array $sort = [];
92
93    /**
94     * Currently sorted column.
95     */
96    protected ?string $currentSortColumn = null;
97
98    /**
99     * Current sort direction.
100     */
101    protected string $currentSortDirection = self::SORT_ASCENDING;
102
103    /**
104     * Flag to show vertical borders.
105     */
106    protected bool $showVerticalBorders = false;
107
108    /**
109     * Array of additional attributes.
110     */
111    protected array $attributes = [];
112
113    /**
114     * Flag to enable pagination.
115     */
116    protected bool $paginate = false;
117
118    /**
119     * Total number of rows for pagination.
120     */
121    protected int $totalRows = 0;
122
123    /**
124     * Position of the pagination controls ('top', 'bottom', or 'both').
125     */
126    protected string $paginationPosition = 'bottom';
127
128    /**
129     * Pager object for handling pagination.
130     */
131    protected ?Pager $pager = null;
132
133    /**
134     * Content for the table footer.
135     */
136    protected ?string $footer = null;
137
138    /**
139     * Callbacks object for handling custom actions.
140     */
141    protected IWebRequestCallbacks $callbacks;
142
143    /**
144     * The renderer instance used to render the table.
145     */
146    protected TableRenderer $renderer;
147
148    /**
149     * Constructor for the Table class.
150     *
151     * @param TableRenderer $renderer The renderer to use for rendering the table.
152     */
153    public function __construct( TableRenderer $renderer ) {
154        $this->renderer = $renderer;
155    }
156
157    /**
158     * Set the Table HTML ID attribute.
159     *
160     * @since 0.1.0
161     * @param string $id The ID for the Table element.
162     * @return $this
163     */
164    public function setId( string $id ): self {
165        $this->id = $id;
166
167        return $this;
168    }
169
170    /**
171     * Set the IWebRequestCallbacks implementation for request handling.
172     *
173     * @param IWebRequestCallbacks $callbacks The IWebRequestCallbacks implementation.
174     *
175     * @return $this
176     */
177    public function setCallbacks( IWebRequestCallbacks $callbacks ): self {
178        $this->callbacks = $callbacks;
179
180        return $this;
181    }
182
183    /**
184     * Set the caption for the table.
185     *
186     * The caption provides a description of the table's contents and purpose. It is essential for accessibility
187     * as it helps screen readers convey the context of the table to users. To visually hide the caption while
188     * keeping it accessible, use the `setHideCaption()` method.
189     *
190     * Example usage:
191     *
192     *     $table->setCaption('Article List');
193     *
194     * @since 0.1.0
195     * @param string $caption The caption text to be displayed above the table.
196     * @return $this Returns the Table instance for method chaining.
197     */
198    public function setCaption( string $caption ): self {
199        $this->caption = $caption;
200
201        return $this;
202    }
203
204    /**
205     * Set whether to hide the caption.
206     *
207     * If set to true, the caption will be visually hidden but still accessible to screen readers.
208     *
209     * @since 0.1.0
210     * @param bool $hideCaption Indicates if the caption should be visually hidden.
211     * @return $this Returns the Table instance for method chaining.
212     */
213    public function setHideCaption( bool $hideCaption ): self {
214        $this->hideCaption = $hideCaption;
215
216        return $this;
217    }
218
219    /**
220     * Set the columns for the table.
221     *
222     * Each column is defined by an associative array with attributes such as 'id', 'label', 'sortable', etc.
223     *
224     * Example usage:
225     *
226     *     $table->setColumns([
227     *         ['id' => 'title', 'label' => 'Title', 'sortable' => true],
228     *         ['id' => 'creation_date', 'label' => 'Creation Date', 'sortable' => false]
229     *     ]);
230     *
231     * @since 0.1.0
232     * @param array $columns An array of columns, where each column is an associative array containing column
233     *                       attributes.
234     * @return $this Returns the Table instance for method chaining.
235     */
236    public function setColumns( array $columns ): self {
237        $this->columns = $columns;
238
239        return $this;
240    }
241
242    /**
243     * Set the data for the table.
244     *
245     * The data array should correspond to the columns defined. Each row is an associative array where keys match
246     * column IDs.
247     *
248     * Example usage:
249     *
250     *     $table->setData([
251     *         ['title' => 'Mercury', 'creation_date' => '2024-01-01'],
252     *         ['title' => 'Venus', 'creation_date' => '2024-01-02'],
253     *     ]);
254     *
255     * @since 0.1.0
256     * @param array $data An array of data to be displayed in the table, where each row is an associative array with
257     *                    keys matching column IDs.
258     * @return $this Returns the Table instance for method chaining.
259     */
260    public function setData( array $data ): self {
261        $this->data = $data;
262
263        return $this;
264    }
265
266    /**
267     * Set whether to use row headers.
268     *
269     * If enabled, the first column of the table will be treated as row headers. This is useful for accessibility
270     * and to provide additional context for each row.
271     *
272     * @since 0.1.0
273     * @param bool $useRowHeaders Indicates if row headers should be used.
274     * @return $this Returns the Table instance for method chaining.
275     */
276    public function setUseRowHeaders( bool $useRowHeaders ): self {
277        $this->useRowHeaders = $useRowHeaders;
278
279        return $this;
280    }
281
282    /**
283     * Set whether to show vertical borders between columns.
284     *
285     * Vertical borders can help distinguish between columns, especially in tables with many columns.
286     *
287     * @since 0.1.0
288     * @param bool $showVerticalBorders Indicates if vertical borders should be displayed between columns.
289     * @return $this Returns the Table instance for method chaining.
290     */
291    public function setShowVerticalBorders( bool $showVerticalBorders ): self {
292        $this->showVerticalBorders = $showVerticalBorders;
293
294        return $this;
295    }
296
297    /**
298     * Set the sort order for the table.
299     *
300     * This method defines the initial sort order for the table. The array should contain
301     * column IDs as keys and sort directions ('asc' or 'desc') as values.
302     *
303     * Example usage:
304     *
305     *     $table->setSort([
306     *         'column1' => 'asc',
307     *         'column2' => 'desc'
308     *     ]);
309     *
310     * @since 0.1.0
311     * @param array $sort An associative array of column IDs and their respective sort directions ('asc' or 'desc').
312     * @return $this Returns the Table instance for method chaining.
313     */
314    public function setSort( array $sort ): self {
315        $this->sort = $sort;
316
317        return $this;
318    }
319
320    /**
321     * Set whether the table should be paginated.
322     *
323     * If enabled, pagination controls will be added to the table, allowing users to navigate through multiple pages of
324     * data.
325     *
326     * @since 0.1.0
327     * @param bool $paginate Indicates if the table should be paginated.
328     * @return $this Returns the Table instance for method chaining.
329     */
330    public function setPaginate( bool $paginate ): self {
331        $this->paginate = $paginate;
332
333        return $this;
334    }
335
336    /**
337     * Set the total number of rows in the table.
338     *
339     * This value is used in conjunction with pagination to calculate the total number of pages and to display the
340     * current range of rows.
341     *
342     * @since 0.1.0
343     * @param int $totalRows The total number of rows in the table.
344     * @return $this Returns the Table instance for method chaining.
345     */
346    public function setTotalRows( int $totalRows ): self {
347        $this->totalRows = $totalRows;
348
349        return $this;
350    }
351
352    /**
353     * Set the position of the pagination controls.
354     *
355     * The pagination controls can be displayed at the top, bottom, or both top and bottom of the table.
356     *
357     * @since 0.1.0
358     * @param string $paginationPosition The position of the pagination controls ('top', 'bottom', 'both').
359     * @return $this Returns the Table instance for method chaining.
360     */
361    public function setPaginationPosition( string $paginationPosition ): self {
362        $this->paginationPosition = $paginationPosition;
363
364        return $this;
365    }
366
367    /**
368     * Set additional HTML attributes for the table element.
369     *
370     * This method allows custom HTML attributes to be added to the `<table>` element, such as `id`, `class`,
371     * or `data-*` attributes. These attributes are automatically escaped to prevent XSS vulnerabilities.
372     *
373     * Example usage:
374     *
375     *     $table->setAttributes(['class' => 'custom-table-class', 'data-info' => 'additional-info']);
376     *
377     * @since 0.1.0
378     * @param array $attributes An associative array of HTML attributes to be added to the `<table>` element.
379     *
380     * @return $this Returns the Table instance for method chaining.
381     */
382    public function setAttributes( array $attributes ): self {
383        foreach ( $attributes as $key => $value ) {
384            $this->attributes[$key] = $value;
385        }
386        return $this;
387    }
388
389    /**
390     * Set the Pager instance for the table.
391     *
392     * The Pager instance provides pagination controls for the table. If set, pagination controls will be rendered
393     * according to the settings.
394     *
395     * @since 0.1.0
396     * @param Pager $pager The Pager instance.
397     * @return $this Returns the Table instance for method chaining.
398     */
399    public function setPager( Pager $pager ): self {
400        $this->pager = $pager;
401
402        return $this;
403    }
404
405    /**
406     * Set the footer content for the table.
407     *
408     * The footer is an optional section that can contain additional information or actions related to the table.
409     *
410     * @since 0.1.0
411     * @param string $footer The footer content.
412     * @return $this Returns the Table instance for method chaining.
413     */
414    public function setFooter( string $footer ): self {
415        $this->footer = $footer;
416
417        return $this;
418    }
419
420    /**
421     * Set the header content for the table.
422     *
423     * This method allows custom content to be added to the table's header, such as actions or additional text.
424     *
425     * Example usage:
426     *
427     *     $table->setHeaderContent('Custom Actions');
428     *
429     * @since 0.1.0
430     * @param string $headerContent The content to be displayed in the table header.
431     * @return $this Returns the Table instance for method chaining.
432     */
433    public function setHeaderContent( string $headerContent ): self {
434        $this->headerContent = $headerContent;
435
436        return $this;
437    }
438
439    /**
440     * Set the current sort column.
441     *
442     * This method specifies which column is currently being used for sorting the table data.
443     * The column with this ID will be marked as sorted in the table header.
444     *
445     * Example usage:
446     *
447     *     $table->setCurrentSortColumn('title');
448     *
449     * @since 0.1.0
450     * @param string $currentSortColumn The ID of the column used for sorting.
451     *
452     * @return $this Returns the Table instance for method chaining.
453     */
454    public function setCurrentSortColumn( string $currentSortColumn ): self {
455        $this->currentSortColumn = $currentSortColumn;
456
457        return $this;
458    }
459
460    /**
461     * Set the current sort direction.
462     *
463     * This method specifies the direction for sorting the table data. Acceptable values are 'asc' for ascending
464     * and 'desc' for descending. The method validates these values to ensure they are correct.
465     *
466     * Example usage:
467     *
468     *     $table->setCurrentSortDirection('asc');
469     *
470     * @since 0.1.0
471     * @param string $currentSortDirection The sort direction ('asc' or 'desc').
472     *
473     * @return $this Returns the Table instance for method chaining.
474     */
475    public function setCurrentSortDirection( string $currentSortDirection ): self {
476        if ( $currentSortDirection === self::SORT_ASCENDING || $currentSortDirection === self::SORT_DESCENDING ) {
477            $this->currentSortDirection = $currentSortDirection;
478        }
479
480        return $this;
481    }
482
483    /**
484     * Build and return the Table component object.
485     * This method constructs the immutable Table object with all the properties set via the builder.
486     *
487     * @since 0.1.0
488     * @return Table The constructed Table.
489     */
490    public function build(): Table {
491        return new Table(
492            $this->id,
493            $this->caption,
494            $this->hideCaption,
495            $this->columns,
496            $this->data,
497            $this->useRowHeaders,
498            $this->headerContent,
499            $this->sort,
500            $this->currentSortColumn,
501            $this->currentSortDirection,
502            $this->showVerticalBorders,
503            $this->attributes,
504            $this->paginate,
505            $this->totalRows,
506            $this->paginationPosition,
507            $this->pager,
508            $this->footer,
509            $this->callbacks,
510            $this->renderer
511        );
512    }
513}