Bänder erklärt
Jeder Report ist ein Stapel von Bändern über die volle Seitenbreite. An der Preisliste siehst du, welches Band was rendert — und wie oft.
Die Preisliste, Band für Band
Die Preisliste definiert ein Report-Start-Band mit dem Titel, einen Tabellenkopf, ein Detailband, eine Gruppe mit Kopf und Fuss sowie ein Report-Ende-Band mit der Zusammenfassung. Die gerenderte Seite ist das Ergebnis dieser Bänder, die in fester Reihenfolge laufen.
Legende
-
L
Layout (L)
Briefkopf und Adresszeile kommen aus CompanyLayout. Das Layout umschliesst jeden Report, ist aber kein Band.
-
1
Report-Start / Titel (1)
Läuft genau einmal am Beginn des Reports. Hier gehört der Report-Titel hin. Auf Seite 2, 3 und so weiter wird das Band nicht wiederholt.
-
2
Tabellenkopf (2)
Leitet die Detailzeilen ein. Hier öffnet er die Tabelle und rendert die Spaltenköpfe; im Strict-Modus wird er zuoberst auf jeder Seite wiederholt.
-
3
Gruppenkopf (3)
Läuft einmal, wenn sich der Gruppenschlüssel ändert. Die Preisliste gruppiert nach Kategorie, darum bekommen „Küche“ und „Büro“ je einen Kopf.
-
4
Detailband (4)
Läuft einmal pro Datensatz. Jede Preislisten-Zeile ist ein Detaildurchlauf; das Band rendert immer dieselbe Blade-View.
-
5
Gruppenfuss (5)
Läuft am Ende einer Gruppe und kann deren Aggregate lesen. Hier druckt er die Zwischensumme der Kategorie.
-
6
Report-Ende / Zusammenfassung (6)
Läuft genau einmal nach dem letzten Datensatz und hat Zugriff auf die Report-Aggregate. Hier schliesst es die Tabelle und druckt die Anzahl Positionen. Auf späteren Seiten wird es nicht wiederholt.
Der Bänderstapel
Von aussen nach innen setzt die Engine einen Report in dieser Reihenfolge zusammen. Nicht definierte Bänder werden übersprungen.
pageHeader()
reportStart()
gridHeader()
group()->header()
detail()
group()->footer()
reportEnd()
pageFooter()
Report-Bänder laufen einmal — Seitenbänder wiederholen sich
reportStart() und reportEnd() umschliessen den ganzen Report. Sie laufen genau einmal, egal über wie viele Seiten der Report geht. Nur pageHeader(), gridHeader() und pageFooter() werden im Strict-Modus bei einem neuen Seitenanfang wiederholt.
Bänder über Seitenumbrüche hinweg
Der Lieferschein macht die Wiederholung sichtbar. Er verwendet den Strict-Modus, in dem jedes Band eine fixe Höhe hat. Seitenkopf, Tabellenkopf und Seitenfuss werden auf jeder Seite wiederholt; das Detailband fährt mit den nächsten Datensätzen fort.
Von der Definition zur Blade-View
Die Report-Klasse entscheidet, welche Bänder es gibt und welche View sie rendert. Die Blade-Views rendern nur ihren Ausschnitt; die Engine entscheidet, wie oft dieser Ausschnitt läuft.
<?php
declare(strict_types=1);
namespace App\Reports\PriceList;
use App\Reports\Layout\CompanyLayout;
use Guggach\Reports\Definition\ReportBuilder;
use Guggach\Reports\Filters\FilterBuilder;
use Guggach\Reports\Report as BaseReport;
use Guggach\Reports\Sources\ReportSource;
final class Report extends BaseReport
{
public function key(): string
{
return 'price_list';
}
public function name(): string
{
return 'Preisliste';
}
public function layout(): string
{
return CompanyLayout::class;
}
public function source(): ReportSource
{
return new Source;
}
/**
* Demonstrate the fluent control description: operator listbox for the
* number, multiselect for the enum and a searchable dual-list for the
* relation (id/FK) field.
*/
public function filterControls(): array
{
return FilterBuilder::for($this->source())
->field('price')->operators(['=', '>=', '<=', 'between', 'in'])
->field('category')->multiselect()
->field('supplier')->picker(valueField: 'id', labelField: 'label')
->build();
}
public function define(ReportBuilder $r): void
{
// Module-relative view names resolve to app/Reports/PriceList/views/*.
// Report start/end run once per report; the grid header introduces the
// table and the detail band renders one row per record.
$r->reportStart(view: 'report-start');
$r->gridHeader(view: 'grid-head');
$r->detail(view: 'row');
$r->reportEnd(view: 'report-end');
// Grouped by category (data is sorted by category) with a subtotal.
$r->group('category', function ($g): void {
$g->header(view: 'category-head');
$g->footer(view: 'category-foot');
$g->aggSum('price', as: 'category_total');
});
}
}
<h1 style="margin:0 0 12px">{{ $ctx->report->name() }}</h1>
<table style="width:100%; border-collapse:collapse">
<thead>
<tr style="border-bottom:2px solid #111">
<th style="text-align:left">SKU</th>
<th style="text-align:left">Bezeichnung</th>
<th style="text-align:right">Preis (CHF)</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid #ddd">
<td>{{ $ctx->sku }}</td>
<td>{{ $ctx->name }}</td>
<td style="text-align:right">{{ number_format((float) $ctx->price, 2, '.', "'") }}</td>
</tr>
</tbody>
</table>
<p style="margin-top:12px; color:#555">{{ $ctx->total }} Positionen</p>
Direkt aus dem laufenden Modul gelesen — der Code passt zur Vorschau.