Advanced Features
Complex Aggregate Filtering
Build sophisticated aggregate conditions using the visual filter builder:
Filter Groups
Create multiple filter groups with AND/OR logic:
- Group filters logically
- Nest conditions within groups
- Visual organization with operator display
Relationship Filters in Aggregates
Filter aggregates based on related model conditions:
COUNT(orders) WHERE orders.status = 'completed'
AND orders.customer.country = 'US'
Through Relationships
Data Lens provides optimized support for HasOneThrough and HasManyThrough relationships.
Performance Optimization
// config/data-lens.php
'through_relationships' => [
'max_depth' => 3, // Maximum chain depth
'performance_threshold_ms' => 1000, // Log slow queries
'auto_index_suggestion' => true, // Suggest indexes
'optimize_queries' => true, // Enable optimizations
],
Automatic Index Suggestions
When enabled, Data Lens logs index recommendations:
[Data Lens] Suggested index: orders (customer_id, created_at)
Cache Management
Intelligent Caching
Data Lens caches expensive operations:
- Model introspection results
- Relationship metadata
- Filter type detection
- Through relationship chains
Multi-Tenant Cache Isolation
š For complete multi-tenant setup, see the Multi-Tenant Setup Guide
Automatic cache isolation per tenant prevents data leakage:
// config/data-lens.php
'cache' => [
'tenant_resolver' => function () {
return auth()->user()?->company_id;
},
],
Cache Clearing Strategies
# Clear all cache
php artisan data-lens:clear-cache
# Clear specific type
php artisan data-lens:clear-cache --type=model_fields
# In deployment scripts
php artisan data-lens:clear-cache --force
Column Type Detection
Data Lens automatically detects appropriate column types based on patterns:
Money Fields
Automatically formatted when field names contain:
- price, cost, amount, balance
- fee, payment, salary, total
- budget, revenue, income, expense, tax
Boolean Fields
Automatically rendered as badges when field names:
- Start with: is_, has_, can_, should_
- Contain: active, enabled, approved
Custom Patterns
// config/data-lens.php
'column_type_detection' => [
'money_field_patterns' => ['custom_price_field'],
'boolean_field_patterns' => ['custom_flag'],
],
Model Labels
Data Lens reads display labels directly from your Filament resources. If a CustomerResource overrides getModelLabel() to return a translated string, that label appears in the model picker in the report builder, the "Data Model" column in the report list, and the "Data Model" entry on the report view page.
No configuration is required ā this is automatic for any Filament resource registered in the active panel.
How resolution works
- The active Filament panel's registered resources are scanned.
- The first resource whose $model matches the report's data model wins.
- That resource's getModelLabel() (or getPluralModelLabel() for plurals) is called.
- If no resource matches, Data Lens falls back to humanizing the class basename ā App\Models\OrderItem becomes Order Item.
Caching
Resolved labels are cached using the existing model_fields cache. The default TTL is six hours and is configurable:
// config/data-lens.php
'cache' => [
'ttl' => [
'model_fields' => 21600, // 6 hours
],
],
When no panel is resolvable (e.g. inside a queued job that runs before any panel context is established), labels are not cached, so a later request inside a panel can still resolve a resource override.
Example: translated singular and plural
namespace App\Filament\Resources;
use Filament\Resources\Resource;
class CustomerResource extends Resource
{
protected static ?string $model = \App\Models\Customer::class;
public static function getModelLabel(): string
{
return __('models.customer.singular'); // "Cliente"
}
public static function getPluralModelLabel(): string
{
return __('models.customer.plural'); // "Clientes"
}
}
The report-builder model picker will list "Cliente"; the report list and view page will mirror it.
Verifying it works
In Tinker:
\Padmission\DataLens\Support\ModelLabelResolver::singular(\App\Models\Customer::class);
// => "Cliente"
To clear the cache during development:
php artisan data-lens:clear-cache
Polymorphic (MorphTo) Relationships
Data Lens supports polymorphic relationships (MorphTo) on host models, letting users pick fields from any concrete target type the relation can point to. The feature is opt-in per model.
Declaring morph targets
Add the HasDataLensMorphTargets trait to your host model and declare the concrete target classes for each polymorphic relation:
use Padmission\DataLens\Concerns\HasDataLensMorphTargets;
class Transaction extends Model
{
use HasDataLensMorphTargets;
public function payeeable(): \Illuminate\Database\Eloquent\Relations\MorphTo
{
return $this->morphTo('payeeable');
}
public static function dataLensMorphTargets(): array
{
return [
'payeeable' => [
\App\Models\Agency::class,
\App\Models\Vendor::class,
\App\Models\PropertyOwner::class,
],
];
}
}
The trait declaration is the security boundary: a MorphTo without it stays hidden from the report builder.
How users see it
When a model declares MorphTo targets, the relationship dropdown in the report builder shows one entry per declared branch ā for example payeeable ā Agency, payeeable ā Vendor, payeeable ā PropertyOwner. Users pick the branch they want; the field list then populates from that branch's columns.
Reports can select fields from multiple branches in the same view. Each row populates exactly one branch column (the one matching that row's stored *_type value); the others are NULL.
Filtering by morph type
Data Lens auto-exposes a synthetic {relation}_type field per declared MorphTo (e.g. payeeable_type for the example above). Use it as a filter to scope a report to one or more concrete types, or as an output column to display the human label of each row's type.
Filter semantics
When a filter targets a branch field (e.g. payeeable ā Agency ā name IS NULL), Data Lens routes the query through whereHasMorph so the type predicate is implicit. IS NULL correctly means "the payeeable is an Agency with a null name" ā not "the payeeable is anything other than an Agency".
Scope notes
- The MorphTo branch picker uses Eloquent's eager-loading + per-row traversal; no extra joins are added to the base query.
- Aggregating over a morph branch (e.g. "count agencies-paid") is not supported ā the feature is for column selection and filtering only.
- Deeper traversal beyond a morph branch (e.g. payeeable ā Agency ā users ā country) is supported by the path walker but not yet surfaced in the deep-relationship configuration modal.
Calculated Columns
Calculated columns are user-definable through the report builder UI ā no code changes required. The form has three kinds:
- Simple math between two operands (column or constant): +, -, *, /, %
- Aggregate functions over a relationship: COUNT, SUM, AVG, MIN, MAX, COUNT DISTINCT
- Date arithmetic on date or datetime columns: add a duration, subtract a duration, or compute the difference between two dates
Operands can mix database columns, constants, and other calculated columns; expressions are evaluated by the database during the report query.
Date arithmetic operations
| Operation | Operands | Result | |---|---|---| | DATE_ADD | date_column + N unit | New date shifted forward | | DATE_SUBTRACT | date_column ā N unit | New date shifted backward | | DATE_DIFF | date_column ā date_column (unit) | Integer count of unit between the two dates |
Supported units: days, weeks, months, quarters, years.
Example: an order's expected delivery date is seven days after it shipped. Add a calculated column with:
- Left operand: shipped_at (column)
- Operation: DATE_ADD
- Right operand: 7 (constant)
- Unit: days
The report query selects the computed timestamp alongside your other columns.
Database support
Date arithmetic is compiled per connection at query time:
- MySQL / MariaDB ā native DATE_ADD / DATE_SUB / TIMESTAMPDIFF
- PostgreSQL ā interval arithmetic (col + (N || ' unit')::interval) plus AGE() / EXTRACT() for differences
- SQLite ā datetime(col, '+N units') plus julianday() and strftime() for differences
The dialect is detected from the active database connection ā no configuration is required, and reports created against one engine work unchanged when the connection is switched. Postgres has no QUARTER interval, so quarter arithmetic is internally rewritten to 3 months.
NULL handling
When any operand is NULL (for example, an order without a shipped_at), the calculated value is also NULL and the column renders empty. This holds across all three database engines.
Limitations
- Filtering and sorting on a calculated date column use the column alias; indexes on the source date columns won't be used for that filter. For large datasets, prefer pre-filtering with a regular date filter on the source column.
- Quarters on Postgres behave as 3 months. For workloads where calendar-quarter semantics matter (e.g. fiscal-year boundaries), prefer expressing the report in months.
Advanced Formatting
Badge Columns
Use the visual formatting interface to create badge columns:
- Select badge style in the format configuration modal
- Choose colors based on field values (success, warning, danger, etc.)
- Configure color mappings for different states
Custom Icons
Add Heroicons to enhance visual communication:
- Position before or after text
- Independent color configuration
- Full Heroicon library support
HTML Content
Safely render HTML content:
- Automatic sanitization
- Preserves safe formatting
- Prevents XSS attacks
API Advanced Usage
IP Whitelisting
Restrict API access to specific IPs (global configuration):
// Enable API per panel
DataLensPlugin::make()
->apiEnabled()
// Configure IP whitelist globally in config/data-lens.php
'api' => [
'ip_whitelist' => [
'192.168.1.0/24', // Subnet
'10.0.0.50', // Single IP
],
],
Scheduling Advanced Features
Complex Recurrence Rules
Data Lens uses RFC 5545 compliant RRULE for complex schedules:
- Every weekday at 9 AM
- First and third Monday of each month
- Last Friday of each quarter
- Custom RRULE strings
Timezone Handling
Configure timezone resolution for your application:
// In AppServiceProvider boot method
use Padmission\DataLens\DataLens;
DataLens::setTimezoneResolver(function () {
return auth()->user()?->timezone ?? config('app.timezone');
});
Email Customization
Create custom mail classes for advanced features:
class TenantAwareReportEmail extends ReportEmail
{
public function build()
{
// Add tenant-specific headers
$this->withSymfonyMessage(function ($message) {
$message->getHeaders()->addTextHeader(
'X-Tenant-ID',
$this->schedule->customReport->tenant_id
);
});
return parent::build();
}
}
Export Optimizations
Streaming CSV Driver
For CSV exports, Data Lens uses a high-performance streaming driver by default that bypasses PhpSpreadsheet:
// In config/data-lens.php
'exports' => [
'csv_driver' => 'streaming', // or 'laravel-excel'
],
Streaming driver benefits:
- O(n) performance vs O(n²) with PhpSpreadsheet
- Uses native fputcsv() with cursor-based iteration
- Minimal memory footprint regardless of export size
- Memory-efficient storage: uses rename() for local disks (zero memory copy), streams for S3/remote disks
When to use laravel-excel:
- You need advanced cell formatting
- You require formula support
- You're using custom Laravel Excel features
Chunked Processing
Configure chunk size globally for large exports:
// In config/data-lens.php
'exports' => [
'chunk_size' => 5000, // Process 5000 rows at a time
],
Memory Management
Exports use streaming to minimize memory usage:
- Processes data in chunks via cursor iteration
- Streams directly to file without loading into memory
- Automatic cleanup of temporary files
- Detects local vs remote storage for optimal file handling
Security Features
Column Security
Automatically exclude sensitive columns:
php artisan data-lens:suggest-excluded-columns --format=config
Output categorized by risk level:
- HIGH: Passwords, tokens, keys
- MEDIUM: Personal info, financial data
- LOW: Tracking, configuration
Model Exclusions
Prevent entire models from appearing in reports:
'excluded_models' => [
\App\Models\AdminUser::class,
\App\Models\SystemConfig::class,
],
Performance Monitoring
Query Performance Tracking
Log slow queries for optimization:
'through_relationships' => [
'performance_threshold_ms' => 500, // Log queries over 500ms
],
Export Performance
Monitor export processing times:
- Tracked in schedule history
- Available via API metadata
- Logged for analysis
Integration Features
Data Lens integrates with popular Filament packages:
'integrations' => [
'custom_fields' => true, // Relaticle Custom Fields
'advanced_tables' => true, // Advanced Tables
],
Custom Fields
- Rich field types (text, select, date, file upload, etc.)
- Multi-tenancy field configuration
- Built-in validation framework
Advanced Tables
- User-customizable saved views
- Advanced filter builder
- Column reordering and persistence
Debugging Tools
Debug Mode
Enable detailed logging:
php artisan data-lens:check-scheduled-reports --debug
Cache Inspection
View cache contents for debugging:
// In tinker or debug code
app(\Padmission\DataLens\Services\CacheManager::class)
->getCacheKey('model_fields', 'App\Models\User');