Getting started
Table
The Record Finder will be provided as a form component that you can use in any of your forms:
use RalphJSmit\Filament\RecordFinder\Forms\Components\RecordFinder;
RecordFinder::make('product_id')
->label('Product')
The Record Finder component will show a button to choose/select records. Clicking the button will open a full modal or slide-over with a table.
Specifying table query
You can provide a query to the table using the ->tableQuery() (or alias ->query()) method. The method accepts a query, a Relation or a closure that returns a query or a Relation. For example:
RecordFinder::make('product_id')
->tableQuery(Product::query())
->query(Filament::getTenant()->products()) // Alias `query()` for convenience...
Of course, you can provide any constraints to your query that you want:
RecordFinder::make('post_id')
->query(fn (User $record) => Post::query()->whereBelongsTo($record))
Relationships
You can integrate the RecordFinder also very easily into a relationship using the ->relationship() method. This will automatically adjust the query to the relationship and save the selected record(s) to the relationship. For example:
RecordFinder::make('product_id')
->relationship(name: 'products')
If you want to integrate with a BelongsToMany relationship, you can use the ->multiple() method in combination with ->relationship():
RecordFinder::make('products')
->multiple()
->relationship()
When using ->disabled() with ->multiple() and ->relationship(), ensure that ->disabled() is called before ->relationship(). This ensures that the dehydrated() call from within relationship() is not overridden by the call from disabled(). This works the same as with the regular Select component:
RecordFinder::make('products')
->multiple()
->disabled()
->relationship()
Attaching pivot table
You can also attach pivot data if you want:
RecordFinder::make('products')
->multiple()
->relationship()
->pivotData(function (Forms\Get $get) {
return [...];
})
If you're building a form inside your Livewire component, make sure you have set up the form's model. Otherwise, the
RecordFinderdoesn't know which model to use to retrieve the relationship from.
The
->relationship()support is identical to the native FilamentSelectimplementation. Don't be afraid to just simply replaceSelectbyRecordFinder, remove the option label method calls (if present) and you should be good to go!
Excluding the current record
When working with recursive relationships, you will likely want to remove the current record from the set of results. This can easily be done using the ignoreRecord argument to the ->relationship() method:
RecordFinder::make('parent_id')
->relationship('parent', ignoreRecord: true)
Customizing the relationship query
You may customize the database query that retrieves options using the modifyQueryUsing parameter of the ->relationship() method:
RecordFinder::make('products')
->multiple()
->relationship('products', function (Builder $query) {
return $query->whereBelongsTo(Filament::getTenant())->withTrashed();
})
Customizing the table
The table shown inside the Record Finder modal is fully customizable — you can configure its columns, filters, groups, actions and pagination.
Columns
You can provide columns to the table using the ->tableColumns() (or alias ->table()) method:
use Filament\Tables;
RecordFinder::make('product_id')
->tableStandalone() // Ensure table configuration starts empty...
->tableColumns([
Tables\Columns\TextColumn::make('tenant.name'),
Tables\Columns\TextColumn::make('name')
->description(fn (Product $product) => $product->description),
])
// Alias `table()` for convenience...
->table([...])
Note regarding closures: the package supports using closures everywhere as you are normally used to. This means that you can just use closures to inject the current
$record, the current$column, the current$livewireor any other parameter that Filament provides. The only limitation is that you cannot reference the variable$thisorstatic(in the sense ofstatic::someMethod()) inside your closures. However, this has never been recommended practice and you wouldn't normally ever need to use$this, since all relevant parameters can be injected by Filament as a closure parameter. If you want to force yourself not to use these, you can make a habit of writing every closure as astatic functionorstatic fn () => ....
Getting columns from the resource
By default, if you do not provide any columns to the ->table() method, the Record Finder will automatically check which Eloquent model the table query is querying. If that model has a resource in the current panel, then the Record Finder will automatically use the table configuration from that particular resource.
Said differently, if you do not provide a custom table configuration (like columns, groups and filters), the Record Finder will automatically take these from how you configured them on your resource. This has the benefit that in most cases the table in the selection modal will look identical to the resource in your app. For users this can be a great UX, since they are already familiar with the table from the resource and most of the time there is no real reason to deviate from that in the record finder. It is also beneficial for you as a developer, because you don't need to repeat common configuration like columns, groups and filters.
The only exception is that all types of actions are not automatically taken from the resource. If you want to add any of these, use the ->tableHeaderActions(), ->tableActions() or ->tableEmptyStateActions().
If you want to disable this behaviour, you can use the ->tableStandalone() (or alias ->standalone()) method. This will ensure you'll start with a fresh table:
RecordFinder::make('product_id')
->tableStandalone()
->standalone() // Alias for convenience...
->tableColumns([
TextColumn::make('name'),
// ...
])
// ...
Filters
You can add table filters by using the ->tableFilters() method. This allows you to easily add functionality to filter records from the table. For example:
RecordFinder::make('product_id')
->tableFilters([
Tables\Filters\TernaryFilter::make('is_public'),
])
Groups
You can provide table groups using the ->tableGroups() method.
RecordFinder::make('product_id')
->tableGroups([
'category.name',
Tables\Grouping\Group::make('category.name'),
])
You can set the default group using the ->tableDefaultGroup() method:
RecordFinder::make('product_id')
->tableGroups(['category.name'])
->tableDefaultGroup('category.name')
You can hide the grouping settings using the ->tableGroupingSettingsHidden() method:
RecordFinder::make('product_id')
->tableGroups(['category.name'])
->tableDefaultGroup('category.name')
->tableGroupingSettingsHidden()
This works great if you want to always group a table by default on e.g. category and not allow the end user to change the grouping.
Actions
You can add actions to the table using the ->tableHeaderActions(), ->tableActions(), ->tableBulkActions() and ->tableEmptyStateActions() methods. This allows you to easily add functionality to create, edit or delete records from the table. For example:
use Filament\Tables;
RecordFinder::make('author_id')
->tableHeaderActions([
Tables\Actions\Action::make('your_header_action')
])
->tableActions([
Tables\Actions\Action::make('your_action'),
])
->tableBulkActions([
Tables\Actions\BulkAction::make('your_bulk_action')
])
->tableEmptyStateActions([
Tables\Actions\Action::make('your_empty_state_action'),
])
Pagination
You can set the available pagination page options by using the ->tablePaginationPageOptions() method:
RecordFinder::make('product_id')
->tablePaginationPageOptions([10, 25, 50, 100])
Modifying the underlying table
You can modify the underlying table object in the Record Finder by passing a closure to the modifyTableUsing() method and returning the modified table:
RecordFinder::make('product_id')
->tableGroups([
'category.name'
])
->modifyTableUsing(function (Table\Table $table) {
// Nice example: force a certain group to be applied always and disallow the user to select a group.
$table->defaultGroup('category.name')->groupingSettingsHidden();
})