/

Individuelle SQL-Reports

Ein Grid mit einem beliebigen, schreibgeschützten SELECT-Statement über query() betreiben - für Reports, die sich nicht auf eine einzelne Tabelle abbilden lassen.

table() vs. query()

->table($name) betreibt ein Grid mit einer echten, beschreibbaren Tabelle. ->query($sql) ist die Alternative für alles, was sich nicht auf eine einzelne Tabelle abbilden lässt - Joins, Aggregate, berechnete Spalten über mehrere Tabellen hinweg:

public function query(string $sql): self

$sql wird unverändert übernommen und von der API-Schicht als abgeleitete Tabelle (Derived Table) eingebunden - dieselbe Vertrauensstufe, die auch die rohen SQL-Escape-Hatches von where_raw()/subselect() haben. Es muss ein einfacher String sein, den Sie selbst im PHP-Code Ihrer Seite geschrieben haben - niemals etwas, das aus Request-Eingaben zusammengebaut wird.

Der Report-Modus ist schreibgeschützt

Ein Grid im Query-Modus hat kein Hinzufügen/Bearbeiten/Löschen, keine Bulk-Aktionen und kein Inline-Editing - es ist von Natur aus schreibgeschützt, da es keine einzelne Tabelle gibt, auf die ein Schreibvorgang eindeutig zielen könnte. Dies wird sowohl clientseitig als auch serverseitig durchgesetzt: Die API lehnt POST/PUT/DELETE gegen eine Route im Query-Modus rundheraus ab, unabhängig davon, was der Client sendet. Nur Auflisten/Suchen/Sortieren/Paginieren/Exportieren bleiben übrig.

columns() und route() sind weiterhin erforderlich

Ein Grid im Query-Modus öffnet nie eine eigene Datenbankverbindung, um Spalten zu ermitteln - seine Spaltenliste ist ausschließlich das, was ->columns() vorgibt. ->route() ist ebenfalls erforderlich, da es keinen Tabellennamen gibt, aus dem sich eine Route ableiten ließe.

Ein Beispiel mit JOIN

$xcrud = Xcrud::get_instance();
$xcrud->route('order-summary');
$xcrud->query(
    'SELECT o.orderNumber, o.orderDate, o.status, c.customerName, c.country,
            COUNT(od.id) AS itemCount,
            SUM(od.quantityOrdered * od.priceEach) AS orderTotal
     FROM orders o
     JOIN customers c ON c.customerNumber = o.customerNumber
     JOIN orderdetails od ON od.orderNumber = o.orderNumber
     GROUP BY o.orderNumber, o.orderDate, o.status, c.customerName, c.country'
);
$xcrud->columns('orderNumber,orderDate,status,customerName,country,itemCount,orderTotal');
$xcrud->order_by('orderTotal', 'desc');
$xcrud->title('Order Summary Report');

echo $xcrud->render();

Die Spaltenaliase in $sql und die Namen in ->columns() werden nie gegeneinander geprüft (beim Rendern findet kein Datenbankzugriff statt) - eine Abweichung zeigt sich erst als Fehler bei der ersten echten Anfrage an das Grid, nicht als Render-Zeit-Exception wie es ein unbekannter Spaltenname in order_by()/label()/where() auslösen würde. Prüfen Sie die beiden Listen von Hand gegen.

Ein abschließendes ORDER BY innerhalb von $sql ohne LIMIT kann beim Einbetten als abgeleitete Tabelle stillschweigend entfallen - unproblematisch, da die eigene Sortierung des Grids bzw. ->order_by() ohnehin stets ein äußeres ORDER BY anwendet, aber verlassen Sie sich nicht darauf, dass ein inneres von selbst etwas bewirkt.