Skip to content

Commit e94aa64

Browse files
committed
Add API for DataTables
1 parent 08efdc6 commit e94aa64

8 files changed

Lines changed: 508 additions & 0 deletions

File tree

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
<?php
2+
3+
namespace API\Controllers;
4+
5+
use ApiPlatform\Metadata\ApiResource;
6+
use ApiPlatform\Metadata\Post;
7+
use ApiPlatform\Metadata\Operation;
8+
use ApiPlatform\State\ProcessorInterface;
9+
use DTO\DataTablesLoadRequest\DataTablesLoadRequest;
10+
use DTO\DataTablesLoadRequest\Column;
11+
use DTO\DataTablesLoadResponse\DataTablesLoadResponse;
12+
use DTO\DataTablesLoadResponse\RowData;
13+
14+
use Models\Module;
15+
use Util\Query;
16+
use Modules;
17+
use Plugins;
18+
use InvalidArgumentException;
19+
20+
#[Post(
21+
uriTemplate: '/datatables/list/{id_module}/{id_plugin}/{id_parent}',
22+
processor: DataTablesController::class,
23+
input: DataTablesLoadRequest::class,
24+
output: DataTablesLoadResponse::class,
25+
)]
26+
class DataTablesResource
27+
{
28+
}
29+
30+
final class DataTablesController implements ProcessorInterface
31+
{
32+
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): DataTablesLoadResponse
33+
{
34+
if (!$data instanceof DataTablesLoadRequest) {
35+
throw new InvalidArgumentException();
36+
}
37+
38+
$id_module = !empty($uriVariables['id_module']) ? $uriVariables['id_module'] : null;
39+
$id_plugin = !empty($uriVariables['id_plugin']) ? $uriVariables['id_plugin'] : null;
40+
$id_parent = !empty($uriVariables['id_parent']) ? $uriVariables['id_parent'] : null;
41+
42+
$module = Modules::get($id_module);
43+
Modules::setCurrent($id_module);
44+
45+
$plugin = null;
46+
if (!empty($id_plugin)) {
47+
Plugins::setCurrent($id_plugin);
48+
$plugin = Plugins::get($id_plugin);
49+
}
50+
51+
$structure = isset($plugin) ? $plugin : $module;
52+
53+
return $this->retrieveRecords($structure, $data, $id_module, $id_plugin, $id_parent);
54+
}
55+
56+
protected function retrieveRecords($structure, DataTablesLoadRequest $data, $id_module, $id_plugin, $id_parent): DataTablesLoadResponse
57+
{
58+
// Informazioni fondamentali
59+
$order = $data->order ? $data->order[0] : [];
60+
61+
if (!empty($order)) {
62+
$order['column'] = $order['column'] - 1;
63+
}
64+
65+
$query_structure = Query::readQuery($structure);
66+
67+
$response = new DataTablesLoadResponse($data->draw);
68+
69+
$query = Query::getQuery($structure, [], [], [], $query_structure);
70+
if (empty($query)) {
71+
return $response;
72+
}
73+
74+
// Ricerca
75+
$search = [];
76+
$columns = $data->columns;
77+
array_shift($columns);
78+
for ($i = 0; $i < count($columns); ++$i) {
79+
$col = Column::fromArray($columns[$i]);
80+
if (!empty($col->search->value) || $col->search->value == '0') {
81+
$search[$query_structure['fields'][$i]] = $col->search->value;
82+
}
83+
}
84+
85+
// CONTEGGIO TOTALE
86+
$response->recordsTotal = database()->fetchNum($query);
87+
88+
// CONTEGGIO RECORD FILTRATI (senza LIMIT)
89+
$query_filtered = Query::getQuery($structure, $search, $order, [], $query_structure);
90+
if (empty($id_plugin)) {
91+
$query_filtered = Modules::replaceAdditionals($id_module, $query_filtered);
92+
}
93+
$response->recordsFiltered = database()->fetchNum($query_filtered);
94+
95+
// SOMME
96+
$response->summable = Query::getSums($structure, $search);
97+
98+
// MEDIE
99+
$response->avg = Query::getAverages($structure, $search);
100+
101+
$limit = [
102+
'start' => $data->start,
103+
'length' => $data->length,
104+
];
105+
// RISULTATI VISIBILI (con LIMIT)
106+
$query = Query::getQuery($structure, $search, $order, $limit, $query_structure);
107+
108+
// Filtri derivanti dai permessi (eventuali)
109+
if (empty($id_plugin)) {
110+
$query = Modules::replaceAdditionals($id_module, $query);
111+
}
112+
113+
// Esecuzione query per ottenere i risultati
114+
$data = Query::executeAndCount($query);
115+
$rows = $data['results'];
116+
117+
// Allineamento delle righe
118+
$align = $this->detectAlignment($rows);
119+
// Creazione della tabella
120+
foreach ($rows as $i => $r) {
121+
$row_data = $this->getSingleRow($r, $query_structure, $align, $id_module, $id_plugin, $id_parent);
122+
$response->data[] = $row_data;
123+
}
124+
125+
return $response;
126+
}
127+
128+
protected function detectAlignment(array $rows): array
129+
{
130+
// Allineamento delle righe
131+
$align = [];
132+
$row = isset($rows[0]) ? $rows[0] : [];
133+
foreach ($row as $field => $value) {
134+
if (!empty($value)) {
135+
$value = trim($value);
136+
}
137+
138+
// Allineamento a destra se il valore della prima riga risulta numerica
139+
if (is_numeric($value) && formatter()->isStandardNumber($value)) {
140+
$align[$field] = 'text-right';
141+
}
142+
143+
// Allineamento al centro se il valore della prima riga risulta relativo a date o icone
144+
elseif (formatter()->isStandardDate($value) || preg_match('/^icon_(.+?)$/', $field)) {
145+
$align[$field] = 'text-center';
146+
}
147+
}
148+
149+
return $align;
150+
}
151+
152+
protected function getSingleRow($r, $query_structure, $align, $id_module, $id_plugin, $id_parent): array {
153+
// Evitare risultati con ID a null
154+
// Codice non applicabile in ogni caso: sulla base dei permessi, ID può non essere impostato
155+
/*
156+
if (empty($r['id'])) {
157+
continue;
158+
}*/
159+
160+
$result = [
161+
'id' => $r['id'],
162+
'<span class="hide" data-id="'.$r['id'].'"></span>', // Colonna ID
163+
];
164+
165+
foreach ($query_structure['fields'] as $pos => $field) {
166+
$column = [];
167+
168+
if (!empty($r['_bg_'])) {
169+
if (preg_match('/-light$/', $r['_bg_'])) {
170+
$column['data-background'] = substr($r['_bg_'], 0, -6); // Remove the "-light" suffix from the word
171+
} else {
172+
$column['data-background'] = $r['_bg_'];
173+
}
174+
}
175+
176+
// Allineamento
177+
if (!empty($align[$field])) {
178+
$column['class'] = $align[$field];
179+
}
180+
181+
$value = trim((string) $r[$field]);
182+
183+
// Formattazione HTML
184+
if (empty($query_structure['html_format'][$pos]) && !empty($value)) {
185+
$value = strip_tags($value ?: '');
186+
}
187+
188+
// Formattazione automatica
189+
if (!empty($query_structure['format'][$pos]) && !empty($value)) {
190+
if (formatter()->isStandardTimestamp($value)) {
191+
$value = Translator::timestampToLocale($value);
192+
} elseif (formatter()->isStandardDate($value)) {
193+
$value = Translator::dateToLocale($value);
194+
} elseif (formatter()->isStandardTime($value)) {
195+
$value = Translator::timeToLocale($value);
196+
} elseif (formatter()->isStandardNumber($value)) {
197+
$value = Translator::numberToLocale($value);
198+
}
199+
}
200+
201+
// Icona
202+
if (preg_match('/^color_(.+?)$/', $field, $m)) {
203+
$value = isset($r['color_title_'.$m[1]]) ? $r['color_title_'.$m[1]] : '';
204+
205+
// Formattazione automatica
206+
if (!empty($query_structure['format'][$pos]) && !empty($value)) {
207+
if (formatter()->isStandardTimestamp($value)) {
208+
$value = Translator::timestampToLocale($value);
209+
} elseif (formatter()->isStandardDate($value)) {
210+
$value = Translator::dateToLocale($value);
211+
} elseif (formatter()->isStandardTime($value)) {
212+
$value = Translator::timeToLocale($value);
213+
} elseif (formatter()->isStandardNumber($value)) {
214+
$value = Translator::numberToLocale($value);
215+
}
216+
}
217+
218+
$column['class'] = 'text-center small';
219+
$column['data-background'] = $r[$field];
220+
}
221+
222+
// Icona di stampa
223+
elseif ($field == '_print_') {
224+
$print = $r['_print_'];
225+
226+
$print_url = Prints::getHref($print, $r['id']);
227+
228+
$value = '<a href="'.$print_url.'" target="_blank"><i class="fa fa-2x fa-print"></i></a>';
229+
}
230+
231+
// Immagine
232+
elseif ($field == '_img_') {
233+
$module = Module::where('id', $id_module)->first();
234+
if (!empty($r['_img_'])) {
235+
$fileinfo = Uploads::fileInfo($r['_img_']);
236+
237+
$directory = '/'.$module->upload_directory.'/';
238+
$image = $directory.$r['_img_'];
239+
$image_thumbnail = $directory.$fileinfo['filename'].'_thumb600.'.$fileinfo['extension'];
240+
241+
$url = file_exists(base_dir().$image_thumbnail) ? base_path_osm().$image_thumbnail : base_path_osm().$image;
242+
243+
$value = '<img src="'.$url.'" style="max-height: 80px; max-width:120px">';
244+
}
245+
}
246+
247+
// Icona
248+
elseif (preg_match('/^icon_(.+?)$/', trim($field), $m)) {
249+
$value = '<span class=\'badge text-black\' style=\'font-weight:normal;\' ><i class="'.$r[$field].'" title="'.$r['icon_title_'.$m[1]].'" ></i> <span>'.$r['icon_title_'.$m[1]].'</span></span>';
250+
}
251+
252+
// Colore del testo
253+
if (!empty($column['data-background'])) {
254+
$column['data-color'] = isset($column['data-color']) ? $column['data-color'] : color_inverse(trim($column['data-background']));
255+
} elseif (preg_match('/^mailto_(.+?)$/', trim($field), $m)) {
256+
$column['class'] = '';
257+
$value = ($r[$field] ? '<a class="btn btn-default btn-sm btn-block" style="font-weight:normal;" href="mailto:'.$r[$field].'" target="_blank"><i class="fa fa-envelope text-primary"></i> '.$r[$field].'</a>' : '');
258+
} elseif (preg_match('/^tel_(.+?)$/', trim($field), $m)) {
259+
$column['class'] = '';
260+
$value = ($r[$field] ? '<a class="btn btn-default btn-sm btn-block" href="tel:'.$r[$field].'" target="_blank"><i class="fa fa-phone text-primary"></i> '.$r[$field].'</a>' : '');
261+
}
262+
263+
// Link della colonna
264+
if ($field != '_print_' && !preg_match('/^tel_(.+?)$/', trim($field), $m) && !preg_match('/^mailto_(.+?)$/', trim($field), $m)) {
265+
$id_record = $r['id'];
266+
$hash = '';
267+
268+
$id_module = !empty($r['_link_module_']) ? $r['_link_module_'] : $id_module;
269+
if (!empty($r['_link_record_'])) {
270+
$id_record = $r['_link_record_'];
271+
$hash = !empty($r['_link_hash_']) ? '#'.$r['_link_hash_'] : '';
272+
unset($id_plugin);
273+
}
274+
275+
// Link per i moduli
276+
if (empty($id_plugin)) {
277+
$column['data-link'] = base_path_osm().'/editor.php?id_module='.$id_module.'&id_record='.$id_record.$hash;
278+
}
279+
// Link per i plugin
280+
else {
281+
$column['data-link'] = base_path_osm().'/add.php?id_module='.$id_module.'&id_record='.$id_record.'&id_plugin='.$id_plugin.'&id_parent='.$id_parent.'&edit=1'.$hash;
282+
283+
$column['data-type'] = 'dialog';
284+
}
285+
}
286+
287+
$attributes = [];
288+
foreach ($column as $key => $val) {
289+
$val = is_array($val) ? implode(' ', $val) : $val;
290+
$attributes[] = $key.'="'.$val.'"';
291+
}
292+
293+
// Replace base_link() per le query
294+
$value = str_replace('base_link()', base_path_osm(), $value);
295+
$result[] = str_replace('|attr|', implode(' ', $attributes), '<div |attr|>'.$value.'</div>');
296+
}
297+
298+
return $result;
299+
}
300+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace DTO\DataTablesLoadRequest;
5+
6+
final class Column
7+
{
8+
/**
9+
* @param string $data
10+
* @param string|null $name
11+
* @param bool $searchable
12+
* @param bool $orderable
13+
* @param Search|null $search
14+
*/
15+
public function __construct(
16+
public string $data,
17+
public ?string $name = null,
18+
public bool $searchable = true,
19+
public bool $orderable = true,
20+
public ?Search $search = null
21+
){}
22+
23+
public static function fromArray(array $input = []): self
24+
{
25+
$data = isset($input['data']) ? (string)$input['data'] : '';
26+
$name = isset($input['name']) ? (string)$input['name'] : null;
27+
$searchable = isset($input['searchable']) ? filter_var($input['searchable'], FILTER_VALIDATE_BOOLEAN) : true;
28+
$orderable = isset($input['orderable']) ? filter_var($input['orderable'], FILTER_VALIDATE_BOOLEAN) : true;
29+
$search = isset($input['search']) && is_array($input['search'])
30+
? Search::fromArray($input['search'])
31+
: null;
32+
return new self($data, $name, $searchable, $orderable, $search);
33+
}
34+
35+
public function getData(): string { return $this->data; }
36+
public function getName(): ?string { return $this->name; }
37+
public function isSearchable(): bool { return $this->searchable; }
38+
public function isOrderable(): bool { return $this->orderable; }
39+
public function getSearch(): ?Search { return $this->search; }
40+
41+
public function toArray(): array
42+
{
43+
return [
44+
'data' => $this->data,
45+
'name' => $this->name,
46+
'searchable' => $this->searchable,
47+
'orderable' => $this->orderable,
48+
'search' => $this->search->toArray(),
49+
];
50+
}
51+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace DTO\DataTablesLoadRequest;
5+
6+
final class DataTablesLoadRequest
7+
{
8+
public int $draw;
9+
public int $start;
10+
public int $length;
11+
/**
12+
* @var Search
13+
*/
14+
public array $search; // Deserialization is not working correctly
15+
/**
16+
* @var OrderItem[]
17+
*/
18+
public array $order = []; // Deserialization is not working correctly
19+
/**
20+
* @var Column[]
21+
*/
22+
public array $columns = []; // Deserialization is not working correctly
23+
public ?string $_ = null;
24+
25+
public function getDraw(): int { return $this->draw; }
26+
public function getStart(): int { return $this->start; }
27+
public function getLength(): int { return $this->length; }
28+
public function getSearch(): Search { return $this->search; }
29+
/** @return OrderItem[] */
30+
public function getOrder(): array { return $this->order; }
31+
/** @return Column[] */
32+
public function getColumns(): array { return $this->columns; }
33+
34+
public function toArray(): array
35+
{
36+
return [
37+
'draw' => $this->draw,
38+
'start' => $this->start,
39+
'length' => $this->length,
40+
'search' => $this->search->toArray(),
41+
'order' => array_map(function (OrderItem $o) { return $o->toArray(); }, $this->order),
42+
'columns' => array_map(function (Column $c) { return $c->toArray(); }, $this->columns),
43+
'_' => $this->_,
44+
];
45+
}
46+
}

0 commit comments

Comments
 (0)