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();
|
|
}
|
|
} |