Skip to content

Commit ea90d22

Browse files
document lifecycle hooks and custom search handler support
1 parent 027449b commit ea90d22

2 files changed

Lines changed: 182 additions & 6 deletions

File tree

docs/table-component/component-configuration.md

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use Illuminate\View\View; // [!code ++]
4848
class DishTable extends PowerGridComponent
4949
{
5050
public function noDataLabel(): string|View // [!code ++:5]
51-
{
51+
{
5252
//return 'We could not find any dish matching your search.';
5353
return view('dishes.no-data');
5454
}
@@ -92,7 +92,7 @@ use PowerComponents\LivewirePowerGrid\PowerGridComponent;
9292
class DishTable extends PowerGridComponent
9393
{
9494
public bool $deferLoading = true;// [!code ++:3]
95-
95+
9696
public string $loadingComponent = 'components.my-custom-loading';
9797
}
9898
```
@@ -442,7 +442,7 @@ class DishTable extends PowerGridComponent
442442
public function setUp(): array
443443
{
444444
$this->persist(// [!code ++:4]
445-
tableItems: ['columns', 'filters', 'sort'],
445+
tableItems: ['columns', 'filters', 'sort'],
446446
prefix: auth()->id
447447
);
448448
}
@@ -496,7 +496,7 @@ use PowerComponents\LivewirePowerGrid\Facades\PowerGrid; // [!code ++]
496496
class DishTable extends PowerGridComponent
497497
{
498498
public function setUp(): array
499-
{
499+
{
500500
return [
501501
PowerGrid::cache() // [!code ++:3]
502502
->ttl(60) // [!code ++:3]
@@ -512,7 +512,7 @@ You may also use your own custom tag, as demonstrated below.
512512
use PowerComponents\LivewirePowerGrid\Cache;
513513

514514
public function setUp(): array
515-
{
515+
{
516516
return [
517517
PowerGrid::cache()// [!code ++:3]
518518
->ttl(60) // [!code ++:3]
@@ -603,3 +603,106 @@ If you need to add a custom event to your PowerGrid Table, just override the met
603603
];
604604
}
605605
```
606+
607+
## Lifecycle Hooks
608+
609+
PowerGrid provides three lifecycle hooks that allow you to modify data at key stages of the rendering pipeline. All hooks are defined with no-op defaults, so they are fully opt-in.
610+
611+
These hooks are intended for advanced use cases where you need to adjust the query, rows, or resolved actions after PowerGrid has applied filters, search, and sorting, but before rendering or dispatching data to the frontend.
612+
613+
### Hook Overview
614+
615+
- `transformQuery(mixed $query): mixed`
616+
- Modify the query after filters, search, and sorting are applied, but before pagination executes it. Useful for eager loading, subqueries, withCount(), or any query-level adjustments that depend on the current filter state.
617+
618+
- `transformRows(Collection $rows): Collection`
619+
- Modify the paginated rows after the query has executed and data has been transformed, but before it is passed to the view. Runs on the current page's rows only. Ideal for enriching rows with data from external APIs or computed values not available at query time.
620+
621+
- `transformActions(array $actionsByRow, Collection $rows): array`
622+
- Modify the resolved actions array before it is dispatched to the frontend via JavaScript. Receives both the `$actionsByRow` dictionary (keyed by primary key) and the current `$rows` collection. Useful for dynamically changing button labels, visibility, or attributes based on data not available during `actions()` resolution.
623+
624+
### Examples
625+
626+
#### transformQuery — add counts and a subselect
627+
628+
```php
629+
public function transformQuery(mixed $query): mixed
630+
{
631+
return $query
632+
->withCount('comments')
633+
->addSelect(DB::raw('(SELECT COUNT(*) FROM reviews WHERE reviews.dish_id = dishes.id) as review_count'));
634+
}
635+
```
636+
637+
#### transformRows — enrich rows with external API data
638+
639+
```php
640+
use Illuminate\Support\Collection;
641+
642+
public function transformRows(Collection $rows): Collection
643+
{
644+
$ids = $rows->pluck('id')->toArray();
645+
$apiData = ExternalApi::getByIds($ids);
646+
647+
return $rows->map(function ($row) use ($apiData) {
648+
$row->external_score = $apiData[$row->id] ?? null;
649+
650+
return $row;
651+
});
652+
}
653+
```
654+
655+
#### transformActions — modify action slots per row
656+
657+
```php
658+
public function transformActions(array $actionsByRow, Collection $rows): array
659+
{
660+
$ids = $rows->pluck('id')->toArray();
661+
$counts = NotificationService::getCounts($ids);
662+
663+
foreach ($actionsByRow as $rowId => &$actions) {
664+
foreach ($actions as &$action) {
665+
if ($action['action'] === 'notifications') {
666+
$count = $counts[$rowId] ?? 0;
667+
$action['slot'] = "Notifications <span class=\"badge\">{$count}</span>";
668+
}
669+
}
670+
}
671+
672+
return $actionsByRow;
673+
}
674+
```
675+
676+
### Pipeline Position
677+
678+
```
679+
datasource()
680+
681+
682+
Filters / Search / Sorting
683+
684+
685+
★ transformQuery() ← modify the builder before it hits the DB
686+
687+
688+
Pagination
689+
690+
691+
Data Transformation
692+
693+
694+
★ transformActions() ← modify resolved actions before JS dispatch
695+
696+
697+
★ transformRows() ← enrich/modify rows before the view
698+
699+
700+
Render
701+
```
702+
703+
### Notes and Best Practices
704+
705+
- Use `transformQuery` for query-level modifications only. Avoid executing heavy per-row logic inside this hook — use `transformRows` for per-row enrichment.
706+
- `transformRows` runs only on the current page's rows (after pagination). If you need to enrich all rows regardless of pagination, run a separate process or fetch the required data aggregated by IDs.
707+
- `transformActions` receives the already-resolved actions array. Modify keys such as `slot`, `can`, `attributes`, `icon`, `iconAttributes`, etc., but avoid mutating the structure unexpectedly.
708+
- Because hooks run on the server side, keep I/O and long-running operations under control; consider caching results when calling external services.

docs/table-features/searching-data.md

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,82 @@ class DishTable extends PowerGridComponent
163163

164164
</div>
165165

166+
## Custom Search Handler
167+
168+
The global search logic is resolved via the Laravel container, allowing you to replace the default implementation with your own handler.
169+
170+
### SearchHandlerContract
171+
172+
Your custom handler must implement the `SearchHandlerContract` interface:
173+
174+
```php
175+
namespace PowerComponents\LivewirePowerGrid\DataSource\Processors\Database\Handlers;
176+
177+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
178+
use Illuminate\Database\Query\Builder as QueryBuilder;
179+
180+
interface SearchHandlerContract
181+
{
182+
public function apply(EloquentBuilder|QueryBuilder $query): EloquentBuilder|QueryBuilder;
183+
}
184+
```
185+
186+
### Binding Your Handler
187+
188+
Register your custom handler in a Service Provider (e.g., `AppServiceProvider::register()`):
189+
190+
```php
191+
use PowerComponents\LivewirePowerGrid\DataSource\Processors\Database\Handlers\SearchHandlerContract;
192+
use App\Support\Powergrid\Handlers\SearchHandler;
193+
194+
public function register(): void
195+
{
196+
$this->app->bind(SearchHandlerContract::class, function ($app, array $params) {
197+
return new SearchHandler($params['component']);
198+
});
199+
}
200+
```
201+
202+
### Example: Custom Handler
203+
204+
```php
205+
// app/Support/PowerGrid/Handlers/SearchHandler.php
206+
207+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
208+
use Illuminate\Database\Query\Builder as QueryBuilder;
209+
use PowerComponents\LivewirePowerGrid\DataSource\Processors\Database\Handlers\SearchHandlerContract;
210+
use PowerComponents\LivewirePowerGrid\PowerGridComponent;
211+
212+
class SearchHandler implements SearchHandlerContract
213+
{
214+
public function __construct(
215+
protected readonly PowerGridComponent $component
216+
) {}
217+
218+
public function apply(EloquentBuilder|QueryBuilder $query): EloquentBuilder|QueryBuilder
219+
{
220+
$search = $this->component->search;
221+
222+
if (empty($search)) {
223+
return $query;
224+
}
225+
226+
return $query->where(function ($q) use ($search) {
227+
$q->where('name', 'like', "%{$search}%")
228+
->orWhere('email', 'like', "%{$search}%");
229+
});
230+
}
231+
}
232+
```
233+
234+
::: tip
235+
The default `SearchHandler` uses `protected` methods, so you can also **extend** it instead of replacing it entirely.
236+
:::
237+
238+
166239
## Query String
167240

168-
To enable the Query functionality, you must declare a method `queryString()` inside your Table Component class.
241+
To enable the Query functionality, you must declare a method `queryString()` inside your Table Component class.
169242

170243
```php
171244
// app/Livewire/DishTable.php

0 commit comments

Comments
 (0)