tableIsSortable = !empty($options['base_url']); // How much stuff do we have? $this->recordCount = $options['get_count'](...(!empty($options['get_count_params']) ? $options['get_count_params'] : [])); // Should we create a page index? $this->items_per_page = !empty($options['items_per_page']) ? $options['items_per_page'] : 30; $this->needsPageIndex = !empty($this->items_per_page) && $this->recordCount > $this->items_per_page; $this->index_class = isset($options['index_class']) ? $options['index_class'] : ''; // Figure out where to start. $this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start']; // Let's bear a few things in mind... $this->base_url = $options['base_url']; // This should be set at all times, too. if (empty($options['no_items_label'])) $options['no_items_label'] = ''; // Gather parameters for the data gather function first. $parameters = [$this->start, $this->items_per_page, $options['sort_order'], $options['sort_direction']]; if (!empty($options['get_data_params']) && is_array($options['get_data_params'])) $parameters = array_merge($parameters, $options['get_data_params']); // Okay, let's fetch the data! $data = $options['get_data'](...$parameters); // Clean up a bit. $rows = $data['rows']; $this->sort_order = $data['order']; $this->sort_direction = $data['direction']; unset($data); // Okay, now for the column headers... $this->generateColumnHeaders($options); // Generate a pagination if requested if ($this->needsPageIndex) $this->generatePageIndex(); // Not a single row in sight? if (empty($rows)) $this->body = $options['no_items_label']; // Otherwise, parse it all! else $this->parseAllRows($rows, $options); // Got a title? $this->title = isset($options['title']) ? htmlentities($options['title']) : ''; $this->title_class = isset($options['title_class']) ? $options['title_class'] : ''; // Maybe even a form or two? $this->form_above = isset($options['form_above']) ? $options['form_above'] : (isset($options['form']) ? $options['form'] : null); $this->form_below = isset($options['form_below']) ? $options['form_below'] : (isset($options['form']) ? $options['form'] : null); } private function generateColumnHeaders($options) { foreach ($options['columns'] as $key => $column) { if (empty($column['header'])) continue; $header = [ 'class' => isset($column['class']) ? $column['class'] : '', 'colspan' => !empty($column['header_colspan']) ? $column['header_colspan'] : 1, 'href' => !$this->tableIsSortable || empty($column['is_sortable']) ? '' : $this->getLink($this->start, $key, $key == $this->sort_order && $this->sort_direction == 'up' ? 'down' : 'up'), '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 parseAllRows($rows, $options) { foreach ($rows as $i => $row) { $className = $i & 1 ? 'even' : 'odd'; if (isset($options['row_classifier'])) $className .= $options['row_classifier']($row); $newRow = [ 'class' => $className, 'cells' => [], ]; foreach ($options['columns'] as $column) { if (isset($column['enabled']) && $column['enabled'] == false) continue; // The hard way? if (isset($column['parse'])) { if (!isset($column['parse']['type'])) $column['parse']['type'] = 'value'; // Parse the basic value first. switch ($column['parse']['type']) { // value: easy as pie. default: case 'value': $value = $row[$column['parse']['data']]; break; // sprintf: filling the gaps! case 'sprintf': $parameters = [$column['parse']['data']['pattern']]; foreach ($column['parse']['data']['arguments'] as $identifier) $parameters[] = $row[$identifier]; $value = sprintf(...$parameters); break; // timestamps: let's make them readable! case 'timestamp': if (empty($column['parse']['data']['pattern']) || $column['parse']['data']['pattern'] === 'long') $pattern = '%F %H:%M'; elseif ($column['parse']['data']['pattern'] === 'short') $pattern = '%F'; else $pattern = $column['parse']['data']['pattern']; if (!is_numeric($row[$column['parse']['data']['timestamp']])) $timestamp = strtotime($row[$column['parse']['data']['timestamp']]); else $timestamp = (int) $row[$column['parse']['data']['timestamp']]; if (isset($column['parse']['data']['if_null']) && $timestamp == 0) $value = $column['parse']['data']['if_null']; else $value = strftime($pattern, $timestamp); break; // function: the flexible way! case 'function': $value = $column['parse']['data']($row); break; } // Generate a link, if requested. if (!empty($column['parse']['link'])) { // First, generate the replacement variables. $keys = array_keys($row); $values = array_values($row); foreach ($keys as $keyKey => $keyValue) $keys[$keyKey] = '{' . strtoupper($keyValue) . '}'; $value = '' . $value . ''; } } // The easy way! else $value = $row[$column['value']]; // Append the cell to the row. $newRow['cells'][] = [ 'width' => !empty($column['cell_width']) && is_int($column['cell_width']) ? $column['cell_width'] : null, 'value' => $value, ]; } // Append the new row in the body. $this->body[] = $newRow; } } 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 getArray() { // Makes no sense to call it for a table, but inherits from PageIndex due to poor design, sorry. throw new Exception('Function call is ambiguous.'); } public function getHeader() { return $this->header; } public function getBody() { return $this->body; } public function getTitle() { return $this->title; } public function getTitleClass() { return $this->title_class; } }