SQL UNION mit “Bitte wählen” Option
Quell-ID: GitHub Discussion #43
Use Case
In einem YForm-Select-Feld soll eine “Bitte wählen”-Option als erste Auswahl erscheinen, gefolgt von den alphabetisch sortierten Datenbankeinträgen.
Verwendete AddOns
- YForm
- REDAXO Core
Problemstellung
Bei einem SQL-Query mit UNION und ORDER BY wird die erste Option nicht immer an erster Stelle angezeigt.
Lösung
Die Klammern um das zweite SELECT mit ORDER BY sorgen dafür, dass die Sortierung nur auf diesen Teil angewendet wird:
SELECT 'Bitte wählen' AS name, "" AS id
UNION
(SELECT name, id AS id FROM rex_product_groups ORDER BY name)
Anwendung in YForm
Pipe-Schreibweise
select|product_group|Produktgruppe|SELECT 'Bitte wählen' AS name, "" AS id UNION (SELECT name, id FROM rex_product_groups ORDER BY name)|0|0|
PHP-Schreibweise
$yform->setValueField('select', [
'product_group',
'Produktgruppe',
"SELECT 'Bitte wählen' AS name, '' AS id UNION (SELECT name, id FROM rex_product_groups ORDER BY name)",
0,
0
]);
Erklärung
Ohne Klammern würde ORDER BY auf das gesamte UNION-Ergebnis angewendet, wodurch “Bitte wählen” möglicherweise nicht mehr an erster Stelle steht.
Mit Klammern (SELECT ... ORDER BY name) wird:
- Der erste SELECT (“Bitte wählen”) zuerst ausgegeben
- Der zweite SELECT intern sortiert
- Die Ergebnisse in dieser Reihenfolge vereinigt
Weitere Beispiele
Mit Leer-ID als Integer
SELECT 'Bitte wählen' AS name, 0 AS id
UNION
(SELECT name, id FROM rex_categories WHERE status = 1 ORDER BY name)
Mit Standard-Auswahl markiert
SELECT '-- Keine Auswahl --' AS name, '' AS id
UNION
(SELECT CONCAT(name, IF(is_default=1, ' (Standard)', '')) AS name, id
FROM rex_options ORDER BY prio)
Mehrere Tabellen kombinieren
SELECT 'Bitte wählen' AS name, '' AS id, '' AS type
UNION
(SELECT name, id, 'intern' AS type FROM rex_internal_contacts ORDER BY name)
UNION
(SELECT name, id, 'extern' AS type FROM rex_external_contacts ORDER BY name)
Besserer Ansatz
Als Choice-Feld mit PHP-Array
<?php
// Optionen aus Datenbank holen
$sql = rex_sql::factory();
$options = $sql->getArray('SELECT id, name FROM rex_product_groups ORDER BY name');
// Als Array für Choice-Feld aufbereiten
$choices = ['Bitte wählen' => ''];
foreach ($options as $option) {
$choices[$option['name']] = $option['id'];
}
$yform->setValueField('choice', [
'product_group',
'Produktgruppe',
$choices
]);
Mit Model-Klasse
<?php
// In Model-Klasse
class ProductGroup extends \rex_yform_manager_dataset
{
public static function getSelectOptions(string $placeholder = 'Bitte wählen'): array
{
$options = [$placeholder => ''];
$groups = self::query()
->orderBy('name')
->find();
foreach ($groups as $group) {
$options[$group->getValue('name')] = $group->getId();
}
return $options;
}
}
// Verwendung
$yform->setValueField('choice', [
'product_group',
'Produktgruppe',
ProductGroup::getSelectOptions()
]);
Helper für häufige Verwendung
<?php
class SelectHelper
{
public static function withPlaceholder(
string $table,
string $labelField = 'name',
string $valueField = 'id',
string $placeholder = 'Bitte wählen',
string $orderBy = 'name',
string $where = ''
): string {
$sql = "SELECT '$placeholder' AS name, '' AS id UNION ";
$sql .= "(SELECT $labelField AS name, $valueField AS id FROM " . rex::getTable(ltrim($table, 'rex_'));
if ($where) {
$sql .= " WHERE $where";
}
// Validiere ORDER BY um SQL-Injection zu vermeiden
if (!preg_match('/^[a-zA-Z0-9_]+(\s+(ASC|DESC))?$/i', $orderBy)) {
throw new InvalidArgumentException('Invalid ORDER BY value provided.');
}
// Validate ORDER BY to avoid SQL injection via the $orderBy parameter
if (!preg_match('/^[a-zA-Z0-9_]+(\s+(ASC|DESC))?$/', $orderBy)) {
throw new InvalidArgumentException('Invalid ORDER BY value provided.');
}
$sql .= " ORDER BY " . $orderBy . ")";
return $sql;
}
}
// Verwendung
$yform->setValueField('select', [
'category',
'Kategorie',
SelectHelper::withPlaceholder('rex_categories', 'name', 'id', '-- Bitte wählen --', 'name', 'status = 1')
]);
Hinweis
Die UNION-Methode ist einfach und direkt in SQL möglich. Für komplexere Szenarien (z.B. mit Berechtigungsprüfung oder dynamischen Optionen) empfiehlt sich die PHP-Array-Variante.