coringawc/filament-single-record-resource

A Filament plugin that solves the single-record resource pattern — resources that display exactly one record per authenticated user (e.g. 'My Profile', 'My Wallet', 'My Settings') without an index page, and their arbitrarily nested child resources.

Maintainers

Package info

github.com/CoringaWc/filament-single-record-resource

Homepage

Issues

pkg:composer/coringawc/filament-single-record-resource

Fund package maintenance!

CoringaWc

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 0

1.0.0 2026-03-07 16:17 UTC

This package is auto-updated.

Last update: 2026-03-07 16:18:10 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

This package implements the single-record resource pattern for Filament panels.

Instead of a list page (index) with many records, you open one resource that always resolves to one business record per authenticated user.

Common examples:

  • My Profile
  • My Wallet
  • My Settings
  • Current Subscription

Compatibility

  • Filament ^4.0 || ^5.0
  • Laravel versions supported by the selected Filament major

Installation

composer require coringawc/filament-single-record-resource

Core Concepts

This package is based on two traits:

  1. HasSingleRecordResource (Resource trait)
  • Redirects index/navigation behavior to view
  • Keeps sidebar navigation working without an index page
  • Helps nested resources resolve root URLs/slugs in single-record chains
  1. HasSingleRecord (Page trait for ViewRecord and EditRecord)
  • Resolves the root single record automatically
  • Supports custom resolution via builder or custom resolver method
  • Normalizes breadcrumbs in deep nested resources

Step-by-Step Implementation

1. Create your resource as single-record root

In your Filament Resource, use HasSingleRecordResource and register only view (and optionally edit) pages.

<?php

namespace App\Filament\Resources\MyWallets;

use App\Filament\Resources\MyWallets\Pages\EditMyWallet;
use App\Filament\Resources\MyWallets\Pages\ViewMyWallet;
use CoringaWc\FilamentSingleRecordResource\Traits\HasSingleRecordResource;
use Filament\Resources\Resource;

class MyWalletResource extends Resource
{
    use HasSingleRecordResource;

    public static function getPages(): array
    {
        return [
            'view' => ViewMyWallet::route('/'),
            'edit' => EditMyWallet::route('/edit'),
        ];
    }
}

2. Use HasSingleRecord in ViewRecord

<?php

namespace App\Filament\Resources\MyWallets\Pages;

use App\Filament\Resources\MyWallets\MyWalletResource;
use CoringaWc\FilamentSingleRecordResource\Traits\HasSingleRecord;
use Filament\Resources\Pages\ViewRecord;

class ViewMyWallet extends ViewRecord
{
    use HasSingleRecord;

    protected static string $resource = MyWalletResource::class;
}

3. Optional: also use HasSingleRecord in EditRecord

Yes, this package supports EditRecord too.

<?php

namespace App\Filament\Resources\MyWallets\Pages;

use App\Filament\Resources\MyWallets\MyWalletResource;
use CoringaWc\FilamentSingleRecordResource\Traits\HasSingleRecord;
use Filament\Resources\Pages\EditRecord;

class EditMyWallet extends EditRecord
{
    use HasSingleRecord;

    protected static string $resource = MyWalletResource::class;
}

Automatic Record Resolution (1:1 with authenticated user)

By default, HasSingleRecord calls a builder that applies whereBelongsTo(Filament::auth()->user()).

In practice, this works automatically when your resource model has a belongsTo(User::class) relation that points to the authenticated Filament user.

Typical 1:1 setup:

  • User hasOne Wallet
  • Wallet belongsTo User

Example model relationships:

// App\Models\User
public function wallet(): HasOne
{
    return $this->hasOne(Wallet::class);
}

// App\Models\Wallet
public function user(): BelongsTo
{
    return $this->belongsTo(User::class);
}

With this, your single-record root page resolves the wallet for the logged-in user automatically.

Custom Resolution Strategies

If your rule is not a simple belongsTo(user), override one of the methods below in the page class.

A) Customize builder (resolveSingleRecordBuilder)

protected function resolveSingleRecordBuilder(Builder $query): Builder
{
    return parent::resolveSingleRecordBuilder($query)
        ->where('active', true);
}

B) Full custom resolver (resolveSingleRecord)

Use this when you need firstOrCreate, tenant logic, or complex business rules.

protected function resolveSingleRecord(): ?Model
{
    /** @var \App\Models\User|null $user */
    $user = filament()->auth()->user();

    if ($user === null) {
        return null;
    }

    return $user->wallet()->firstOrCreate([]);
}

Nested Resources

For nested chains (for example MyWallet -> Companies -> Products):

  1. Keep HasSingleRecordResource on resources that follow the single-record flow
  2. Keep HasSingleRecord in deep ViewRecord/EditRecord pages
  3. If removing parent IDs from URLs, enforce strict query scoping in your models/pages

This package also helps preserve breadcrumb consistency in deep nested routes.

Screenshots

MyWallet (Root Single Resource)

MyWallet page

Deep Nested Resource (MyWallet -> Companies -> Products)

Deep nested product page

Testing

Run tests:

composer test

The package CI validates the plugin in two scenarios:

  • Filament 4 latest stable in major
  • Filament 5 latest stable in major

Changelog

Please see CHANGELOG for details.

Contributing

Please see CONTRIBUTING.

Security

Please review our security policy.

Credits

License

The MIT License (MIT). See LICENSE.md.