Version: 1.5.2+
Enhanced For: Production & Enterprise Use
Last Updated: December 30, 2025
This guide covers all advanced features of the ArtFlow Table package optimized for speed, robustness, and enterprise applications.
The package uses 18+ specialized traits for modularity and performance:
DatatableTrait
├── Core Traits (Search, Sorting, Caching)
├── UI Traits (Column Visibility, Events)
├── Advanced Traits (Optimization, Export, API)
└── WithPagination (Livewire Pagination)
Every request executes a SINGLE optimized query:
SELECT DISTINCT table.*
FROM table
LEFT JOIN related_tables ON conditions
WITH COUNT(relationships) AS count_aggregates
WHERE search_filters
ORDER BY sort_column
LIMIT pagination;
Result: 50 items = 1 query ✅ (not 51)
Automatic relationship loading:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Product',
'columns' => [
['key' => 'name', 'label' => 'Product'],
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name'],
['key' => 'brand_name', 'label' => 'Brand', 'relation' => 'brand:name'],
],
])
Behind the Scenes:
$query->with(['category', 'brand']); // Single eager load
Result: 50 products with categories = 1 query ✅
Automatic count loading:
'columns' => [
['key' => 'products_count', 'label' => 'Products'],
['key' => 'reviews_count', 'label' => 'Reviews'],
]
Behind the Scenes:
$query->withCount(['products', 'reviews']); // Aggregation in single query
Result: 50 categories with counts = 1 query ✅
Automatic caching of query results:
protected $cachedQueryResults = null;
protected $cachedQueryHash = null;
protected $distinctValuesCacheTime = 300; // 5 minutes
How it Works:
Filters pre-load available values on mount:
protected function preloadDistinctValues(): void
Result: Filter dropdowns populate instantly ✅
Large exports processed in chunks:
public function export($format)
{
$query->chunk(500, function($records) {
// Process 500 at a time
// Prevents memory issues with large datasets
});
}
Handles: Exporting 100K+ records without memory errors ✅
Searches all configured text columns simultaneously:
'columns' => [
['key' => 'title', 'label' => 'Title'], // ✅ Searchable
['key' => 'description', 'label' => 'Desc'], // ✅ Searchable
['key' => 'price', 'label' => 'Price'], // ❌ Not searchable (number)
]
Search in related model columns:
'columns' => [
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name'],
]
Result: Search finds "electronics" in category.name ✅
Search inside JSON columns:
'columns' => [
['key' => 'metadata', 'label' => 'Tags', 'json' => 'tags.0'],
]
Result: Search finds values inside JSON arrays ✅
'columns' => [
// Standard database column
['key' => 'name', 'label' => 'Name'],
// Related data (eager loaded)
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name'],
// Relationship count (aggregated)
['key' => 'reviews_count', 'label' => 'Reviews'],
// JSON extraction
['key' => 'metadata', 'json' => 'color', 'label' => 'Color'],
// Function result (computed)
['key' => 'status', 'function' => 'getStatus', 'label' => 'Status'],
// Raw HTML (custom rendering)
['key' => 'price', 'raw' => '₹{{ $row->price }}', 'label' => 'Price'],
]
Users can show/hide columns via dropdown:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [...],
'colvisBtn' => true, // Show visibility toggle
])
Features:
Hide columns but keep them sortable:
'columns' => [
['key' => 'id', 'label' => 'ID', 'hidden' => true], // Not displayed
]
Use Case: Sort by ID without showing it to users
System auto-detects best sort column:
protected function autoDetectOptimalSort(): void
{
// Prioritizes: created_at > updated_at > id
}
Architecture supports multi-column sorting for future:
public function applySorts(array $sorts): void
{
// Future enhancement: support multiple sort columns
}
Automatically creates efficient JOINs for sorting:
'sortBy' => 'customer_name', // Sorts by related.name
'relation' => 'customer:name'
Generated SQL:
SELECT DISTINCT orders.*
FROM orders
LEFT JOIN customers ON orders.customer_id = customers.id
ORDER BY customers.name ASC
LIMIT 50;
Filter by specific columns:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Product',
'columns' => [...],
'filters' => [
'status' => ['type' => 'select', 'options' => ['active', 'inactive']],
'price' => ['type' => 'number'],
'created_at' => ['type' => 'date'],
],
])
Chain multiple filters with AND logic:
// User selects:
// Filter 1: status = 'active'
// Filter 2: price > 100
// Result: Only active products over $100
System auto-detects filter type:
['type' => 'text'] // Text input
['type' => 'number'] // Number input with operators
['type' => 'date'] // Date picker
['type' => 'select'] // Dropdown
['type' => 'distinct'] // Auto-populated from data
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [...],
'showExport' => true,
'exportFormats' => ['csv', 'excel', 'pdf'],
])
Raw HTML content is sanitized:
// Before raw output, system sanitizes HTML
$html = Blade::renderString($column['raw'], $data);
All queries use parameterized statements:
$query->where('name', 'like', "%{$search}%"); // ✅ Safe
Supports authorization gates:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'customQuery' => Item::where('user_id', auth()->id()), // User's items only
])
User's column visibility preferences saved in session:
public $enableSessionPersistence = true;
// Session key auto-generated per user & table
// Remembers across page refreshes
Last used sort order remembered:
protected function saveSortingToSession(): void
{
// Saves to session, restored on page load
}
URL query parameters for sharing:
public $enableQueryStringSupport = true;
// URL: ?search=john&sortBy=name&sortDirection=asc
// Allows sharing filtered views
Pass custom Eloquent query:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [...],
'customQuery' => Item::whereStatus('active')
->whereTenantId(auth()->user()->tenant_id),
])
Pass collection instead of model:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [...],
'data' => Item::all(), // Use existing collection
])
Style table with Tailwind/Bootstrap:
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [...],
'tableClass' => 'table table-striped',
'theadClass' => 'bg-dark text-white',
'rowClass' => 'hover:bg-light',
])
// Sort changed
$this->triggerSortEvent($column, $direction, $oldColumn, $oldDirection);
// Filter applied
$this->triggerFilterEvent($filterData);
// Pagination changed
$this->triggerPaginationEvent($page, $perPage, $oldPage, $oldPerPage);
<script>
document.addEventListener('livewire:initialized', function() {
// Table initialized
});
Livewire.on('sortChanged', (data) => {
// Logging, analytics, etc.
});
</script>
// config/aftable.php
'cache' => [
'enabled' => env('AFTABLE_CACHE', true),
'ttl' => env('AFTABLE_CACHE_TTL', 300), // 5 minutes
],
'pagination' => [
'default_per_page' => env('AFTABLE_PER_PAGE', 50),
'max_per_page' => env('AFTABLE_MAX_PER_PAGE', 1000),
],
'search' => [
'min_length' => 3,
'debounce_ms' => 500,
],
For very large tables (1M+ records):
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Item',
'columns' => [
// Only sortable columns - skip others
['key' => 'id', 'label' => 'ID'],
['key' => 'name', 'label' => 'Name'],
],
'records' => 100, // Larger page size
'searchable' => true, // Enable search (more efficient than scrolling)
'enableSessionPersistence' => false, // Reduce session overhead
])
[@livewire](https://github.com/livewire)('aftable', [
'model' => 'App\Models\Product',
'columns' => [
['key' => 'sku', 'label' => 'SKU'],
['key' => 'name', 'label' => 'Product Name'],
['key' => 'category_name', 'label' => 'Category', 'relation' => 'category:name'],
['key' => 'warehouse_id', 'relation' => 'warehouse:name', 'label' => 'Warehouse'],
['key' => 'stock', 'label' => 'Stock'],
['key' => 'price', 'label' => 'Price'],
['key' => 'reviews_count', 'label' => 'Reviews'],
['key' => 'updated_at', 'label' => 'Last Updated'],
],
'filters' => [
'status' => ['type' => 'select'],
'stock' => ['type' => 'number'],
'price' => ['type' => 'number'],
],
'sortBy' => 'updated_at',
'sortDirection' => 'desc',
'records' => 100,
'searchable' => true,
'showExport' => true,
'exportFormats' => ['csv', 'excel', 'pdf'],
'colvisBtn' => true,
'refreshBtn' => true,
'customQuery' => Product::whereStatus('active'),
])
Performance:
| Feature | Status | Notes |
|---|---|---|
| Database Sorting | ✅ | All column types |
| Relationship Sorting | ✅ | Via JOINs |
| Count Sorting | ✅ | Via withCount() |
| Multi-Column Search | ✅ | Text & relations |
| JSON Search | ✅ | Any JSON path |
| Filter by Column | ✅ | Multiple filters |
| Date Range Filter | ✅ | Advanced |
| Export CSV | ✅ | Full data |
| Export Excel | ✅ | Formatted |
| Export PDF | ✅ | Printable |
| Pagination | ✅ | Dynamic per-page |
| Column Visibility | ✅ | Real-time toggle |
| Session Persistence | ✅ | User preferences |
| Query String Support | ✅ | Shareable URLs |
| Performance Caching | ✅ | 5min TTL |
| Eager Loading | ✅ | Auto-detection |
| Count Aggregation | ✅ | Auto-detection |
| Chunked Processing | ✅ | Large exports |
| XSS Protection | ✅ | HTML sanitization |
| Custom CSS | ✅ | Tailwind ready |
| Dark Mode | ✅ | Bootstrap 5 |
Version: 1.5.2+
Status: Production Ready ✅
Support: https://artflow.pk
How can I help you explore Laravel packages today?