Filament + Laravel 12 项目骨架
目标:在 Laravel 12 + MySQL 上,快速搭建一套 Filament Admin 后台,内置权限(Spatie + Shield)、Dashboard、商品 CRUD(ProductResource)、中文本地化、基础种子数据。
1) 新建项目 & 依赖安装
# 1. 创建项目(Laravel 12)
composer create-project laravel/laravel:"^12.0" filament-skeleton
cd filament-skeleton
# 2. 配置 .env (数据库、缓存、队列等)
cp .env.example .env
php artisan key:generate
# 3. 安装 Filament(v3)
composer require filament/filament:"^3.2" -W
php artisan filament:install
# 4. 安装权限(Spatie)+ Filament Shield(自动生成资源权限)
composer require spatie/laravel-permission:"^6.7" -W
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations"
php artisan migrate
composer require bezhansalleh/filament-shield:"^8.0"
php artisan vendor:publish --tag=filament-shield-config
# 5.(可选)中文语言包
composer require filament/laravel-translations:"^3.0"
php artisan vendor:publish --tag=filament-translations
.env建议:APP_NAME="Admin" APP_LOCALE=zh_CN APP_FALLBACK_LOCALE=zh_CN APP_TIMEZONE=Asia/Shanghai DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=filament_skeleton DB_USERNAME=root DB_PASSWORD=secret
2) 用户模型启用权限 Trait
编辑 app/Models/User.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasRoles; // ← 权限 Trait
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
}注意:Spatie 使用webguard;如需自定义 guard,请同步在config/auth.php和config/permission.php中调整。
3) 基础数据表:Products
3.1 迁移
php artisan make:model Product -mfs生成的迁移(database/migrations/xxxx_xx_xx_create_products_table.php)示例:
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('sku')->unique();
$table->unsignedInteger('stock')->default(0);
$table->decimal('price', 10, 2)->default(0);
$table->enum('status', ['draft', 'active', 'disabled'])->default('draft');
$table->json('images')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['status']);
});
}3.2 模型(app/Models/Product.php)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name', 'sku', 'stock', 'price', 'status', 'images',
];
protected $casts = [
'images' => 'array',
'price' => 'decimal:2',
];
}3.3 工厂(database/factories/ProductFactory.php)
public function definition(): array
{
return [
'name' => fake()->words(3, true),
'sku' => strtoupper(fake()->bothify('SKU-####-??')),
'stock' => fake()->numberBetween(0, 999),
'price' => fake()->randomFloat(2, 10, 9999),
'status' => fake()->randomElement(['draft','active','disabled']),
'images' => [],
];
}4) Filament 基础安装 & 用户
# 安装向导已完成(前文第 1 步),这里创建后台用户
php artisan make:filament-user
# 依提示输入 name/email/password 生成后台登录账号默认后台路径:/admin(可在config/filament.php调整)
5) 生成 Product 资源(Resource)
php artisan make:filament-resource Product --generate会生成:
app/Filament/Resources/ProductResource.php
app/Filament/Resources/ProductResource/Pages/
├── ListProducts.php
├── CreateProduct.php
└── EditProduct.php5.1 ProductResource 表单 & 列表示例
app/Filament/Resources/ProductResource.php:
<?php
namespace App\Filament\Resources;
use App\Models\Product;
use Filament\Forms;
use Filament\Resources\Form;
use Filament\Resources\Table;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\FileUpload;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\BadgeColumn;
class ProductResource extends Resource
{
protected static ?string $model = Product::class;
protected static ?string $navigationGroup = '商品管理';
protected static ?string $navigationIcon = 'heroicon-o-cube';
protected static ?string $navigationLabel = '商品';
public static function form(Form $form): Form
{
return $form->schema([
Section::make('基础信息')->schema([
TextInput::make('name')->label('名称')->required()->maxLength(120),
TextInput::make('sku')->label('SKU')->required()->unique(ignoreRecord: true),
TextInput::make('price')->label('价格')->numeric()->required()->minValue(0),
TextInput::make('stock')->label('库存')->numeric()->required()->minValue(0),
Select::make('status')->label('状态')
->options([
'draft' => '草稿',
'active' => '上架',
'disabled' => '下架',
])->default('draft')->required(),
])->columns(2),
Section::make('图片')->schema([
FileUpload::make('images')
->label('图片')
->image()
->multiple()
->directory('products/'.date('Ymd'))
->reorderable()
->imageEditor(),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('id')->sortable()->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('name')->label('名称')->searchable()->sortable(),
TextColumn::make('sku')->label('SKU')->searchable()->copyable(),
TextColumn::make('price')->label('价格')->money('CNY', 2),
TextColumn::make('stock')->label('库存')->sortable(),
BadgeColumn::make('status')->label('状态')
->colors([
'secondary' => 'draft',
'success' => 'active',
'danger' => 'disabled',
])
->sortable(),
TextColumn::make('created_at')->label('创建时间')->dateTime()->sortable(),
])
->filters([
Tables\Filters\SelectFilter::make('status')->label('状态')
->options([
'draft' => '草稿',
'active' => '上架',
'disabled' => '下架',
]),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getPages(): array
{
return [
'index' => Pages\ListProducts::route('/'),
'create' => Pages\CreateProduct::route('/create'),
'edit' => Pages\EditProduct::route('/{record}/edit'),
];
}
}6) Dashboard 小组件(统计卡)
php artisan make:filament-widget ProductStats --type=stats-overviewapp/Filament/Widgets/ProductStats.php:
<?php
namespace App\Filament\Widgets;
use App\Models\Product;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Card;
class ProductStats extends BaseWidget
{
protected static ?string $heading = '数据总览';
protected function getCards(): array
{
return [
Card::make('商品总数', Product::query()->count()),
Card::make('上架商品', Product::query()->where('status','active')->count()),
Card::make('总库存', (int) Product::query()->sum('stock')),
];
}
}将该组件加入默认面板(app/Filament/Pages/Dashboard.php 的 getWidgets() 中追加):
protected function getWidgets(): array
{
return [
Widgets\AccountWidget::class,
\App\Filament\Widgets\ProductStats::class,
];
}7) 权限:Shield 一键生成
7.1 生成资源权限
# 根据现有 Filament 资源生成对应权限(view/list/create/update/delete)
php artisan shield:generate --all
# 创建默认角色(可在 config/filament-shield.php 里配置角色名,如 Super Admin / Admin / Editor)
php artisan shield:install这会创建roles/permissions并把 Super Admin 赋权(含*能力)。
7.2 为用户分配角色
# Tinker 示例
php artisan tinker
>>> $u = \App\Models\User::where('email','you@example.com')->first();
>>> $u->assignRole('Super Admin');之后登录 /admin,即可看到完整导航和权限受控菜单。8) Seeder(可选:快速造数)
创建 Seeder:
php artisan make:seeder ProductSeederdatabase/seeders/ProductSeeder.php:
public function run(): void
{
\App\Models\Product::factory()->count(50)->create();
}注册到 DatabaseSeeder:
public function run(): void
{
$this->call([ProductSeeder::class]);
}执行:
php artisan migrate --seed9) 中文化 & 主题
config/app.php中设置:'locale' => 'zh_CN'- 发布的语言包位于
lang/vendor/filament/*,可按需覆盖。 - 自定义主题可在
resources/css/filament/admin/theme.css定制 Tailwind 变量(php artisan filament:install已建立构建管线)。
10) 导航与分组
本示例在
ProductResource里设置了:protected static ?string $navigationGroup = '商品管理';protected static ?string $navigationLabel = '商品';
- 如需排序/图标统一,可在
config/filament.php的navigation设置中集中控制。
11) 常见问题(FAQ)
- 登录 403/无菜单:未分配角色或无权限。执行 Shield 安装并给用户
Super Admin角色。 - 图片上传失败:确认
filesystems.php配置及storage:link:php artisan storage:link。 - 权限缓存:修改权限后运行
php artisan permission:cache-reset。 - 多环境:在
.env中配置APP_URL、SESSION_、CACHE_、QUEUE_。
12) 一键脚本(可选)
将以下脚本保存为 scripts/quick-boot.sh:
#!/usr/bin/env bash
set -e
php artisan migrate --force
php artisan storage:link || true
# 生成资源权限并安装 Shield 角色
php artisan shield:generate --all || true
php artisan shield:install || true
# 如果没有管理员就创建一个(可改邮箱/密码)
php artisan tinker --execute='if(!\App\Models\User::where