承接 agroezinger/filament-shield-enhanced 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

agroezinger/filament-shield-enhanced

Composer 安装命令:

composer require agroezinger/filament-shield-enhanced

包简介

Fine-grained page permissions & structured UI addon for bezhansalleh/filament-shield.

README 文档

README

Plumb score Plumb security score Plumb maintenance score Plumb ecosystem score Scanned by Plumb

Warning

Testing Phase: Versions 0.0.* are currently in the testing phase. At present, there are no known bugs.

A standalone addon for bezhansalleh/filament-shield that adds fine-grained page and resource permissions and a structured Role Resource UI — without forking or replacing the original package.

Why this exists.
The features were proposed upstream in bezhanSalleh/filament-shield#698. The author has not had time to review the PR. This addon ships the same functionality as a composable layer on top of the official package.

Features

Feature Description
Multi-action page permissions Declare several permissions per page via getShieldPagePermissions().
Multi-action resource permissions Declare custom permissions per resource via getShieldResourcePermissions() — beyond the standard CRUD policy methods.
canShield('action') Fluent, type-safe permission check — instance method on Pages, static method on Resources.
getShieldPermissions() Returns a pre-resolved action → bool map for injection into child Livewire components.
HasInjectedShieldPermissions Trait for child Livewire components that receive the map from the parent page.
EnhancedPagePermissionsForm Form builder helper for the published RoleResource — renders each enhanced page as a separate Section with individual checkboxes.
EnhancedResourcePermissionsForm Form builder helper for the published RoleResource — renders each enhanced resource as a separate Section with individual checkboxes.
Three-part page key convention {Prefix}{sep}{Action}{sep}{Subject} (e.g. Page:EditSettings:SettingsPage) — fully respects filament-shield's separator and case config.
Two-part resource key convention {Action}{sep}{ModelBasename} (e.g. ViewContactInfo:Member) — matches Shield's own resource permission format, no extra prefix.
Zero conflict Does not replace any original class. Falls back gracefully on entities that do not declare the method.

Requirements

Dependency Version
PHP ^8.2
Laravel ^11.0 | ^12.0 | ^13.0
Filament ^4.0 | ^5.0
bezhansalleh/filament-shield ^4.0

Installation

composer require agroezinger/filament-shield-enhanced

Publish the config (optional):

php artisan vendor:publish --tag="filament-shield-enhanced-config"

Usage — Pages

1 — Declare fine-grained permissions on a Page

Replace (or complement) the original HasPageShield with the enhanced version:

<?php

namespace App\Filament\Pages;

use Agroezinger\FilamentShieldEnhanced\Traits\HasPageShield;
use Filament\Pages\Page;

class SettingsPage extends Page
{
    use HasPageShield;

    /**
     * Declare every action that can be independently granted on this page.
     * The 'view' action controls whether the user can navigate to the page at all.
     *
     * Three entry formats can be mixed freely:
     *
     *   'action'                          → label auto-generated from action name
     *   'action' => 'Label'               → explicit label
     *   'action' => ['text'        => 'Label',
     *                'description' => 'Shown below the checkbox in the role editor']
     */
    public static function getShieldPagePermissions(): array
    {
        return [
            'view'               => 'Can view this page',
            'editGlobalSettings' => [
                'text'        => 'Can change global settings',
                'description' => 'Grants access to all fields in the Global Settings section.',
            ],
            'exportData'         => 'Can export data as CSV / Excel',
        ];
    }
}

Then run the enhanced generator to create the permissions in the database:

php artisan shield:generate-enhanced-pages --all-panels

Use --panel=<id> to limit the scan to a single panel.

This will create three permissions for the page above:

Page:View:SettingsPage
Page:EditGlobalSettings:SettingsPage
Page:ExportData:SettingsPage

2 — Check permissions in PHP (Pages)

// Inside the Page class
if ($this->canShield('editGlobalSettings')) {
    // Perform restricted action
}
{{-- Inside the Page Blade view --}}
@if($this->canShield('exportData'))
    <x-filament::button wire:click="export">Export</x-filament::button>
