jidaikobo-shibata /
kontiki-framework
| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace Jidaikobo\Kontiki\Renderers; |
||||
| 4 | |||||
| 5 | use Carbon\Carbon; |
||||
| 6 | use Slim\Views\PhpRenderer; |
||||
| 7 | use Jidaikobo\Kontiki\Models\ModelInterface; |
||||
| 8 | |||||
| 9 | class TableRenderer |
||||
| 10 | { |
||||
| 11 | protected $fields; |
||||
| 12 | protected $data; |
||||
| 13 | protected $view; |
||||
| 14 | protected $table; |
||||
| 15 | protected $adminDirName; |
||||
| 16 | protected $context; |
||||
| 17 | protected $routes; |
||||
| 18 | protected $postType; |
||||
| 19 | protected $deleteType; // Context: "hardDelete" or "softDelete" |
||||
| 20 | private ?ModelInterface $model = null; |
||||
| 21 | |||||
| 22 | public function __construct(PhpRenderer $view) |
||||
| 23 | { |
||||
| 24 | $this->view = $view; |
||||
| 25 | } |
||||
| 26 | |||||
| 27 | public function setModel(ModelInterface $model): void |
||||
| 28 | { |
||||
| 29 | $this->model = $model; |
||||
| 30 | } |
||||
| 31 | |||||
| 32 | public function render( |
||||
| 33 | array $data, |
||||
| 34 | string $adminDirName, |
||||
| 35 | array $routes = [], |
||||
| 36 | string $context = 'all' |
||||
| 37 | ): string { |
||||
| 38 | $this->deleteType = $this->model->getDeleteType(); |
||||
|
0 ignored issues
–
show
|
|||||
| 39 | $this->data = $data; |
||||
| 40 | $this->adminDirName = $adminDirName; |
||||
| 41 | $this->routes = $routes; |
||||
| 42 | $this->context = $context; |
||||
| 43 | |||||
| 44 | $this->fields = array_filter( |
||||
| 45 | $this->model->getFields(), |
||||
| 46 | fn($field) => isset($field['display_in_list']) && |
||||
| 47 | ($field['display_in_list'] === true || $field['display_in_list'] == $context) |
||||
| 48 | ); |
||||
| 49 | |||||
| 50 | $createButton = $this->renderCreateButton(); |
||||
| 51 | $displayModes = $this->renderDisplayModes(); |
||||
| 52 | $headers = $this->renderHeaders(); |
||||
| 53 | $rows = array_map(function ($row) { |
||||
| 54 | return $this->renderRow($row); |
||||
| 55 | }, $this->data); |
||||
| 56 | |||||
| 57 | return $this->view->fetch('tables/table.php', [ |
||||
| 58 | 'createButton' => $createButton, |
||||
| 59 | 'displayModes' => $displayModes, |
||||
| 60 | 'headers' => $headers, |
||||
| 61 | 'rows' => implode("\n", $rows), |
||||
| 62 | ]); |
||||
| 63 | } |
||||
| 64 | |||||
| 65 | protected function renderCreateButton(): array |
||||
| 66 | { |
||||
| 67 | $filtered = array_filter($this->routes, function ($routes) { |
||||
| 68 | return in_array('createButton', $routes['type'], true); |
||||
| 69 | }); |
||||
| 70 | $createButton = !empty($filtered) ? reset($filtered) : []; |
||||
| 71 | return $createButton; |
||||
| 72 | } |
||||
| 73 | |||||
| 74 | protected function renderDisplayModes(): array |
||||
| 75 | { |
||||
| 76 | $displayModes = []; |
||||
| 77 | |||||
| 78 | foreach ($this->routes as $route) { |
||||
| 79 | if (strpos($route['path'], $this->adminDirName . '/index') === false) { |
||||
| 80 | continue; |
||||
| 81 | } |
||||
| 82 | |||||
| 83 | $displayModes[] = [ |
||||
| 84 | 'name' => __(basename($route['path'])), |
||||
| 85 | 'path' => $route['path'], |
||||
| 86 | ]; |
||||
| 87 | } |
||||
| 88 | |||||
| 89 | return $displayModes; |
||||
| 90 | } |
||||
| 91 | |||||
| 92 | protected function renderHeaders(): string |
||||
| 93 | { |
||||
| 94 | $headerHtml = ''; |
||||
| 95 | foreach ($this->fields as $name => $config) { |
||||
| 96 | $label = e($config['label']); |
||||
| 97 | $headerHtml .= sprintf('<th class="text-nowrap">%s</th>', $label); |
||||
| 98 | } |
||||
| 99 | $headerHtml .= '<th>' . __('actions') . '</th>'; |
||||
| 100 | return $headerHtml; |
||||
| 101 | } |
||||
| 102 | |||||
| 103 | protected function renderRow(array $row): string |
||||
| 104 | { |
||||
| 105 | $cellsHtml = $this->renderValues($row); |
||||
| 106 | $cellsHtml .= $this->renderActions($row); |
||||
| 107 | |||||
| 108 | return sprintf('<tr>%s</tr>', $cellsHtml); |
||||
| 109 | } |
||||
| 110 | |||||
| 111 | protected function renderValues(array $row): string |
||||
| 112 | { |
||||
| 113 | $currentTime = Carbon::now('UTC')->setTimezone(env('TIMEZONE', 'UTC')); |
||||
| 114 | $cellsHtml = ''; |
||||
| 115 | |||||
| 116 | foreach (array_keys($this->fields) as $name) { |
||||
| 117 | $values = $this->getRowValues($name, $row, $currentTime); |
||||
| 118 | $value = implode(', ', array_filter($values)); |
||||
| 119 | $cellsHtml .= sprintf('<td>%s</td>', e($value)); |
||||
| 120 | } |
||||
| 121 | |||||
| 122 | return $cellsHtml; |
||||
| 123 | } |
||||
| 124 | |||||
| 125 | protected function getRowValues(string $name, array $row, Carbon $currentTime): array |
||||
| 126 | { |
||||
| 127 | if ($name === 'status') { |
||||
| 128 | return $this->getStatusValues($row, $currentTime); |
||||
| 129 | } |
||||
| 130 | |||||
| 131 | if ($this->isSelectableField($name)) { |
||||
| 132 | return [$this->resolveOptionLabel($name, $row[$name] ?? '')]; |
||||
| 133 | } |
||||
| 134 | |||||
| 135 | if ($this->isUtcField($name)) { |
||||
| 136 | return [$this->formatDateTimeField($row[$name] ?? '')]; |
||||
| 137 | } |
||||
| 138 | |||||
| 139 | return [$row[$name] ?? '']; |
||||
| 140 | } |
||||
| 141 | |||||
| 142 | private function isSelectableField(string $name): bool |
||||
| 143 | { |
||||
| 144 | $type = $this->fields[$name]['type'] ?? 'text'; |
||||
| 145 | return in_array($type, ['select', 'checkbox', 'radio'], true) && $name !== 'status'; |
||||
| 146 | } |
||||
| 147 | |||||
| 148 | private function resolveOptionLabel(string $name, string|int|null $value): string |
||||
| 149 | { |
||||
| 150 | $options = $this->fields[$name]['options'] ?? []; |
||||
| 151 | return $options[$value] ?? ''; |
||||
| 152 | } |
||||
| 153 | |||||
| 154 | private function isUtcField(string $name): bool |
||||
| 155 | { |
||||
| 156 | return !empty($this->fields[$name]['save_as_utc']); |
||||
| 157 | } |
||||
| 158 | |||||
| 159 | private function formatDateTimeField(?string $value): string |
||||
| 160 | { |
||||
| 161 | if (empty($value)) { |
||||
| 162 | return ''; |
||||
| 163 | } |
||||
| 164 | |||||
| 165 | try { |
||||
| 166 | return Carbon::parse($value)->format('Y-m-d H:i'); |
||||
| 167 | } catch (\Exception $e) { |
||||
| 168 | return $value; // fallback to raw |
||||
| 169 | } |
||||
| 170 | } |
||||
| 171 | |||||
| 172 | private function getStatusValues(array $row, Carbon $currentTime): array |
||||
| 173 | { |
||||
| 174 | $values = [__($row['status'] ?? '') ?: '']; |
||||
| 175 | |||||
| 176 | $this->addStatusIfConditionMet( |
||||
| 177 | $values, |
||||
| 178 | $row, |
||||
| 179 | 'published_at', |
||||
| 180 | $currentTime, |
||||
| 181 | fn($time) => $time->greaterThan($currentTime), |
||||
| 182 | 'reserved' |
||||
| 183 | ); |
||||
| 184 | |||||
| 185 | $this->addStatusIfConditionMet( |
||||
| 186 | $values, |
||||
| 187 | $row, |
||||
| 188 | 'expired_at', |
||||
| 189 | $currentTime, |
||||
| 190 | fn($time) => $currentTime->greaterThan($time), |
||||
| 191 | 'expired' |
||||
| 192 | ); |
||||
| 193 | |||||
| 194 | return $values; |
||||
| 195 | } |
||||
| 196 | |||||
| 197 | /** |
||||
| 198 | * Add a status to values if the condition is met. |
||||
| 199 | * |
||||
| 200 | * @param array $values Reference to the values array. |
||||
| 201 | * @param array $row The data row. |
||||
| 202 | * @param string $key The key to check in the row. |
||||
| 203 | * @param Carbon $currentTime The current timestamp. |
||||
| 204 | * @param callable $condition Callback that takes a Carbon instance and returns a boolean. |
||||
| 205 | * @param string $status The status text to add if the condition is met. |
||||
| 206 | */ |
||||
| 207 | private function addStatusIfConditionMet( |
||||
| 208 | array &$values, |
||||
| 209 | array $row, |
||||
| 210 | string $key, |
||||
| 211 | Carbon $currentTime, |
||||
|
0 ignored issues
–
show
The parameter
$currentTime is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||
| 212 | callable $condition, |
||||
| 213 | string $status |
||||
| 214 | ): void { |
||||
| 215 | if (!empty($row[$key])) { |
||||
| 216 | $time = Carbon::parse($row[$key], env('TIMEZONE', 'UTC')); |
||||
| 217 | if ($condition($time)) { |
||||
| 218 | $values[0] = __($status); |
||||
| 219 | } |
||||
| 220 | } |
||||
| 221 | } |
||||
| 222 | |||||
| 223 | protected function renderActions(array $row): string |
||||
| 224 | { |
||||
| 225 | $id = e($row['id']); |
||||
| 226 | |||||
| 227 | $uri = env('BASEPATH', '') . "/{$this->adminDirName}/%s/%s"; |
||||
| 228 | |||||
| 229 | $tpl = '<a href="' . $uri . '" class="btn btn-%s btn-sm">%s</a> '; |
||||
| 230 | $tplTrash = '<a href="' . $uri . '" class="btn btn-%s btn-sm">%s <span class="fa-solid fa-trash"></span></a> '; |
||||
| 231 | $tplPreview = '<a href="' . $uri . '" class="btn btn-%s btn-sm" target="preview">%s <span class="fa-solid fa-arrow-up-right-from-square" aria-label="' . __('open_in_new_window') . '"></span></a> '; |
||||
| 232 | |||||
| 233 | $actions = [ |
||||
| 234 | 'edit' => sprintf($tpl, 'edit', $id, 'primary', __('edit')), |
||||
| 235 | 'delete' => sprintf($tplTrash, 'delete', $id, 'danger', __('delete')), |
||||
| 236 | 'trash' => sprintf($tplTrash, 'trash', $id, 'warning', __('to_trash')), |
||||
| 237 | 'restore' => sprintf($tpl, 'restore', $id, 'success', __('restore')), |
||||
| 238 | 'preview' => sprintf($tplPreview, 'preview', $id, 'info', __('preview')), |
||||
| 239 | ]; |
||||
| 240 | |||||
| 241 | $previewBtn = $this->view->getAttributes()['is_previewable'] ? $actions['preview'] : ''; |
||||
| 242 | |||||
| 243 | $html = ''; |
||||
| 244 | if ($this->deleteType == 'hardDelete') { |
||||
| 245 | $html .= $actions['edit'] . $actions['delete']; |
||||
| 246 | } elseif ($this->context == 'trash') { |
||||
| 247 | $html .= $actions['restore'] . $previewBtn . $actions['delete']; |
||||
| 248 | } elseif ($this->deleteType == 'softDelete') { |
||||
| 249 | $html .= $actions['edit'] . $previewBtn . $actions['trash']; |
||||
| 250 | } else { |
||||
| 251 | $html .= $actions['edit']; |
||||
| 252 | } |
||||
| 253 | |||||
| 254 | return sprintf('<td class="text-nowrap">%s</td>', $html); |
||||
| 255 | } |
||||
| 256 | } |
||||
| 257 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.