dynamic table
This commit is contained in:
@@ -19,6 +19,8 @@ class TwigGlobalsExtension extends AbstractExtension
|
||||
new TwigFunction('render_pager', [$this, 'renderPager'], ['is_safe' => ['html']]),
|
||||
new TwigFunction('is_active_route', [$this, 'isActiveRoute'], ['is_safe' => ['html']]),
|
||||
new TwigFunction('get_current_route', [$this, 'getCurrentRoute'], ['is_safe' => ['html']]),
|
||||
new TwigFunction('render_actions', [$this, 'renderActions'], ['is_safe' => ['html']]),
|
||||
new TwigFunction('render_cell', [$this, 'renderCell'], ['is_safe' => ['html']]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -115,4 +117,187 @@ class TwigGlobalsExtension extends AbstractExtension
|
||||
// Убираем начальный слеш если есть
|
||||
return ltrim($route, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует HTML для кнопок действий в строке таблицы
|
||||
*
|
||||
* @param object|array $item Данные строки (объект или массив)
|
||||
* @param array $actions Массив конфигураций действий
|
||||
* @return string HTML код кнопок
|
||||
*/
|
||||
public function renderActions($item, array $actions = []): string
|
||||
{
|
||||
if (empty($actions)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Конвертируем объект в массив для доступа к свойствам
|
||||
$itemArray = $this->objectToArray($item);
|
||||
|
||||
// DEBUG: логируем для отладки
|
||||
log_message('debug', 'renderActions: item type = ' . gettype($item));
|
||||
log_message('debug', 'renderActions: item keys = ' . (is_array($itemArray) ? implode(', ', array_keys($itemArray)) : 'N/A'));
|
||||
log_message('debug', 'renderActions: item = ' . print_r($itemArray, true));
|
||||
|
||||
$html = '<div class="btn-group btn-group-sm">';
|
||||
|
||||
foreach ($actions as $action) {
|
||||
$label = $action['label'] ?? 'Action';
|
||||
$urlPattern = $action['url'] ?? '#';
|
||||
$icon = $action['icon'] ?? '';
|
||||
$class = $action['class'] ?? 'btn-outline-secondary';
|
||||
$title = $action['title'] ?? $label;
|
||||
$target = $action['target'] ?? '';
|
||||
|
||||
// Подставляем значения из item в URL
|
||||
$url = $this->interpolate($urlPattern, $itemArray);
|
||||
|
||||
log_message('debug', 'renderActions: urlPattern = ' . $urlPattern . ', url = ' . $url);
|
||||
|
||||
// Формируем HTML кнопки/ссылки
|
||||
$iconHtml = $icon ? '<i class="' . esc($icon) . '"></i> ' : '';
|
||||
$targetAttr = $target ? ' target="' . esc($target) . '"' : '';
|
||||
|
||||
$html .= '<a href="' . esc($url) . '" '
|
||||
. 'class="btn ' . esc($class) . '" '
|
||||
. 'title="' . esc($title) . '"'
|
||||
. $targetAttr . '>'
|
||||
. $iconHtml . esc($label)
|
||||
. '</a>';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертирует объект в массив (включая защищённые свойства)
|
||||
*/
|
||||
private function objectToArray($data): array
|
||||
{
|
||||
if (is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if (is_object($data)) {
|
||||
// Используем json_decode/encode для надёжного извлечения всех свойств
|
||||
$json = json_encode($data);
|
||||
return json_decode($json, true);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Рендерит значение ячейки таблицы
|
||||
*
|
||||
* @param object|array $item Данные строки
|
||||
* @param string $key Ключ поля для отображения
|
||||
* @param array $config Конфигурация колонки (опционально)
|
||||
* @return string HTML код ячейки
|
||||
*/
|
||||
public function renderCell($item, string $key, array $config = []): string
|
||||
{
|
||||
$itemArray = $this->objectToArray($item);
|
||||
$value = $itemArray[$key] ?? null;
|
||||
|
||||
// Если значение пустое и есть значение по умолчанию
|
||||
if (($value === null || $value === '' || $value === false) && isset($config['default'])) {
|
||||
return $config['default'];
|
||||
}
|
||||
|
||||
// Если указан шаблон, используем его для рендеринга
|
||||
if (isset($config['template'])) {
|
||||
$template = $config['template'];
|
||||
// Подставляем все значения из item в шаблон
|
||||
foreach ($itemArray as $k => $v) {
|
||||
$template = str_replace('{' . $k . '}', esc($v ?? ''), $template);
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
|
||||
// Применяем тип преобразования если указан
|
||||
if (isset($config['type'])) {
|
||||
switch ($config['type']) {
|
||||
case 'email':
|
||||
if ($value) {
|
||||
return '<a href="mailto:' . esc($value) . '">' . esc($value) . '</a>';
|
||||
}
|
||||
return $config['default'] ?? '—';
|
||||
|
||||
case 'phone':
|
||||
if ($value) {
|
||||
return '<a href="tel:' . esc($value) . '">' . esc($value) . '</a>';
|
||||
}
|
||||
return $config['default'] ?? '—';
|
||||
|
||||
case 'date':
|
||||
if ($value) {
|
||||
return esc(date('d.m.Y', strtotime($value)));
|
||||
}
|
||||
return $config['default'] ?? '—';
|
||||
|
||||
case 'datetime':
|
||||
if ($value) {
|
||||
return esc(date('d.m.Y H:i', strtotime($value)));
|
||||
}
|
||||
return $config['default'] ?? '—';
|
||||
|
||||
case 'boolean':
|
||||
case 'bool':
|
||||
if ($value) {
|
||||
return '<span class="badge bg-success">Да</span>';
|
||||
}
|
||||
return '<span class="badge bg-secondary">Нет</span>';
|
||||
|
||||
case 'uppercase':
|
||||
return $value ? esc(strtoupper($value)) : ($config['default'] ?? '');
|
||||
|
||||
case 'lowercase':
|
||||
return $value ? esc(strtolower($value)) : ($config['default'] ?? '');
|
||||
|
||||
case 'truncate':
|
||||
$length = $config['length'] ?? 50;
|
||||
if ($value && strlen($value) > $length) {
|
||||
return esc(substr($value, 0, $length)) . '...';
|
||||
}
|
||||
return esc($value ?? '');
|
||||
|
||||
case 'currency':
|
||||
if ($value !== null && $value !== '') {
|
||||
return number_format((float) $value, 0, '.', ' ') . ' ₽';
|
||||
}
|
||||
return $config['default'] ?? '—';
|
||||
|
||||
case 'percent':
|
||||
if ($value !== null && $value !== '') {
|
||||
return esc((float) $value) . '%';
|
||||
}
|
||||
return $config['default'] ?? '—';
|
||||
}
|
||||
}
|
||||
|
||||
// По умолчанию просто возвращаем значение
|
||||
return $value !== null && $value !== '' ? esc((string) $value) : '—';
|
||||
}
|
||||
|
||||
/**
|
||||
* Подставляет значения из данных в шаблон строки
|
||||
*
|
||||
* @param string $pattern Шаблон с плейсхолдерами вида {field_name}
|
||||
* @param object|array $data Данные для подстановки
|
||||
* @return string Результирующая строка
|
||||
*/
|
||||
private function interpolate(string $pattern, $data): string
|
||||
{
|
||||
// Конвертируем объект в массив
|
||||
$data = is_object($data) ? $this->objectToArray($data) : $data;
|
||||
|
||||
// Заменяем все {key} на значения
|
||||
return preg_replace_callback('/\{(\w+)\}/', function ($matches) use ($data) {
|
||||
$key = $matches[1];
|
||||
return isset($data[$key]) ? esc($data[$key]) : $matches[0];
|
||||
}, $pattern);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user