345 lines
9.5 KiB
PHP
345 lines
9.5 KiB
PHP
<?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 .= ' ';
|
||
}
|
||
$html .= '</td>';
|
||
}
|
||
|
||
// Кастомные колонки
|
||
foreach (array_keys($this->customColumns) as $columnName) {
|
||
$html .= '<td>';
|
||
if (isset($this->filters[$columnName])) {
|
||
$html .= $this->filters[$columnName];
|
||
} else {
|
||
$html .= ' ';
|
||
}
|
||
$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();
|
||
}
|
||
} |