coringawc/filament-single-record-resource
Implements the single-record resource pattern for Filament. Replace index lists with a per-user “one record” resource (Profile, Settings, Wallet, Subscription). Traits handle routing, navigation, auth fallbacks, record resolution, and nested breadcrumbs.
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:
^4.0 || ^5.0composer require coringawc/filament-single-record-resource
This package is based on two traits:
It also exposes an explicit contract interface, SingleRecordResolvableResource, for Resources that want first-class static-analysis support.
HasSingleRecordResource (Resource trait)viewindex pageview authorization on the resolved record when viewAny is deniedHasSingleRecord (Page trait for ViewRecord and EditRecord)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\Contracts\SingleRecordResolvableResource;
use CoringaWc\FilamentSingleRecordResource\Traits\HasSingleRecordResource;
use Filament\Resources\Resource;
class MyWalletResource extends Resource implements SingleRecordResolvableResource
{
use HasSingleRecordResource;
public static function getPages(): array
{
return [
'view' => ViewMyWallet::route('/'),
'edit' => EditMyWallet::route('/edit'),
];
}
}
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;
}
HasSingleRecord in EditRecordYes, 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;
}
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 WalletWallet belongsTo UserExample 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.
For explicit static-analysis support, implement SingleRecordResolvableResource on Resources using HasSingleRecordResource. The package still accepts legacy Resources that define the same methods manually, but the interface is now the recommended public contract.
Filament normally uses viewAny() to decide whether a Resource can register navigation and be accessed at the Resource level.
For a root single-record resource, that default is too strict because there is no collection UX. This package now treats the Resource as accessible when:
viewAny() is allowed, orviewAny() is denied but view() is allowed for the resolved single recordThis means a policy can intentionally deny listing while still allowing the user to open their own single record.
If your rule is not a simple belongsTo(user), prefer overriding one of the methods below on the Resource so authorization and page loading stay aligned.
resolveSingleRecordBuilder)public static function resolveSingleRecordBuilder(Builder $query): Builder
{
return parent::resolveSingleRecordBuilder($query)
->where('active', true);
}
resolveSingleRecord)Use this when you need firstOrCreate, tenant logic, or complex business rules.
public static function resolveSingleRecord(): ?Model
{
/** @var \App\Models\User|null $user */
$user = filament()->auth()->user();
if ($user === null) {
return null;
}
return $user->wallet()->firstOrCreate([]);
}
If a specific ViewRecord/EditRecord page truly needs different resolution behavior than the Resource, you can still override the page methods:
protected function resolveSingleRecordBuilder(Builder $query): Builder
{
return parent::resolveSingleRecordBuilder($query)
->where('active', true);
}
For nested chains (for example MyWallet -> Companies -> Products):
HasSingleRecordResource on resources that follow the single-record flowHasSingleRecord in deep ViewRecord/EditRecord pagesSingleRecordResolvableResource on Resources using HasSingleRecordResource so static analysis can understand the contract explicitlyThis package also helps preserve breadcrumb consistency in deep nested routes.


Run tests:
composer test
The package CI validates the plugin in two scenarios:
Please see CHANGELOG for details.
Please see CONTRIBUTING.
Please review our security policy.
The MIT License (MIT). See LICENSE.md.
How can I help you explore Laravel packages today?