Filament Nested Resources: Managing Courses and Their Lessons
Laravel #Filament #Laravel #Nested Resources #Admin Panel #PHP

Filament Nested Resources: Managing Courses and Their Lessons

2 min read Mohamed Said Mohamed Said

Managing hierarchical data within an administration panel is a common requirement for many web applications. For Laravel developers using Filament, implementing nested resources can significantly improve the user experience by allowing direct management of related records. This tutorial demonstrates how to set up nested resources in Filament, using the example of managing Courses and their associated Lessons.

Understanding Nested Resources

In Laravel, nested resources refer to the relationship where one resource controller is nested within another. For instance, a Lesson is typically associated with a Course, making it a nested resource. Filament provides a straightforward way to integrate this concept into its admin panel.

Our goal is to achieve the following:

  1. From the list of courses, provide a direct link to manage the lessons for each specific course.
  2. The lesson management page should clearly display the parent course's title and include breadcrumbs that reflect this hierarchy.

Preparing the Lesson Resource

To enable nested resource management, we need to configure the LessonResource. Several key adjustments are necessary:

  • Define a new route slug: We'll set a specific slug for the LessonResource to indicate its nested nature, such as courses/lessons.
  • Prevent navigation registration: Since lessons are managed within the context of a course, the LessonResource itself shouldn't appear as a top-level item in the Filament navigation.
  • Modify the getEloquentQuery(): This is crucial for filtering lessons to show only those belonging to the currently selected course. We'll use the record parameter from the request.
  • Adjust getPages(): We need to define custom routes for listing, creating, and editing lessons, ensuring they accept the parent record parameter.

Here's the configuration for app/Filament/Resources/LessonResource.php:

<?php

namespace App\Filament\Resources;

use App\Filament\Resources\LessonResource\Pages;
use App\Models\Lesson;
use Filament\Forms;
use Filament\Resources\Resource;
use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;
use Filament\Forms\Form;
use Filament\Tables\Table;

class LessonResource extends Resource
{
    protected static ?string $model = Lesson::class;

    protected static ?string $slug = 'courses/lessons';

    protected static bool $shouldRegisterNavigation = false;

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\TextInput::make('title')
                    ->required(),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('title')
                    ->searchable()
                    ->sortable(),
            ])
            ->filters([
                //
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\DeleteBulkAction::make(),
            ]);
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListLessons::route('/'),
            'lessons' => Pages\ListLessons::route('/{record}'),
            'create' => Pages\CreateLesson::route('/{record}/create'),
            'edit' => Pages\EditLesson::route('/{record}/edit'),
        ];
    }

    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()->where('course_id', request('record'));
    }
}

Adding an Action to the Course Resource

With the LessonResource prepared, we can now add an action to the CourseResource that links to the lesson management page for each course.

In app/Filament/Resources/CourseResource.php, we'll add a new action to the actions() method:

<?php

namespace App\Filament\Resources;

use App\Filament\Resources\CourseResource\Pages;
use App\Models\Course;
use Filament\Forms;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Forms\Form;
use Filament\Tables\Table;

class CourseResource extends Resource
{
    protected static ?string $model = Course::class;

    protected static ?string $navigationIcon = 'heroicon-o-collection';

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\TextInput::make('title')
                    ->required(),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('title')
                    ->searchable()
                    ->sortable(),
                Tables\Columns\TextColumn::make('lessons_count')
                    ->counts('lessons'),
            ])
            ->filters([
                //
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\DeleteBulkAction::make(),
            ])
            ->prependActions([
                Tables\Actions\Action::make('View lessons')
                    ->color('success')
                    ->icon('heroicon-s-view-list')
                    ->url(fn (Course $record): string => LessonResource::getUrl('lessons', ['record' => $record]))
            ]);
    }

    public static function getRelations(): array
    {
        return [
            //
        ];
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListCourses::route('/'),
            'create' => Pages\CreateCourse::route('/create'),
            'edit' => Pages\EditCourse::route('/{record}/edit'),
        ];
    }
}

Customizing the Create Lesson Action

To ensure that the "Create Lesson" button on the ListLessons page correctly associates the new lesson with the parent course, we need to modify its URL generation. This is done within the ListLessons page class.

In app/Filament/Resources/LessonResource/Pages/ListLessons.php:

<?php

namespace App\Filament\Resources\LessonResource\Pages;

use App\Filament\Resources\LessonResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;

class ListLessons extends ListRecords
{
    protected static string $resource = LessonResource::class;

    protected function getActions(): array
    {
        return [
            Actions\CreateAction::make()
                ->url(fn (): string => LessonResource::getUrl('create', ['record' => request('record')])),
        ];
    }
}

Takeaways

  • Filament's nested resources allow for intuitive management of related data within the admin panel.
  • Configuring custom routes and query scopes in the child resource (LessonResource) is key to filtering data correctly.
  • Adding custom actions in the parent resource (CourseResource) provides direct links to manage nested resources.
  • Ensuring create actions pass the parent record ID maintains the relationship integrity.

This setup provides a clean and efficient way to manage hierarchical data, enhancing the usability of your Filament-powered administration interfaces.

Source: Filament Nested Resources: Manage Courses and their Lessons

Found this useful?

Frequently Asked Questions

3 questions
Q01 How do I prevent a nested resource from appearing in the main Filament navigation?
In the nested resource's Resource class (e.g., LessonResource), set the static property $shouldRegisterNavigation to false.
Q02 How can I filter the nested resource list to show only items related to a parent record?
Override the getEloquentQuery() method in the nested resource's Resource class. Use the request('record') parameter to filter the query based on the parent's ID.
Q03 How do I create a link from a parent resource's table to manage its nested resources?
Add a custom Tables\Actions\Action to the parent resource's table configuration. Use the url() method to generate the URL to the nested resource's index page, passing the parent record as a parameter.

Continue reading

More Articles

View all