@endif

3 — Inject permissions into child Livewire components

Parent page Blade:

@livewire('settings-sidebar', [
    'permissions' => $this->getShieldPermissions()
])

Child Livewire component:

<?php

namespace App\Livewire;

use Agroezinger\FilamentShieldEnhanced\Traits\HasInjectedShieldPermissions;
use Livewire\Component;

class SettingsSidebar extends Component
{
    use HasInjectedShieldPermissions;

    // $this->permissions is automatically populated by Livewire.

    public function save(): void
    {
        $this->authorizeShield('editGlobalSettings'); // aborts 403 if not permitted
        // … save logic
    }

    public function render()
    {
        return view('livewire.settings-sidebar');
    }
}

Usage — Resources

4 — Declare fine-grained permissions on a Resource

Add HasResourceShield to any Filament Resource and declare custom actions via getShieldResourcePermissions():

<?php

namespace App\Filament\Resources;

use Agroezinger\FilamentShieldEnhanced\Traits\HasResourceShield;
use App\Models\Member;
use Filament\Resources\Resource;

class MemberResource extends Resource
{
    use HasResourceShield;

    protected static ?string $model = Member::class;

    /**
     * Declare custom permissions beyond the standard CRUD policy methods.
     * Keys are action names; values are human-readable labels (shown in the role editor).
     *
     * Same three entry formats as getShieldPagePermissions():
     *   'action'                                   → auto-generated label
     *   'action' => 'Label'                        → explicit label
     *   'action' => ['text' => '...', 'description' => '...']
     */
    public static function getShieldResourcePermissions(): array
    {
        return [
            'Export'          => 'Export member list (basic data)',
            'ExportFinance'   => 'Export member list including financial data (IBAN, fees)',
            'ViewContactInfo' => 'View contact details (email, phone, address)',
            'ViewBankingInfo' => 'View bank details (IBAN, BIC, account holder)',
        ];
    }
}

Then create the permissions in the database:

php artisan shield:generate-enhanced-resources --all-panels

This will create (for the example above):

Export:Member
ExportFinance:Member
ViewContactInfo:Member
ViewBankingInfo:Member

The key format (Action:ModelBasename) is identical to Shield's own resource permission format so everything looks consistent.

5 — Check resource permissions in PHP

canShield() is a static method on Resources (unlike Pages, where it is an instance method):

// Anywhere in your application
if (MemberResource::canShield('ViewContactInfo')) {
    // show contact section
}

// Returns ['Export' => true, 'ViewContactInfo' => false, …]
$permissions = MemberResource::getShieldPermissions();

Super-admin bypass is applied automatically — identical behaviour to the page trait.

6 — Structured UI in the published RoleResource

After publishing the RoleResource with php artisan shield:publish --panel=<id> two files need small changes.

6a — RoleResource: add both enhanced tabs

Open the published RoleResource.php and override two methods:

use Agroezinger\FilamentShieldEnhanced\Forms\EnhancedPagePermissionsForm;
use Agroezinger\FilamentShieldEnhanced\Forms\EnhancedResourcePermissionsForm;
use BezhanSalleh\FilamentShield\Facades\FilamentShield;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;

/**
 * Exclude pages that declare getShieldPagePermissions() from the standard
 * "Pages" tab — they are managed exclusively by the Enhanced tab.
 */
public static function getPageOptions(): array
{
    return collect(FilamentShield::getPages())
        ->reject(fn(array $page) => method_exists($page['pageFqcn'], 'getShieldPagePermissions'))
        ->flatMap(fn(array $page) => $page['permissions'])
        ->toArray();
}

