'filter-row' ]; /** * Конструктор класса * * @param Collection $collection */ public function __construct(Collection $collection) { $this->collection = $collection; $this->columns = []; $this->customColumns = []; $this->valueProcessors = []; $this->filters = []; $this->tableAttributes = [ 'class' => 'table table-bordered table-striped', 'id' => 'dataTable' ]; } /** * Установка столбцов для отображения * * @param array $columns Массив столбцов в формате ['field' => 'Заголовок'] * @return $this */ public function setColumns(array $columns): static { $this->columns = $columns; return $this; } /** * Добавление кастомной колонки * * @param string $columnName Название колонки (ключ) * @param string $title Заголовок колонки * @param callable $callback Функция для генерации содержимого * @return $this */ public function addCustomColumn(string $columnName, string $title, callable $callback): static { $this->customColumns[$columnName] = [ 'title' => $title, 'callback' => $callback ]; return $this; } /** * Добавление обработчика значения для колонки * * @param string $columnName Название колонки * @param callable $processor Функция обработки значения * @return $this */ public function addValueProcessor(string $columnName, callable $processor): static { $this->valueProcessors[$columnName] = $processor; return $this; } /** * Добавление фильтра для колонки * * @param string $columnName Название колонки * @param string $filterHtml HTML-код фильтра * @return $this */ public function addFilter(string $columnName, string $filterHtml): static { $this->filters[$columnName] = $filterHtml; $this->filterRowEnabled = true; return $this; } /** * Включение/отключение строки фильтров * * @param bool $enabled * @return $this */ public function enableFilterRow(bool $enabled = true): static { $this->filterRowEnabled = $enabled; return $this; } /** * Установка атрибутов строки фильтров * * @param array $attributes * @return $this */ public function setFilterRowAttributes(array $attributes): static { $this->filterRowAttributes = array_merge($this->filterRowAttributes, $attributes); return $this; } /** * Установка атрибутов для таблицы * * @param array $attributes Массив атрибутов HTML * @return $this */ public function setTableAttributes(array $attributes): static { $this->tableAttributes = array_merge($this->tableAttributes, $attributes); return $this; } /** * Получить HTML-код таблицы (без вывода) * * @return string */ public function getHtml(): string { if (empty($this->columns) && empty($this->customColumns) && $this->collection->isNotEmpty()) { // Автоматически определяем столбцы на основе первой модели $firstItem = $this->collection->first(); $this->columns = array_combine( array_keys($firstItem->toArray()), array_map('ucfirst', array_keys($firstItem->toArray())) ); } $html = 'buildAttributes($this->tableAttributes) . '>'; $html .= $this->renderHeader(); if ($this->filterRowEnabled) { $html .= $this->renderFilterRow(); } $html .= $this->renderBody(); $html .= '
'; return $html; } /** * Генерация HTML-таблицы * * @return string */ public function render(): void { echo $this->getHtml(); } public function fetch(): string { return $this->getHtml(); } /** * Генерация заголовка таблицы * * @return string */ protected function renderHeader(): string { $html = ''; // Обычные колонки foreach ($this->columns as $title) { $html .= '' . htmlspecialchars($title) . ''; } // Кастомные колонки foreach ($this->customColumns as $column) { $html .= '' . htmlspecialchars($column['title']) . ''; } $html .= ''; return $html; } /** * Генерация строки фильтров * * @return string */ protected function renderFilterRow(): string { $html = 'buildAttributes($this->filterRowAttributes) . '>'; // Обычные колонки foreach (array_keys($this->columns) as $field) { $html .= ''; if (isset($this->filters[$field])) { $html .= $this->filters[$field]; } else { $html .= ' '; } $html .= ''; } // Кастомные колонки foreach (array_keys($this->customColumns) as $columnName) { $html .= ''; if (isset($this->filters[$columnName])) { $html .= $this->filters[$columnName]; } else { $html .= ' '; } $html .= ''; } $html .= ''; return $html; } /** * Генерация тела таблицы * * @return string */ protected function renderBody(): string { $html = ''; foreach ($this->collection as $item) { $html .= ''; // Обычные колонки foreach (array_keys($this->columns) as $field) { $value = $this->getValue($item, $field); $value = $this->processValue($field, $value, $item); $html .= '' . $value . ''; } // Кастомные колонки foreach ($this->customColumns as $columnName => $column) { $value = call_user_func($column['callback'], $item, $columnName); $html .= '' . $value . ''; } $html .= ''; } $html .= ''; return $html; } /** * Обработка значения ячейки * * @param string $field Название поля * @param mixed $value Значение * @param mixed $item Весь элемент коллекции * @return mixed */ protected function processValue(string $field, mixed $value, mixed $item): mixed { // Если есть обработчик для этого поля - применяем его if (isset($this->valueProcessors[$field])) { $value = call_user_func($this->valueProcessors[$field], $value, $item, $field); } // Если значение не прошло обработку - экранируем его // if (is_string($value)) { // return htmlspecialchars($value); // } return $value; } /** * Получение значения из элемента коллекции * * @param mixed $item * @param string $field * @return mixed */ protected function getValue(mixed $item, string $field): mixed { // Поддержка dot-notation для отношений if (str_contains($field, '.')) { return data_get($item, $field); } // Поддержка accessor'ов модели if (method_exists($item, 'get' . ucfirst($field) . 'Attribute')) { return $item->{$field}; } return $item->{$field} ?? null; } /** * Сборка HTML-атрибутов * * @param array $attributes * @return string */ protected function buildAttributes(array $attributes): string { $result = []; foreach ($attributes as $key => $value) { $result[] = $key . '="' . htmlspecialchars($value) . '"'; } return implode(' ', $result); } /** * Магический метод для преобразования в строку * * @return string */ public function __toString() { return $this->render(); } }