Report system architecture
Hi there,
I'm building a reporting system which should consist of reusable classes in order to allow the same report data to be displayed on the app pages and via an API.
There are a few models that are used for reports.
App\CollectedJob
App\CollectedFailedJob
App\CollectedRequest
App\CollectedQuery
There are 3 report types:
Counts (an integer displaying a number of rows) Charts (daily or hourly data) Tables (list of rows)
Each model has application_id and environment_id columns, so there are App\Application and App\Environment parent models.
All reports are on a date range basis. I've got a DateRange class which holds the start/end Carbon date instances.
Basically each report needs an Application model instance, Environment model instance and a DateRange.
This seems pretty straightforward until some reports need additional filters. Also have in mind that a single app page may need to generate around 15 different types of reports (counts + charts + tables).
Some reports need to be able to be filtered by the url parameters:
- from / to (DateRange) - relevant for all
- response code (relevant to CollectedRequest)
- table name (relevant to CollectedQuery)
I like the QueryFilter approach as in https://laracasts.com/series/eloquent-techniques/episodes/4 .
However since different reports may need the same model, but different parameters I'm a bit confused on whether this is a good approach in this case.
I'd like the system to be quite extendable, so each report could implement a ReportableInterface with a getReportData() method, and each sub-type could implement a Chartable/etc interfaces so that each report would have a consistent API.
Also how would you build the report instances?
I'm thinking about something like:
<?php
namespace App\Client\Reports;
use App\Models\Application;
use App\Models\ApplicationEnvironment;
class ReportBuilder
{
protected $reports = [];
/**
* ReportBuilder constructor.
*
* @param array $reports
*/
public function __construct(array $reports)
{
$this->reports = $reports;
}
/**
* @param Application $application
* @param ApplicationEnvironment $environment
*
* @return AbstractReport[]
*/
public function build(Application $application, ApplicationEnvironment $environment)
{
$built = [];
foreach ($this->reports as $group => $reports) {
foreach ($reports as $name => $report) {
/** @var AbstractReport $instance */
$instance = app()->makeWith($report, ['application' => $application, 'environment' => $environment]);
$built[$group][$name] = $instance->getReportData();
}
}
return $built;
}
}
and then:
$builder = new ReportBuilder(['count' => [RequestCount::class], 'chart' => [RequestChart::class]]));
$reportData = $builder->build(...);
So two main questions:
- How do I make each report depend on DateRange, Application and Environment classes, plus extra filters?
- What would be the best way to build the instances of the reports?
Thanks!
Please or to participate in this conversation.