Files
igmf/kernel/CollectionTableRenderer.php
2025-08-01 14:29:50 +03:00

345 lines
9.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace kernel;
use Illuminate\Database\Eloquent\Collection;
class CollectionTableRenderer
{
protected \Illuminate\Database\Eloquent\Collection $collection;
protected array $columns;
protected array $tableAttributes;
protected array $customColumns = [];
protected array $valueProcessors = [];
protected array $filters = [];
protected bool $filterRowEnabled = false;
protected array $filterRowAttributes = [
'class' => '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 = '<table ' . $this->buildAttributes($this->tableAttributes) . '>';
$html .= $this->renderHeader();
if ($this->filterRowEnabled) {
$html .= $this->renderFilterRow();
}
$html .= $this->renderBody();
$html .= '</table>';
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 = '<thead><tr>';
// Обычные колонки
foreach ($this->columns as $title) {
$html .= '<th>' . htmlspecialchars($title) . '</th>';
}
// Кастомные колонки
foreach ($this->customColumns as $column) {
$html .= '<th>' . htmlspecialchars($column['title']) . '</th>';
}
$html .= '</tr></thead>';
return $html;
}
/**
* Генерация строки фильтров
*
* @return string
*/
protected function renderFilterRow(): string
{
$html = '<tr ' . $this->buildAttributes($this->filterRowAttributes) . '>';
// Обычные колонки
foreach (array_keys($this->columns) as $field) {
$html .= '<td>';
if (isset($this->filters[$field])) {
$html .= $this->filters[$field];
} else {
$html .= '&nbsp;';
}
$html .= '</td>';
}
// Кастомные колонки
foreach (array_keys($this->customColumns) as $columnName) {
$html .= '<td>';
if (isset($this->filters[$columnName])) {
$html .= $this->filters[$columnName];
} else {
$html .= '&nbsp;';
}
$html .= '</td>';
}
$html .= '</tr>';
return $html;
}
/**
* Генерация тела таблицы
*
* @return string
*/
protected function renderBody(): string
{
$html = '<tbody>';
foreach ($this->collection as $item) {
$html .= '<tr>';
// Обычные колонки
foreach (array_keys($this->columns) as $field) {
$value = $this->getValue($item, $field);
$value = $this->processValue($field, $value, $item);
$html .= '<td>' . $value . '</td>';
}
// Кастомные колонки
foreach ($this->customColumns as $columnName => $column) {
$value = call_user_func($column['callback'], $item, $columnName);
$html .= '<td>' . $value . '</td>';
}
$html .= '</tr>';
}
$html .= '</tbody>';
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();
}
}