Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Relation Pages Laravel Package

gheith3/filament-relation-pages

Filament Relation Pages lets you add fully custom, free-form tabs alongside Relation Managers in any resource—no forced table or relationship. Build pages with Filament schema components, Blade/HTML, Alpine.js, and more. Includes an artisan generator.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require gheith3/filament-relation-pages
    

    (No manual service provider registration needed—auto-discovered.)

  2. Generate a relation page:

    php artisan make:filament-relation-page BuildingSummaryPage --resource=Buildings
    

    This creates:

    • app/Filament/Resources/Buildings/RelationPages/BuildingSummaryPage.php
    • resources/views/filament/resources/buildings/building-summary-page.blade.php
  3. Add content to the generated class:

    use gheith3\FilamentRelationPages\RelationPage;
    use Filament\Schemas\Components\Section;
    use Filament\Schemas\Schema;
    
    class BuildingSummaryPage extends RelationPage
    {
        protected static ?string $title = 'Summary';
        protected static string $icon = 'heroicon-o-chart-bar';
    
        public function content(Schema $schema): Schema
        {
            return $schema->components([
                Section::make('Overview')->schema([
                    // Add Filament components here
                ]),
            ]);
        }
    }
    
  4. Register the page in your resource:

    public static function getRelations(): array
    {
        return [
            RelationPages\BuildingSummaryPage::class,
            // Other relations...
        ];
    }
    

First Use Case

Create a custom dashboard tab for a parent model (e.g., Building) that displays aggregated data (e.g., summary stats, charts, or custom HTML) without forcing a table or relationship. Example:

  • A "Financial Overview" tab for Company resources showing revenue trends.
  • A "User Activity" tab for Project resources with Alpine.js-powered interactivity.

Implementation Patterns

Core Workflow

  1. Extend RelationPage: Override content(Schema $schema) to define Filament schema components or use Blade views for raw HTML/Alpine.js.

  2. Leverage Filament’s Schema System:

    public function stats(Schema $schema): Schema
    {
        return $schema->columns(2)->schema([
            TextEntry::make('active_users')->state($this->ownerRecord->users()->active()->count()),
            TextEntry::make('recent_activity')->state($this->ownerRecord->lastActivity()),
        ]);
    }
    

    Access these in Blade via {{ $this->stats }}.

  3. Dynamic Badges:

    public static function getBadge(): ?string
    {
        return $this->ownerRecord->pendingTasks()->count();
    }
    
  4. Conditional Visibility:

    public static function canViewForRecord(Model $ownerRecord): bool
    {
        return $this->ownerRecord->isPremium;
    }
    
  5. Lazy Loading:

    protected static bool $isLazy = true; // Loads content only when tab is clicked
    

Integration Tips

  • Mix Filament Components + Raw HTML:

    <div class="space-y-6">
        {{ $this->content }} <!-- Filament schema -->
        <div x-data="{ open: false }">
            <!-- Alpine.js interactivity -->
        </div>
    </div>
    
  • Pass Livewire Props:

    public static function getDefaultProperties(): array
    {
        return ['theme' => 'dark'];
    }
    

    Access via $this->theme in the class or Blade.

  • Reuse Across Resources: Create a base RelationPage class (e.g., BaseAnalyticsPage) and extend it for consistency.

  • Panel-Specific Placement: Use --panel=admin in the Artisan command to place pages in app/Filament/Admin/Resources/....


Gotchas and Tips

Pitfalls

  1. Static Method Conflicts: Override canViewForRecord(), getTabComponent(), or getDefaultProperties() only if needed. Filament expects these methods to exist, but modifying their signatures may break compatibility.

  2. Livewire Property Locking: $ownerRecord and $pageClass are #[Locked]—avoid modifying them directly in client-side logic (e.g., Alpine.js). Use emit() to trigger server-side updates.

  3. Schema Method Naming: Only methods with the signature *($schema): Schema are auto-discovered. Name them descriptively (e.g., financialSummary()).

  4. Blade View Paths: Ensure Blade views match the generated path (resources/views/filament/resources/{resource}/{page-name}.blade.php). Use php artisan view:clear if paths break after updates.

  5. Lazy Loading Overhead: Set $isLazy = true for heavy pages, but test performance—each tab click triggers a Livewire mount.

Debugging

  • Tab Not Appearing? Check:

    • The class is in getRelations().
    • canViewForRecord() returns true.
    • No typos in the class name or namespace.
  • Schema Not Rendering? Verify:

    • The method name matches the Blade usage (e.g., {{ $this->stats }} requires stats(Schema $schema)).
    • The $ownerRecord is accessible (debug with dd($this->ownerRecord)).
  • Alpine.js Not Working? Ensure the Blade view includes Filament’s Livewire/Alpine setup:

    @filamentScripts
    <div x-data="{}">...</div>
    

Extension Points

  1. Custom Stubs: Publish and modify stubs for project-wide templates:

    php artisan vendor:publish --tag=filament-relation-pages-stubs
    
  2. Global Badges/Icons: Extend the RelationPage class to add default styles or logic:

    abstract class CustomRelationPage extends RelationPage
    {
        protected static string $icon = 'heroicon-o-document-text';
    }
    
  3. Shared Logic: Create a trait for reusable functionality (e.g., caching, permissions):

    trait HasCachedData
    {
        protected ?array $cachedData;
    
        public function getCachedData(): array
        {
            return $this->cachedData ??= $this->fetchData();
        }
    }
    
  4. Testing: Mock ownerRecord in unit tests:

    $page = new BuildingSummaryPage();
    $page->ownerRecord = Building::factory()->create();
    $this->assertTrue($page->canViewForRecord($page->ownerRecord));
    

Performance

  • Avoid N+1 Queries: Eager-load relationships in getDefaultProperties() or canViewForRecord():
    public static function getDefaultProperties(): array
    {
        return ['users' => $ownerRecord->users()->with('roles')->get()];
    }
    
  • Lazy-Load Heavy Data: Use $isLazy = true and load data only when the tab is active.

Config Quirks

  • No Plugin Registration Needed: The package works without calling ->plugin(RelationPagesPlugin::make()) in your panel. The RelationPagesPlugin class exists for Filament’s plugin directory convention only.

  • Filament Version Compatibility: Test with both Filament v4 and v5. The package abstracts differences, but complex schemas may need adjustments for breaking changes (e.g., component signatures).

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon
itsemon245/lamet
baks-dev/dashboard
amoifr/pickle-panther-bundle
make-dev/orca
dmstr/symfony-system-resources-bundle
dmstr/symfony-job-queue-bundle
dmstr/openapi-json-schema-bundle
dmstr/keycloak-security-bundle
dmstr/doctrine-audit-log-bundle