Routing & invocation

A report is addressed by its key(). The package registers the filter form, preview, document and PDF as separate routes — link them or invoke them directly from code.

The report routes

All routes use the prefix reports by default. The route names stay the same when you change the prefix.

GET /reports/{name} · reports.form Filter form. If the report has no filter controls, the route redirects straight to the preview.
GET /reports/{name}/run · reports.preview Preview page with the frame, zoom, print, PDF and “change filters” link. Filters arrive as query parameters.
GET /reports/internal/{name} · reports.internal Only the report document as HTML — for your own iframe or an embed without the preview chrome.
GET /reports/{name}/pdf · reports.pdf Streams the PDF with the same filters.
GET /reports/assets/{file} · reports.asset Shared CSS file for the form and the preview. The UI components include it automatically.

Form first — or straight to the preview

The link to /reports/{name} shows the filter form first. If there are no filters (empty filterControls()), the route redirects automatically to /reports/{name}/run. To skip the form, link the preview route directly.

The flow

The report is only executed once the filters are fixed. The preview is just the shell; the actual document lives on the internal route.

reports.form

Choose filters

The user fills in the form. The values become query parameters on the form target.

reports.preview

Preview

The route validates the filters and renders the frame. The iframe loads the document from reports.internal.

reports.internal · reports.pdf

Document or PDF

Same filters, two outputs: HTML for embedding or a streamed PDF.

Generating links

Use the route names instead of hard-coded paths. That keeps you independent of the configured prefix.

{{-- 1. Filter form first: the user chooses filters, then continues. --}}
<a href="{{ route('reports.form', ['name' => 'price_list']) }}">Preisliste öffnen</a>

{{-- 2. Direct preview, with filters in the query string. --}}
@php($filters = [
    'category' => 'Küche',
    'price' => ['operator' => '>=', 'value' => [50]],
])
<a href="{{ route('reports.preview', ['name' => 'price_list']).'?'.http_build_query($filters) }}">Vorschau</a>

{{-- 3. The raw report document, e.g. for your own iframe. --}}
<iframe src="{{ route('reports.internal', ['name' => 'price_list']).'?'.http_build_query($filters) }}"></iframe>

{{-- 4. Streamed PDF with the same filters. --}}
<a href="{{ route('reports.pdf', ['name' => 'price_list']).'?'.http_build_query($filters) }}">PDF</a>

Four typical links: form, preview, embedded document and PDF.

Careful: a filter field called “name”

The route has a {name} parameter for the report key. A filter field keyed name would overwrite the report key with route(..., ["name" => ...] + $filters). Always append filters as their own query string: route(...)."?".http_build_query($filters). The package routes do the same internally.

Filters as query parameters

The package routes accept the same shapes the automatic form produces. Unknown keys are dropped.

?category=Küche Single value. The default operator of the control is used.
?category[]=Küche&category[]=Büro Multiple values for multiselect and picker.
?price[operator]=>=&price[value][]=50 Explicit operator with one value.
?price[operator]=between&price[value][]=50&price[value][]=100 Range with two values.
?name[operator]=like&name[value]=Kaffee Text filter. Without an operator, like is used and the value is wrapped in %.
?foo=bar Unknown keys are silently dropped by the FilterValidator.

Invoking from code

The Reports facade accepts the same report key and filter arrays. html() returns the complete document as a string; pdf() returns a fluent PDF builder.

Reports::html($report, $filters = []) Complete HTML document as a string — ideal for your own controllers or embeds.
Reports::render($report, $filters = []) Same pipeline; the configured renderer determines the return type.
Reports::pdf($report, $filters = []) PdfBuilder with stream(), download(), save(), name() and disk().
Reports::filterControls($report) A list of FilterControl objects for your own filter UI.
Reports::filterSchema($report) The same controls as JSON-ready arrays.
Reports::find($key) Resolve a report instance by its key().
Reports::all() · register() · resolve() Read the registry, register reports or pass an instance through.
Reports::definition($report) · pageSetup($report) The resolved ReportDefinition and effective page setup.
use Guggach\Reports\Facades\Reports;

// Full HTML document (layout + bands) as a string.
$html = Reports::html('price_list', ['category' => 'Küche']);

// Same pipeline; the configured renderer decides the return type.
$result = Reports::render('price_list', ['category' => 'Küche']);

// PDF: stream, download or save.
return Reports::pdf('price_list', ['category' => 'Küche'])
    ->name('preisliste.pdf')
    ->download();

// Filter metadata for a custom UI.
$controls = Reports::filterControls('price_list'); // FilterControl[]
$schema = Reports::filterSchema('price_list');     // array, JSON-ready
use Guggach\Reports\Facades\Reports;
use Illuminate\Http\Response;

final class PriceListController
{
    public function __invoke(): Response
    {
        return response(
            Reports::html('price_list', ['category' => 'Küche'])
        );
    }
}

Render a report from a controller or deliver it as a PDF.

Prefix, middleware and protection

The routes are enabled by default and run in the web middleware group. Add authentication or a policy for production.

'routes' => [
    'enabled' => true,
    'prefix' => 'reports',

    // Add your own middleware, e.g. auth or a report policy.
    'middleware' => ['web', 'auth'],
],

Adjust routing in config/reports.php.

reports.routes.prefix changes only the path, not the route names (reports.form, reports.preview, reports.internal, reports.pdf, reports.asset). Setting reports.routes.enabled = false disables the routes entirely; then call Reports::html()/pdf() from your own controllers.

Next · Report types How lists, forms and strict reports use the same routes and bands.