Skip to content

Commit e24d220

Browse files
Merge pull request #18218 from MoonE/gis-visualization-settings
Refactor GisVisualization settings
2 parents 2597a13 + 4f36db3 commit e24d220

9 files changed

Lines changed: 371 additions & 576 deletions

File tree

libraries/classes/Controllers/GisDataEditorController.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ public function __invoke(ServerRequest $request): void
103103
'width' => 450,
104104
'height' => 300,
105105
'spatialColumn' => 'wkt',
106-
'mysqlVersion' => $GLOBALS['dbi']->getVersion(),
107-
'isMariaDB' => $GLOBALS['dbi']->isMariaDB(),
108106
];
109107
$data = [
110108
[
@@ -138,8 +136,8 @@ public function __invoke(ServerRequest $request): void
138136
}
139137

140138
$templateOutput = $this->template->render('gis_data_editor_form', [
141-
'width' => $visualizationSettings['width'],
142-
'height' => $visualizationSettings['height'],
139+
'width' => $visualization->getWidth(),
140+
'height' => $visualization->getHeight(),
143141
'field' => $field,
144142
'input_name' => $inputName,
145143
'srid' => $srid,

libraries/classes/Controllers/Table/GisVisualizationController.php

Lines changed: 134 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PhpMyAdmin\Controllers\AbstractController;
88
use PhpMyAdmin\Core;
99
use PhpMyAdmin\DatabaseInterface;
10+
use PhpMyAdmin\FieldMetadata;
1011
use PhpMyAdmin\Gis\GisVisualization;
1112
use PhpMyAdmin\Html\Generator;
1213
use PhpMyAdmin\Http\ServerRequest;
@@ -17,16 +18,15 @@
1718
use PhpMyAdmin\Util;
1819

1920
use function __;
20-
use function array_merge;
21+
use function in_array;
2122
use function is_array;
23+
use function is_string;
2224

2325
/**
2426
* Handles creation of the GIS visualizations.
2527
*/
2628
final class GisVisualizationController extends AbstractController
2729
{
28-
private GisVisualization $visualization;
29-
3030
public function __construct(
3131
ResponseRenderer $response,
3232
Template $template,
@@ -37,8 +37,6 @@ public function __construct(
3737

3838
public function __invoke(ServerRequest $request): void
3939
{
40-
$GLOBALS['urlParams'] ??= null;
41-
$GLOBALS['errorUrl'] ??= null;
4240
$this->checkParameters(['db']);
4341

4442
$GLOBALS['errorUrl'] = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabDatabase'], 'database');
@@ -49,17 +47,10 @@ public function __invoke(ServerRequest $request): void
4947
}
5048

5149
// SQL query for retrieving GIS data
52-
$sqlQuery = '';
53-
if (isset($_GET['sql_query'], $_GET['sql_signature'])) {
54-
if (Core::checkSqlQuerySignature($_GET['sql_query'], $_GET['sql_signature'])) {
55-
$sqlQuery = $_GET['sql_query'];
56-
}
57-
} elseif (isset($_POST['sql_query'])) {
58-
$sqlQuery = $_POST['sql_query'];
59-
}
50+
$sqlQuery = $this->getSqlQuery();
6051

6152
// Throw error if no sql query is set
62-
if ($sqlQuery == '') {
53+
if ($sqlQuery === null) {
6354
$this->response->setRequestStatus(false);
6455
$this->response->addHTML(
6556
Message::error(__('No SQL query was set to fetch data.'))->getDisplay(),
@@ -68,129 +59,180 @@ public function __invoke(ServerRequest $request): void
6859
return;
6960
}
7061

71-
// Execute the query and return the result
72-
$result = $this->dbi->tryQuery($sqlQuery);
73-
// Get the meta data of results
74-
$meta = [];
75-
if ($result !== false) {
76-
$meta = $this->dbi->getFieldsMeta($result);
77-
}
62+
$meta = $this->getColumnMeta($sqlQuery);
7863

7964
// Find the candidate fields for label column and spatial column
8065
$labelCandidates = [];
8166
$spatialCandidates = [];
8267
foreach ($meta as $column_meta) {
68+
if ($column_meta->name === '') {
69+
continue;
70+
}
71+
8372
if ($column_meta->isMappedTypeGeometry) {
8473
$spatialCandidates[] = $column_meta->name;
8574
} else {
8675
$labelCandidates[] = $column_meta->name;
8776
}
8877
}
8978

90-
// Get settings if any posted
91-
$visualizationSettings = [];
92-
// Download as PNG/SVG/PDF use _GET and the normal form uses _POST
93-
if (isset($_POST['visualizationSettings']) && is_array($_POST['visualizationSettings'])) {
94-
$visualizationSettings = $_POST['visualizationSettings'];
95-
} elseif (isset($_GET['visualizationSettings']) && is_array($_GET['visualizationSettings'])) {
96-
$visualizationSettings = $_GET['visualizationSettings'];
97-
}
98-
99-
// Check mysql version
100-
$visualizationSettings['mysqlVersion'] = $this->dbi->getVersion();
101-
$visualizationSettings['isMariaDB'] = $this->dbi->isMariaDB();
79+
if ($spatialCandidates === []) {
80+
$this->response->setRequestStatus(false);
81+
$this->response->addHTML(
82+
Message::error(__('No spatial column found for this SQL query.'))->getDisplay(),
83+
);
10284

103-
if (! isset($visualizationSettings['labelColumn']) && isset($labelCandidates[0])) {
104-
$visualizationSettings['labelColumn'] = '';
85+
return;
10586
}
10687

107-
// If spatial column is not set, use first geometric column as spatial column
108-
if (! isset($visualizationSettings['spatialColumn'])) {
109-
$visualizationSettings['spatialColumn'] = $spatialCandidates[0];
110-
}
88+
// Get settings if any posted
89+
$visualizationSettings = $this->getVisualizationSettings($spatialCandidates, $labelCandidates);
90+
$visualizationSettings['width'] = 600;
91+
$visualizationSettings['height'] = 450;
11192

112-
// Download as PNG/SVG/PDF use _GET and the normal form uses _POST
113-
// Convert geometric columns from bytes to text.
114-
$pos = (int) ($_POST['pos'] ?? $_GET['pos'] ?? $_SESSION['tmpval']['pos']);
115-
if (isset($_POST['session_max_rows']) || isset($_GET['session_max_rows'])) {
116-
$rows = (int) ($_POST['session_max_rows'] ?? $_GET['session_max_rows']);
117-
} else {
118-
if ($_SESSION['tmpval']['max_rows'] !== 'all') {
119-
$rows = (int) $_SESSION['tmpval']['max_rows'];
120-
} else {
121-
$rows = (int) $GLOBALS['cfg']['MaxRows'];
122-
}
123-
}
93+
$rows = $this->getRows();
94+
$pos = $this->getPos();
12495

125-
$this->visualization = GisVisualization::get($sqlQuery, $visualizationSettings, $rows, $pos);
96+
$visualization = GisVisualization::get($sqlQuery, $visualizationSettings, $rows, $pos);
12697

12798
if (isset($_GET['saveToFile'])) {
128-
$this->saveToFile($visualizationSettings['spatialColumn'], $_GET['fileFormat']);
99+
$this->response->disable();
100+
$filename = $visualization->getSpatialColumn();
101+
$visualization->toFile($filename, $_GET['fileFormat']);
129102

130103
return;
131104
}
132105

133106
$this->addScriptFiles(['vendor/openlayers/OpenLayers.js', 'table/gis_visualization.js']);
134107

135108
// If all the rows contain SRID, use OpenStreetMaps on the initial loading.
136-
if (! isset($_POST['displayVisualization'])) {
137-
if ($this->visualization->hasSrid()) {
138-
$visualizationSettings['choice'] = 'useBaseLayer';
139-
} else {
140-
unset($visualizationSettings['choice']);
141-
}
142-
}
143-
144-
$this->visualization->setUserSpecifiedSettings($visualizationSettings);
145-
foreach ($this->visualization->getSettings() as $setting => $val) {
146-
if (isset($visualizationSettings[$setting])) {
147-
continue;
148-
}
149-
150-
$visualizationSettings[$setting] = $val;
151-
}
109+
$useBaseLayer = isset($_POST['redraw']) ? isset($_POST['useBaseLayer']) : $visualization->hasSrid();
152110

153111
/**
154112
* Displays the page
155113
*/
156-
$GLOBALS['urlParams']['goto'] = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabDatabase'], 'database');
157-
$GLOBALS['urlParams']['back'] = Url::getFromRoute('/sql');
158-
$GLOBALS['urlParams']['sql_query'] = $sqlQuery;
159-
$GLOBALS['urlParams']['sql_signature'] = Core::signSqlQuery($sqlQuery);
160-
$downloadUrl = Url::getFromRoute('/table/gis-visualization', array_merge(
161-
$GLOBALS['urlParams'],
162-
[
163-
'saveToFile' => true,
164-
'session_max_rows' => $rows,
165-
'pos' => $pos,
166-
'visualizationSettings[spatialColumn]' => $visualizationSettings['spatialColumn'],
167-
'visualizationSettings[labelColumn]' => $visualizationSettings['labelColumn'] ?? null,
168-
],
169-
));
114+
$urlParams = $GLOBALS['urlParams'] ?? [];
115+
$urlParams['goto'] = Util::getScriptNameForOption($GLOBALS['cfg']['DefaultTabDatabase'], 'database');
116+
$urlParams['back'] = Url::getFromRoute('/sql');
117+
$urlParams['sql_query'] = $sqlQuery;
118+
$urlParams['sql_signature'] = Core::signSqlQuery($sqlQuery);
119+
$downloadParams = [
120+
'saveToFile' => true,
121+
'session_max_rows' => $visualization->getRows(),
122+
'pos' => $visualization->getPos(),
123+
'visualizationSettings[spatialColumn]' => $visualization->getSpatialColumn(),
124+
'visualizationSettings[labelColumn]' => $visualization->getLabelColumn(),
125+
];
126+
$downloadUrl = Url::getFromRoute('/table/gis-visualization', $downloadParams + $urlParams);
170127

171128
$startAndNumberOfRowsFieldset = Generator::getStartAndNumberOfRowsFieldsetData($sqlQuery);
172129

173130
$html = $this->template->render('table/gis_visualization/gis_visualization', [
174-
'url_params' => $GLOBALS['urlParams'],
131+
'url_params' => $urlParams,
175132
'download_url' => $downloadUrl,
176133
'label_candidates' => $labelCandidates,
177134
'spatial_candidates' => $spatialCandidates,
178-
'visualization_settings' => $visualizationSettings,
135+
'spatialColumn' => $visualization->getSpatialColumn(),
136+
'labelColumn' => $visualization->getLabelColumn(),
137+
'width' => $visualization->getWidth(),
138+
'height' => $visualization->getHeight(),
179139
'start_and_number_of_rows_fieldset' => $startAndNumberOfRowsFieldset,
180-
'visualization' => $this->visualization->toImage('svg'),
181-
'draw_ol' => $this->visualization->asOl(),
140+
'useBaseLayer' => $useBaseLayer,
141+
'visualization' => $visualization->asSVG(),
142+
'draw_ol' => $visualization->asOl(),
182143
]);
183144

184145
$this->response->addHTML($html);
185146
}
186147

187148
/**
188-
* @param string $filename File name
189-
* @param string $format Save format
149+
* Reads the sql query from POST or GET
150+
*
151+
* @psalm-return non-empty-string|null
152+
*/
153+
private function getSqlQuery(): string|null
154+
{
155+
$getQuery = $_GET['sql_query'] ?? null;
156+
$getSignature = $_GET['sql_signature'] ?? null;
157+
$postQuery = $_POST['sql_query'] ?? null;
158+
159+
$sqlQuery = null;
160+
if (is_string($getQuery) && is_string($getSignature)) {
161+
if (Core::checkSqlQuerySignature($getQuery, $getSignature)) {
162+
$sqlQuery = $getQuery;
163+
}
164+
} elseif (is_string($postQuery)) {
165+
$sqlQuery = $postQuery;
166+
}
167+
168+
return $sqlQuery === '' ? null : $sqlQuery;
169+
}
170+
171+
/**
172+
* @param string[] $spatialCandidates
173+
* @param string[] $labelCandidates
174+
* @psalm-param non-empty-list<non-empty-string> $spatialCandidates
175+
* @psalm-param list<non-empty-string> $labelCandidates
176+
*
177+
* @return mixed[];
178+
* @psalm-return array{spatialColumn:non-empty-string,labelColumn?:non-empty-string}
190179
*/
191-
private function saveToFile(string $filename, string $format): void
180+
private function getVisualizationSettings(array $spatialCandidates, array $labelCandidates): array
181+
{
182+
$settingsIn = [];
183+
// Download as PNG/SVG/PDF use _GET and the normal form uses _POST
184+
if (is_array($_POST['visualizationSettings'] ?? null)) {
185+
/** @var mixed[] $settingsIn */
186+
$settingsIn = $_POST['visualizationSettings'];
187+
} elseif (is_array($_GET['visualizationSettings'] ?? null)) {
188+
/** @var mixed[] $settingsIn */
189+
$settingsIn = $_GET['visualizationSettings'];
190+
}
191+
192+
$settings = [];
193+
if (
194+
isset($settingsIn['labelColumn']) &&
195+
in_array($settingsIn['labelColumn'], $labelCandidates, true)
196+
) {
197+
$settings['labelColumn'] = $settingsIn['labelColumn'];
198+
}
199+
200+
// If spatial column is not set, use first geometric column as spatial column
201+
$spatialColumnValid = isset($settingsIn['spatialColumn']) &&
202+
in_array($settingsIn['spatialColumn'], $spatialCandidates, true);
203+
$settings['spatialColumn'] = $spatialColumnValid ? $settingsIn['spatialColumn'] : $spatialCandidates[0];
204+
205+
return $settings;
206+
}
207+
208+
private function getPos(): int
192209
{
193-
$this->response->disable();
194-
$this->visualization->toFile($filename, $format);
210+
// Download as PNG/SVG/PDF use _GET and the normal form uses _POST
211+
return (int) ($_POST['pos'] ?? $_GET['pos'] ?? $_SESSION['tmpval']['pos']);
212+
}
213+
214+
private function getRows(): int
215+
{
216+
if (isset($_POST['session_max_rows']) || isset($_GET['session_max_rows'])) {
217+
return (int) ($_POST['session_max_rows'] ?? $_GET['session_max_rows']);
218+
}
219+
220+
if ($_SESSION['tmpval']['max_rows'] === 'all') {
221+
return (int) $GLOBALS['cfg']['MaxRows'];
222+
}
223+
224+
return (int) $_SESSION['tmpval']['max_rows'];
225+
}
226+
227+
/**
228+
* Execute the query and return the result
229+
*
230+
* @return FieldMetadata[]
231+
*/
232+
private function getColumnMeta(string $sqlQuery): array
233+
{
234+
$result = $this->dbi->tryQuery($sqlQuery);
235+
236+
return $result === false ? [] : $this->dbi->getFieldsMeta($result);
195237
}
196238
}

0 commit comments

Comments
 (0)