To solve the issue of saving dependent dropdowns in Filament, you need to ensure that both the parent and child categories are properly saved and related in your database. Here’s a step-by-step solution:
-
Define the Parent Category Dropdown:
- This dropdown will list all parent categories.
- When a parent category is selected, it will reset the child category dropdown.
-
Define the Child Category Dropdown:
- This dropdown will list child categories based on the selected parent category.
- Ensure that the selected child category is saved correctly.
-
Save the Parent Category Based on the Child Category:
- When saving the article, ensure that the parent category is also saved based on the selected child category.
Here’s how you can implement this:
Step 1: Define the Parent Category Dropdown
Select::make('category_id')
->options(Tag::type(12)->whereNull('parent_id')->pluck('tag', 'id'))
->reactive()
->afterStateUpdated(function ($state, $set) {
$set('subcategory_id', null);
})
->default(function (Forms\Get $get) {
$subcategory = Tag::find($get('subcategory_id'));
return $subcategory ? $subcategory->parent_id : null;
})
->live();
Step 2: Define the Child Category Dropdown
Select::make('subcategory_id')
->placeholder(fn(Forms\Get $get): string => empty($get('category_id')) ? 'First select Category' : 'Select an option')
->options(function (Forms\Get $get) {
$categoryId = $get('category_id');
if (empty($categoryId)) {
return [];
}
return Tag::where('parent_id', $categoryId)->pluck('tag', 'id');
})
->reactive();
Step 3: Save the Parent Category Based on the Child Category
In your Article model, you need to ensure that when saving the article, the parent category is also saved based on the selected child category. You can achieve this by overriding the save method or using an observer.
Here’s an example using an observer:
use App\Models\Article;
use App\Models\Tag;
class ArticleObserver
{
public function saving(Article $article)
{
if ($article->subcategory_id) {
$subcategory = Tag::find($article->subcategory_id);
if ($subcategory) {
$article->category_id = $subcategory->parent_id;
}
}
}
}
// In your AppServiceProvider or any other service provider
use App\Models\Article;
use App\Observers\ArticleObserver;
public function boot()
{
Article::observe(ArticleObserver::class);
}
Final Code
Here’s the complete code for the form and the observer:
Form Code
Select::make('category_id')
->options(Tag::type(12)->whereNull('parent_id')->pluck('tag', 'id'))
->reactive()
->afterStateUpdated(function ($state, $set) {
$set('subcategory_id', null);
})
->default(function (Forms\Get $get) {
$subcategory = Tag::find($get('subcategory_id'));
return $subcategory ? $subcategory->parent_id : null;
})
->live();
Select::make('subcategory_id')
->placeholder(fn(Forms\Get $get): string => empty($get('category_id')) ? 'First select Category' : 'Select an option')
->options(function (Forms\Get $get) {
$categoryId = $get('category_id');
if (empty($categoryId)) {
return [];
}
return Tag::where('parent_id', $categoryId)->pluck('tag', 'id');
})
->reactive();
Observer Code
use App\Models\Article;
use App\Models\Tag;
class ArticleObserver
{
public function saving(Article $article)
{
if ($article->subcategory_id) {
$subcategory = Tag::find($article->subcategory_id);
if ($subcategory) {
$article->category_id = $subcategory->parent_id;
}
}
}
}
// In your AppServiceProvider or any other service provider
use App\Models\Article;
use App\Observers\ArticleObserver;
public function boot()
{
Article::observe(ArticleObserver::class);
}
This solution ensures that both the parent and child categories are properly saved and related in your database.