Installation
composer require bisonlab/reports-bundle
Add to config/bundles.php:
return [
// ...
BisonLab\ReportsBundle\BisonLabReportsBundle::class => ['all' => true],
];
First Use Case: Basic Report
Define a report service (e.g., src/Service/MyReportService.php):
namespace App\Service;
use BisonLab\ReportsBundle\Report\ReportInterface;
class MyReportService implements ReportInterface
{
public function getName(): string
{
return 'my_report';
}
public function getTitle(): string
{
return 'My Custom Report';
}
public function getContent(): string
{
return $this->renderData(); // Your logic here
}
}
Register the Service
Add to services.yaml:
services:
App\Service\MyReportService:
tags: [bisonlab.reports.report]
Access the Report Route to the report viewer:
// src/Controller/ReportController.php
use BisonLab\ReportsBundle\Controller\ReportController;
class ReportController extends ReportController
{
public function indexAction()
{
return $this->renderReport('my_report');
}
}
Visit /report/my_report in your browser.
Report Interface
Implement ReportInterface for all reports:
class UserActivityReport implements ReportInterface
{
public function getName(): string { return 'user_activity'; }
public function getTitle(): string { return 'User Activity Log'; }
public function getContent(): string { return $this->generateCsv(); }
}
Dependency Injection Inject services into reports:
class SalesReport implements ReportInterface
{
public function __construct(private SalesService $salesService) {}
public function getContent(): string
{
$data = $this->salesService->getMonthlySales();
return $this->renderTemplate($data);
}
}
Dynamic Data Binding
Use getParameters() to accept user input:
public function getParameters(): array
{
return [
'start_date' => '2023-01-01',
'end_date' => '2023-12-31',
];
}
Access via Request in controller:
$this->renderReport('sales', ['start_date' => $request->query->get('date')]);
Template Integration Use Twig for dynamic content:
public function getContent(): string
{
return $this->twig->render('reports/sales.html.twig', [
'data' => $this->fetchData(),
]);
}
Export Formats
Extend ReportInterface for custom exports:
class PdfReport implements ReportInterface
{
public function getContent(): string
{
return $this->dompdf->loadHtml($this->renderHtml())->output();
}
}
Service Tagging
bisonlab.reports.report will make them invisible to the bundle.services.yaml or via debug:container.Caching Reports
php bin/console cache:clear
# config/packages/bisonlab_reports.yaml
bisonlab_reports:
cache: false
Parameter Validation
getParameters(). Use Symfony Validator:
use Symfony\Component\Validator\Constraints as Assert;
public function getParameters(): array
{
return [
'date' => [
'value' => $request->query->get('date'),
'constraints' => [new Assert\DateTime()],
],
];
}
Route Conflicts
/report/{name}) may clash with existing routes.config/routes.yaml:
bisonlab_reports:
resource: "@BisonLabReportsBundle/Resources/config/routes.xml"
prefix: "/custom_prefix"
php bin/console debug:container bisonlab.reports.report
bisonlab_reports.yaml:
bisonlab_reports:
debug: true
Logs will appear in var/log/dev.log.Custom Storage Override storage engine (e.g., for database-backed reports):
// src/Storage/CustomReportStorage.php
class CustomReportStorage implements ReportStorageInterface
{
public function save(ReportInterface $report): void
{
// Custom logic (e.g., save to DB)
}
}
Register in services.yaml:
BisonLab\ReportsBundle\Storage\ReportStorageInterface: '@App\Storage\CustomReportStorage'
Event Listeners Hook into report lifecycle:
// src/EventListener/ReportListener.php
class ReportListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
ReportEvents::PRE_GENERATE => 'onPreGenerate',
];
}
public function onPreGenerate(ReportEvent $event): void
{
// Modify $event->getReport() before generation
}
}
Security Restrict report access:
// src/Security/Voter/ReportVoter.php
class ReportVoter extends Voter
{
protected function supports(string $attribute, $subject): bool
{
return $attribute === 'VIEW_REPORT' && $subject instanceof ReportInterface;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
return $token->getUser()->hasRole('ROLE_REPORT_USER');
}
}
Secure routes in ReportController:
public function indexAction(string $name, Request $request)
{
$this->denyAccessUnlessGranted('VIEW_REPORT', $this->getReport($name));
return $this->renderReport($name, $request->query->all());
}
How can I help you explore Laravel packages today?