ghanem/friendship-filament

Filament admin panel plugin for the ghanem/friendship package — moderate friendships, requests and blocks.

Maintainers

Package info

github.com/gaitco/friendship-filament

pkg:composer/ghanem/friendship-filament

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-02 13:27 UTC

This package is auto-updated.

Last update: 2026-08-02 13:45:15 UTC


README

tests Latest Version on Packagist Total Downloads License

A Filament admin panel plugin for ghanem/friendship: browse and moderate friendships, inspect any user's social graph, and see panel stats.

Requirements

  • PHP 8.2+
  • Laravel 11.28+
  • Filament 4 or 5
  • ghanem/friendship 2.1+

The core ghanem/friendship package supports Laravel 10, but Filament itself requires Laravel 11.28+ — so this plugin starts there too, even though the package it wraps goes further back.

Installation

composer require ghanem/friendship-filament
php artisan vendor:publish --tag=friendship-filament-config

Register the plugin in your panel provider:

use Ghanem\FriendshipFilament\FriendshipPlugin;

public function panel(Panel $panel): Panel
{
    return $panel->plugin(FriendshipPlugin::make());
}

Configuration

Friendships are polymorphic, so the panel needs to know how to display each model type. Map them in config/friendship-filament.php:

'models' => [
    \App\Models\User::class => [
        'label' => 'name',                                  // see label forms below
        'resource' => \App\Filament\Resources\UserResource::class,  // optional deep link
    ],
],

Other keys in the same file:

'fallback_label' => null,      // used before the "Type #id" default; same forms as 'label'
'deleted_label' => 'Deleted record',
'navigation_group' => null,
'navigation_sort' => null,

Label forms

label and fallback_label accept any of:

Form Example config:cache safe
Attribute name 'name'
Invokable class-string \App\Support\UserLabel::class
[class, method] pair [\App\Support\UserLabel::class, 'make']
Closure fn ($user) => $user->full_name

Do not use closures in production. php artisan config:cache serialises config with var_export() and throws LogicException: Your configuration files are not serializable on any closure — and the error names your config file, not this package. Closures stay supported for local development; use one of the first three forms anywhere config:cache runs.

An invokable class or [class, method] pair is resolved from the container and receives the model:

namespace App\Support;

class UserLabel
{
    public function __invoke(\App\Models\User $user): string
    {
        return $user->first_name.' '.$user->last_name;
    }
}

Types with no entry in models fall back to fallback_label if set, otherwise to User #12-style labels. Rows whose model has been deleted render as deleted_label plus the row's own morph context — Deleted record (User #12) — so orphaned rows stay identifiable.

navigation_group and navigation_sort control where FriendshipResource appears in the panel navigation. navigation_group is only used when no FriendshipPlugin registered on the current panel has set one via ->navigationGroup() — see Customising the plugin. There is no plugin-level equivalent for navigation_sort; it is always read from config.

What you get

Friendships Resource

A read-only moderation table of every friendship: sender, recipient, status badge, and timestamps, filterable by status and sender type. Admins do not create friendships — users do — so there is no create or edit page, only a list and a view page.

Friends relation manager

Add it to your own UserResource to see a user's friendships inline:

use Ghanem\FriendshipFilament\RelationManagers\FriendsRelationManager;

public static function getRelations(): array
{
    return [
        FriendsRelationManager::class,
    ];
}

That's the only step required — the manager builds its own bidirectional query and doesn't need a friendships() relation on your model. It lists friendships in both directions — rows where the user sent the request and rows where they received it — showing the other party and whether the friendship was incoming or outgoing.

Its visibility follows your app's own authorization: if you register a policy for Ghanem\Friendship\Models\Friendship, the relation manager is shown only when viewAny is allowed; if you don't register one, it's shown by default.

Moderation actions

Action Applies to Effect
Approve request Pending Accepts on the recipient's behalf
Unfriend Accepted Removes the friendship for both people
Lift block Blocked Lifts the block as the blocker

Each requires confirmation and is hidden when it doesn't apply. All of them go through the core package's public API (acceptFriendRequest(), unfriend(), unblock()), so your event listeners still fire. Available on both the Resource's table and the relation manager's table.

Widgets

FriendshipStatsWidget (accepted, pending, blocked counts) and FriendshipsChartWidget (accepted friendships formed per day, last 30 days). Both are registered by default; opt out with ->widgets(false).

Customising the plugin

FriendshipPlugin::make()
    ->navigationGroup('Community')
    ->widgets(false)     // skip the dashboard widgets
    ->resource(false);   // skip the Resource, keep the relation manager

Authorization

No policy ships with this package. Register your own policy for Ghanem\Friendship\Models\Friendship and Filament — and the relation manager's own visibility check (see above) — will honour it. Moderation actions are destructive — gate them deliberately.

Note that FriendsRelationManager overrides canViewForRecord(), so the inherited skipAuthorization() / checkPolicyExistence() toggles do not affect whether its tab is visible; that is decided solely by the override described above. Those toggles still apply to row-level actions inside the relation manager.

Testing

composer test

Related packages

License

MIT. See LICENSE.