Bumpcore Panorama is a Laravel package for building Eloquent models from query sources. It lets a model behave like a normal read model while its rows come from a subquery, aggregate, CTE, or custom query builder source.
Use it when you need query-backed models for reports, projections, aggregate tables, read-only relation targets, or custom database features without giving up Eloquent relations and builder chaining.
- Version Table
- Installation
- Quick Start
- HasSource
- Eloquent Compatibility
- Exceptions
- Testing
- Contribution
- Changelog
- Credits
- License
| Bumpcore Panorama | Laravel | PHP |
|---|---|---|
| 0.x | ^12.0 | ^8.2 |
| 0.x | ^13.0 | ^8.3 |
Install the package with Composer:
composer require bumpcore/panoramaAdd the HasSource trait to an Eloquent model and define newSource().
use Bumpcore\Panorama\HasSource;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
class CustomerBalance extends Model
{
use HasSource;
protected $primaryKey = 'customer_id';
public function newSource(int $customer_id): Builder
{
return DB::table('invoices')
->selectRaw('customer_id, sum(amount) as balance')
->where('customer_id', $customer_id)
->groupBy('customer_id');
}
}
$balances = CustomerBalance::query()
->where('balance', '>', 0)
->withParams(customer_id: $customer->getKey())
->get();The model is still queried through Eloquent. Panorama only replaces the model's
from clause with the source query when the builder is executed.
HasSource registers a global scope and a local withParams() scope on the
model. The global scope wraps the model query in the source returned by
newSource().
newSource() may return an Eloquent builder or a base query builder.
use Bumpcore\Panorama\HasSource;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class OpenInvoiceTotal extends Model
{
use HasSource;
public function newSource(): Builder
{
return Invoice::query()
->selectRaw('customer_id, sum(amount) as open_total')
->where('status', 'open')
->groupBy('customer_id');
}
}The source is not expected to hit a real table with the model name. It becomes the derived table Panorama queries from.
Use withParams() to pass named arguments to newSource().
$balance = CustomerBalance::query()
->withParams(customer_id: 1, status: 'open')
->first();Array params are also supported:
$balance = CustomerBalance::query()
->withParams([
'customer_id' => 1,
'status' => 'open',
])
->first();Params must use string keys because they are passed as named arguments. Later calls replace earlier values, just like normal builder state.
By default, Panorama uses the model table name as the SQL alias for the wrapped
source. Override querySourceAlias() when the source should use a different
alias.
class CustomerBalance extends Model
{
use HasSource;
public function querySourceAlias(): string
{
return 'customer_balances';
}
}The alias must not be an empty string.
Panorama keeps the consumer-facing Eloquent builder intact. You can still use:
- normal Eloquent builder methods;
- local scopes;
- relations and eager loading;
whereHas()against query-backed relation models;- custom Eloquent builders;
- external query builder extensions.
The test suite includes compatibility coverage for:
staudenmeir/laravel-ctetpetry/laravel-postgresql-enhanced- consumer-defined custom Eloquent builders
All package-specific failures extend:
Bumpcore\Panorama\Exceptions\PanoramaExceptionMore specific exceptions are available:
InvalidQuerySourceExceptionMissingQuerySourceException
InvalidQuerySourceException is thrown when a model does not use the required
trait, when newSource() returns an unsupported value, or when the source alias
is empty.
MissingQuerySourceException is thrown when a model uses HasSource without
overriding newSource().
Install development dependencies:
composer installRun the test suite:
composer testRun static analysis:
composer analyseCheck code style:
composer cs:checkRun the 100% coverage gate:
composer test:coverageCoverage requires PCOV, Xdebug, or phpdbg.
Contributions are welcome. If you find a bug or have a suggestion for improvement, please open an issue or create a pull request.
Please include tests for behavioral changes and run the quality checks before submitting a pull request:
composer cs:check
composer analyse
composer testSee CHANGELOG.md for version history.
The MIT License (MIT). Please see License File for more information.