public static function getShieldFormComponents(): \Filament\Schemas\Components\Component
{
    $enhancedPageComponents     = EnhancedPagePermissionsForm::make();
    $enhancedPageCount          = count(EnhancedPagePermissionsForm::getPagePermissionFields());

    $enhancedResourceComponents = EnhancedResourcePermissionsForm::make();
    $enhancedResourceCount      = count(EnhancedResourcePermissionsForm::getResourcePermissionFields());

    $tabs = [
        static::getTabFormComponentForResources(),
        static::getTabFormComponentForPage(),
        static::getTabFormComponentForWidget(),
        static::getTabFormComponentForCustomPermissions(),
    ];

    if (! empty($enhancedResourceComponents)) {
        $tabs[] = Tab::make('enhanced_resources')
            ->label('Resources (Fine-grained)')
            ->badge($enhancedResourceCount ?: null)
            ->schema($enhancedResourceComponents);
    }

    if (! empty($enhancedPageComponents)) {
        $tabs[] = Tab::make('enhanced_pages')
            ->label('Pages (Fine-grained)')
            ->badge($enhancedPageCount ?: null)
            ->schema($enhancedPageComponents);
    }

    return Tabs::make('Permissions')
        ->contained()
        ->tabs($tabs)
        ->columnSpan('full');
}

Each Resource that declares getShieldResourcePermissions() appears in the "Resources (Fine-grained)" tab as its own Section with individual checkboxes.

Note: Shield's standard "Resources" tab only shows CRUD policy method permissions (ViewAny, Create, Update, …). Custom resource actions do not appear there — no duplicate-filtering override is needed.

6b — EditRole: add the pre-fill trait

Open the published EditRole.php and add use HasEnhancedRoleForm. This pre-fills both page- and resource-permission checkboxes when the form opens.

use Agroezinger\FilamentShieldEnhanced\Traits\HasEnhancedRoleForm;

class EditRole extends EditRecord
{
    use HasEnhancedRoleForm;

    // … rest of the file unchanged
}

The mutateFormDataBeforeSave() / afterSave() logic from Shield's own EditRole handles saving — no additional overrides needed.

Configuration

// config/filament-shield-enhanced.php

return [
    'pages' => [
        // First segment of the three-part key: Page:Action:Subject
        'permission_prefix' => 'Page',
    ],

    'ui' => [
        'grid_columns' => [
            'default' => 1,
            'sm'      => 2,
            'lg'      => 3,
        ],

        'checkbox_list_columns' => [
            'default' => 1,
            'sm'      => 2,
        ],
    ],
];

How it works internally

This addon does not override any class from filament-shield. Instead it uses the package's public extension point:

FilamentShield::buildPermissionKeyUsing(function (...) { ... });

When a Page class exposes getShieldPagePermissions(), the addon intercepts the key builder and applies its three-part naming convention. All other entities (Resources, Widgets, regular Pages) are delegated back to the original builder unchanged.

Resource permissions use a two-part format matching Shield's own convention and are not created via shield:generate — only via shield:generate-enhanced-resources. This means the hook is not involved for Resources at all.

Upgrading from the fork

If you previously used the agroezinger/filament-shield fork (which is a modified copy of the original package):

  1. Switch composer.json back to the official package:
    composer remove agroezinger/filament-shield
    composer require bezhansalleh/filament-shield agroezinger/filament-shield-enhanced
  2. Replace use BezhanSalleh\FilamentShield\Traits\HasPageShield with
    use Agroezinger\FilamentShieldEnhanced\Traits\HasPageShield in your pages.
  3. Replace use BezhanSalleh\FilamentShield\Traits\HasInjectedShieldPermissions (if used) with
    use Agroezinger\FilamentShieldEnhanced\Traits\HasInjectedShieldPermissions.
  4. Re-run php artisan shield:generate --all so the new three-part keys are created.

Changelog

See CHANGELOG.md.

License

MIT — see LICENSE.md.

Credits

agroezinger/filament-shield-enhanced 适用场景与选型建议

agroezinger/filament-shield-enhanced 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 324 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 05 月 28 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「laravel」 「permission」 「rbac」 「filament」 「filament-shield」 「agroezinger」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 agroezinger/filament-shield-enhanced 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 agroezinger/filament-shield-enhanced 我们能提供哪些服务?
定制开发 / 二次开发

基于 agroezinger/filament-shield-enhanced 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 324
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 41
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 0
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-28