initOrder($options); $this->initPagination($options); $data = $options['get_data']($this->start, $this->items_per_page, $this->sort_order, $this->sort_direction); // Okay, now for the column headers... $this->generateColumnHeaders($options); // Should we create a page index? if ($this->recordCount > $this->items_per_page) $this->generatePageIndex($options); // Process the data to be shown into rows. if (!empty($data)) $this->processAllRows($data, $options); else $this->body = $options['no_items_label'] ?? ''; $this->table_class = $options['table_class'] ?? ''; // Got a title? $this->title = $options['title'] ?? ''; $this->title_class = $options['title_class'] ?? ''; // Maybe even a form or two? $this->form_above = $options['form_above'] ?? $options['form'] ?? null; $this->form_below = $options['form_below'] ?? $options['form'] ?? null; } private function initOrder($options) { assert(isset($options['default_sort_order'])); assert(isset($options['default_sort_direction'])); // Validate sort order (column) $this->sort_order = $options['sort_order']; if (empty($this->sort_order) || empty($options['columns'][$this->sort_order]['is_sortable'])) $this->sort_order = $options['default_sort_order']; // Validate sort direction $this->sort_direction = $options['sort_direction']; if (empty($this->sort_direction) || !in_array($this->sort_direction, ['up', 'down'])) $this->sort_direction = $options['default_sort_direction']; } private function initPagination(array $options) { assert(isset($options['base_url'])); assert(isset($options['items_per_page'])); $this->base_url = $options['base_url']; $this->recordCount = $options['get_count'](); $this->items_per_page = !empty($options['items_per_page']) ? $options['items_per_page'] : 30; $this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start']; $numPages = max(1, ceil($this->recordCount / $this->items_per_page)); $this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages); } private function generateColumnHeaders($options) { foreach ($options['columns'] as $key => $column) { if (empty($column['header'])) continue; $isSortable = !empty($column['is_sortable']); $sortDirection = $key == $this->sort_order && $this->sort_direction === 'up' ? 'down' : 'up'; $header = [ 'class' => isset($column['class']) ? $column['class'] : '', 'cell_class' => isset($column['cell_class']) ? $column['cell_class'] : null, 'colspan' => !empty($column['header_colspan']) ? $column['header_colspan'] : 1, 'href' => $isSortable ? $this->getLink($this->start, $key, $sortDirection) : null, 'label' => $column['header'], 'scope' => 'col', 'sort_mode' => $key == $this->sort_order ? $this->sort_direction : null, 'width' => !empty($column['header_width']) && is_int($column['header_width']) ? $column['header_width'] : null, ]; $this->header[] = $header; } } private function generatePageIndex($options) { $this->pageIndex = new PageIndex([ 'base_url' => $this->base_url, 'index_class' => $options['index_class'] ?? '', 'items_per_page' => $this->items_per_page, 'linkBuilder' => [$this, 'getLink'], 'recordCount' => $this->recordCount, 'sort_direction' => $this->sort_direction, 'sort_order' => $this->sort_order, 'start' => $this->start, ]); } public function getLink($start = null, $order = null, $dir = null) { if ($start === null) $start = $this->start; if ($order === null) $order = $this->sort_order; if ($dir === null) $dir = $this->sort_direction; return $this->base_url . (strpos($this->base_url, '?') ? '&' : '?') . 'start=' . $start . '&order=' . $order. '&dir=' . $dir; } public function getOffset() { return $this->start; } public function getHeader() { return $this->header; } public function getBody() { return $this->body; } public function getCurrentPage() { return $this->currentPage; } public function getPageIndex() { return $this->pageIndex; } public function getTableClass() { return $this->table_class; } public function getTitle() { return $this->title; } public function getTitleClass() { return $this->title_class; } private function processAllRows($rows, $options) { foreach ($rows as $i => $row) { $newRow = [ 'cells' => [], ]; foreach ($options['columns'] as $column) { // Process data for this particular cell. if (isset($column['parse'])) $value = self::processCell($column['parse'], $row); else $value = $row[$column['value']]; // Append the cell to the row. $newRow['cells'][] = [ 'class' => $column['cell_class'] ?? '', 'value' => $value, ]; } // Append the new row in the body. $this->body[] = $newRow; } } private function processCell($options, $rowData) { if (!isset($options['type'])) $options['type'] = 'value'; // Parse the basic value first. switch ($options['type']) { // Basic option: simply take a use a particular data property. case 'value': $value = htmlspecialchars($rowData[$options['data']] ?? ''); break; // Processing via a lambda function. case 'function': $value = $options['data']($rowData); break; // Using sprintf to fill out a particular pattern. case 'sprintf': $parameters = [$options['data']['pattern']]; foreach ($options['data']['arguments'] as $identifier) $parameters[] = $rowData[$identifier]; $value = sprintf(...$parameters); break; // Timestamps get custom treatment. case 'timestamp': if (empty($options['data']['pattern']) || $options['data']['pattern'] === 'long') $pattern = 'Y-m-d H:i'; elseif ($options['data']['pattern'] === 'short') $pattern = 'Y-m-d'; else $pattern = $options['data']['pattern']; if (!isset($rowData[$options['data']['timestamp']])) $timestamp = 0; elseif (!is_numeric($rowData[$options['data']['timestamp']])) $timestamp = strtotime($rowData[$options['data']['timestamp']]); else $timestamp = (int) $rowData[$options['data']['timestamp']]; if (isset($options['data']['if_null']) && $timestamp == 0) $value = $options['data']['if_null']; else $value = date($pattern, $timestamp); break; } // Generate a link, if requested. if (!empty($options['link'])) { // First, generate the replacement variables. $keys = array_keys($rowData); $values = array_values($rowData); foreach ($keys as $keyKey => $keyValue) $keys[$keyKey] = '{' . strtoupper($keyValue) . '}'; $value = '' . $value . ''; } return $value; } }