Getting started
Install the package, create a report module and open the preview. This takes about five minutes.
Install the package
The package is distributed as a Composer library. It ships a service provider, a config file and Artisan commands.
composer require guggach/laravel-reports
Create a report
A report lives in a self-contained module: the report class, its data source and the Blade views next to it.
app/Reports/PriceList/
├── Report.php # bands, groups, filters
├── Source.php # data + field definitions
└── views/
├── report-start.blade.php
├── grid-head.blade.php
├── row.blade.php
├── category-head.blade.php
├── category-foot.blade.php
└── report-end.blade.php
<?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');
});
}
}
<?php
declare(strict_types=1);
namespace App\Reports\PriceList;
use Guggach\Reports\Sources\ReportField;
use Guggach\Reports\Sources\ReportSource;
/**
* Data source of the price list. Report-local on purpose (spec 7.1): a source
* is never shared between reports. Filters are applied in memory here; a
* database-backed variant would push them down instead.
*/
final class Source extends ReportSource
{
public function records(): iterable
{
return $this->applyFilters([
['sku' => 'A-100', 'name' => 'Kaffeemaschine', 'category' => 'Küche', 'supplier' => 'Nordwind AG', 'price' => 129.00],
['sku' => 'B-200', 'name' => 'Wasserkocher', 'category' => 'Küche', 'supplier' => 'Nordwind AG', 'price' => 39.90],
['sku' => 'C-300', 'name' => 'Toaster', 'category' => 'Büro', 'supplier' => 'Berger GmbH', 'price' => 59.50],
['sku' => 'D-400', 'name' => 'Mixer', 'category' => 'Büro', 'supplier' => 'Conti AG', 'price' => 89.00],
]);
}
public function fields(): array
{
return [
new ReportField('sku', 'SKU', type: 'string'),
new ReportField('name', 'Bezeichnung', type: 'string'),
new ReportField('category', 'Kategorie', type: 'enum', options: ['Küche', 'Büro']),
new ReportField('supplier', 'Lieferant', type: 'relation', options: [
'Nordwind AG', 'Berger GmbH', 'Conti AG', 'Delta Handels',
'Egger & Co', 'Fuchs AG', 'Graf GmbH', 'Huber AG',
]),
new ReportField('price', 'Preis', type: 'number'),
];
}
}
<h1 style="margin:0 0 12px">{{ $ctx->report->name() }}</h1>
Open the preview
Reports are addressed by their key. The package registers the preview, the document and the PDF route for you.