翻译进度
13
分块数量
4
参与人数

1.2. 快速开始

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。


快速开始

概述

面板是 Filament 中的顶级容器,它允许你构建功能丰富的管理面板,其中包括页面、资源、表单、表格、通知、Action、信息列表和 Widget。所有面板都有一个默认的仪表盘,其中包含统计、图表、表格等多种 Widget。

前置要求

使用 Filament 之前,你需要先熟悉 Laravel。Filament 是基于许多 Laravel 核心概念构建的,特别是数据库迁移和 Eloquent ORM。如果你此前未曾用过 Laravel,或者需要复习,建议你跟着 Laravel Bootcamp 去创建一个小应用。那个教程涵盖了创建 Laravel 应用的基础知识。

Demo 项目

本教程介绍使用 Filament 为兽医诊所建立一个简单的患者管理系统。它将支持添加新患者(猫、狗或兔子),将他们分配给主人(owners),并记录他们接受的治疗(treatments)。该系统将有一个仪表板,上面有患者类型的统计数据,还有一张显示过去一年治疗次数的图表。

设置数据库和模型

这个项目中需要 3 个模型 - Owner、Patient 和 Treatment。使用以下 artisan 命令创建:

php artisan make:model Owner -m
php artisan make:model Patient -m
php artisan make:model Treatment -m

定义迁移

数据库迁移请使用如下 schema:


// create_owners_table
Schema::create('owners', function (Blueprint $table) {
    $table->id();
    $table->string('email');
    $table->string('name');
    $table->string('phone');
    $table->timestamps();
});

// create_patients_table
Schema::create('patients', function (Blueprint $table) {
    $table->id();
    $table->date('date_of_birth');
    $table->string('name');
    $table->foreignId('owner_id')->constrained('owners')->cascadeOnDelete();
    $table->string('type');
    $table->timestamps();
});

// create_treatments_table
Schema::create('treatments', function (Blueprint $table) {
    $table->id();
    $table->string('description');
    $table->text('notes')->nullable();
    $table->foreignId('patient_id')->constrained('patients')->cascadeOnDelete();
    $table->unsignedInteger('price')->nullable();
    $table->timestamps();
});
Larva 翻译于 1年前

使用命令 php artisan migrate 来运行迁移。

取消模型保护

为了本指南的简洁性,我们将禁用 Laravel 的批量赋值保护。Filament 仅保存有效数据至模型,因此可以安全地取消所有模型的保护。要一次性取消所有 Laravel 模型的保护,请在 app/Providers/AppServiceProvider.phpboot() 方法中添加 Model::unguard()

use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::unguard();
}

设置模型之间的关系

我们来设置模型之间的关系。在我们的系统中,每个宠物主人(Owner)可以拥有多个宠物(Patient),每个宠物(患者)可以接受多次治疗(Treatment)。

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Owner extends Model
{
    public function patients(): HasMany
    {
        return $this->hasMany(Patient::class);
    }
}

class Patient extends Model
{
    public function owner(): BelongsTo
    {
        return $this->belongsTo(Owner::class);
    }

    public function treatments(): HasMany
    {
        return $this->hasMany(Treatment::class);
    }
}

class Treatment extends Model
{
    public function patient(): BelongsTo
    {
        return $this->belongsTo(Patient::class);
    }
}

介绍资源

在 Filament 中,资源是用于为您的 Eloquent 模型构建增删改查(CRUD)界面的静态类。它们描述了管理员如何通过表格和表单与面板中的数据进行交互。

由于患者(宠物)是此系统的核心实体,我们开始通过创建患者资源来构建创建、查看、更新和删除患者的页面。

使用以下命令来创建一个新的 Patient 模型 的 Filament 资源:

php artisan make:filament-resource Patient

这将在 app/Filament/Resources 目录下创建几个文件

.
+-- PatientResource.php
+-- PatientResource
|   +-- Pages
|   |   +-- CreatePatient.php
|   |   +-- EditPatient.php
|   |   +-- ListPatients.php
working 翻译于 1年前

访问浏览器中的 /admin/patients,观察侧边栏中名为 "患者 "的新链接。点击该链接将显示一个空表格。让我们添加一个创建新病人的表单。

设置资源表

打开PatientResource.php文件,其中的form()方法包含一个空的schema([...])数组。在此模式中添加表单字段将创建一个可用于创建和编辑新病人的表单

"姓名 "文本输

Filament 捆绑了大量的[表单字段](.../forms/fields/getting-started)。让我们从一个简单的 [文本输入框] 开始(.../forms/fields/text-input)

use Filament\Forms;
use Filament\Forms\Form;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name'),
        ]);
}

访问 /admin/patients/create(或点击 "New Patient"按钮),观察病人姓名的表单字段是否已添加。

由于该字段在数据库中是必填字段,且最大长度为 255 个字符,因此让我们为姓名字段添加两条[验证规则](.../forms/validation):

use Filament\Forms;

Forms\Components\TextInput::make('name')
    ->required()
    ->maxLength(255)

Attempt to submit the form to create a new patient without a name and observe that a message is displayed informing you that the name field is required.

"类型 "选择

让我们为病人类型添加第二个字段:在猫、狗或兔子之间进行选择。因为有一组固定的选项可供选择,所以 select 字段效果很好

use Filament\Forms;
use Filament\Forms\Form;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name')
                ->required()
                ->maxLength(255),
            Forms\Components\Select::make('type')
                ->options([
                    'cat' => 'Cat',
                    'dog' => 'Dog',
                    'rabbit' => 'Rabbit',
                ]),
        ]);
}
VeryAUX 翻译于 11个月前

The options() method of the Select field accepts an array of options for the user to choose from. The array keys should match the database, and the values are used as the form labels. Feel free to add as many animals to this array as you wish.

Since this field is also required in the database, let's add the required() validation rule:

use Filament\Forms;

Forms\Components\Select::make('type')
    ->options([
        'cat' => 'Cat',
        'dog' => 'Dog',
        'rabbit' => 'Rabbit',
    ])
    ->required()

"Date of birth" picker

Let's add a date picker field for the date_of_birth column along with the validation (the date of birth is required and the date should be no later than the current day).

use Filament\Forms;
use Filament\Forms\Form;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name')
                ->required()
                ->maxLength(255),
            Forms\Components\Select::make('type')
                ->options([
                    'cat' => 'Cat',
                    'dog' => 'Dog',
                    'rabbit' => 'Rabbit',
                ])
                ->required(),
            Forms\Components\DatePicker::make('date_of_birth')
                ->required()
                ->maxDate(now()),
        ]);
}

"Owner" select

We should also add an owner when creating a new patient. Since we added a BelongsTo relationship in the Patient model (associating it to the related Owner model), we can use the relationship() method from the select field to load a list of owners to choose from:

use Filament\Forms;
use Filament\Forms\Form;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name')
                ->required()
                ->maxLength(255),
            Forms\Components\Select::make('type')
                ->options([
                    'cat' => 'Cat',
                    'dog' => 'Dog',
                    'rabbit' => 'Rabbit',
                ])
                ->required(),
            Forms\Components\DatePicker::make('date_of_birth')
                ->required()
                ->maxDate(now()),
            Forms\Components\Select::make('owner_id')
                ->relationship('owner', 'name')
                ->required(),
        ]);
}

The first argument of the relationship() method is the name of the function that defines the relationship in the model (used by Filament to load the select options) — in this case, owner. The second argument is the column name to use from the related table — in this case, name.

Let's also make the owner field required, searchable(), and preload() the first 50 owners into the searchable list (in case the list is long):

use Filament\Forms;

Forms\Components\Select::make('owner_id')
    ->relationship('owner', 'name')
    ->searchable()
    ->preload()
    ->required()

Creating new owners without leaving the page

Currently, there are no owners in our database. Instead of creating a separate Filament owner resource, let's give users an easier way to add owners via a modal form (accessible as a + button next to the select). Use the createOptionForm() method to embed a modal form with TextInput fields for the owner's name, email address, and phone number:

use Filament\Forms;

Forms\Components\Select::make('owner_id')
    ->relationship('owner', 'name')
    ->searchable()
    ->preload()
    ->createOptionForm([
        Forms\Components\TextInput::make('name')
            ->required()
            ->maxLength(255),
        Forms\Components\TextInput::make('email')
            ->label('Email address')
            ->email()
            ->required()
            ->maxLength(255),
        Forms\Components\TextInput::make('phone')
            ->label('Phone number')
            ->tel()
            ->required(),
    ])
    ->required()

A few new methods on the TextInput were used in this example:

  • label() overrides the auto-generated label for each field. In this case, we want the Email label to be Email address, and the Phone label to be Phone number.
  • email() ensures that only valid email addresses can be input into the field. It also changes the keyboard layout on mobile devices.
  • tel() ensures that only valid phone numbers can be input into the field. It also changes the keyboard layout on mobile devices.

The form should be working now! Try creating a new patient and their owner. Once created, you will be redirected to the Edit page, where you can update their details.

Setting up the patients table

Visit the /admin/patients page again. If you have created a patient, there should be one empty row in the table — with an edit button. Let's add some columns to the table, so we can view the actual patient data.

Open the PatientResource.php file. You should see a table() method with an empty columns([...]) array. You can use this array to add columns to the patients table.

Adding text columns

Filament bundles a large selection of table columns. Let's use a simple text column for all the fields in the patients table:

use Filament\Tables;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name'),
            Tables\Columns\TextColumn::make('type'),
            Tables\Columns\TextColumn::make('date_of_birth'),
            Tables\Columns\TextColumn::make('owner.name'),
        ]);
}

Filament uses dot notation to eager-load related data. We used owner.name in our table to display a list of owner names instead of less informational ID numbers. You could also add columns for the owner's email address and phone number.

Making columns searchable

The ability to search for patients directly in the table would be helpful as a veterinary practice grows. You can make columns searchable by chaining the searchable() method to the column. Let's make the patient's name and owner's name searchable.

use Filament\Tables;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name')
                ->searchable(),
            Tables\Columns\TextColumn::make('type'),
            Tables\Columns\TextColumn::make('date_of_birth'),
            Tables\Columns\TextColumn::make('owner.name')
                ->searchable(),
        ]);
}

Reload the page and observe a new search input field on the table that filters the table entries using the search criteria.

Making the columns sortable

To make the patients table sortable by age, add the sortable() method to the date_of_birth column:

use Filament\Tables;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name')
                ->searchable(),
            Tables\Columns\TextColumn::make('type'),
            Tables\Columns\TextColumn::make('date_of_birth')
                ->sortable(),
            Tables\Columns\TextColumn::make('owner.name')
                ->searchable(),
        ]);
}

This will add a sort icon button to the column header. Clicking it will sort the table by date of birth.

Filtering the table by patient type

Although you can make the type field searchable, making it filterable is a much better user experience.

Filament tables can have filters, which are components that reduce the number of records in a table by adding a scope to the Eloquent query. Filters can even contain custom form components, making them a potent tool for building interfaces.

Filament includes a prebuilt SelectFilter that you can add to the table's filters():

use Filament\Tables;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return $table
        ->columns([
            // ...
        ])
        ->filters([
            Tables\Filters\SelectFilter::make('type')
                ->options([
                    'cat' => 'Cat',
                    'dog' => 'Dog',
                    'rabbit' => 'Rabbit',
                ]),
        ]);
}

Reload the page, and you should see a new filter icon in the top right corner (next to the search form). The filter opens a select menu with a list of patient types. Try filtering your patients by type.

Introducing relation managers

Currently, patients can be associated with their owners in our system. But what happens if we want a third level? Patients come to the vet practice for treatment, and the system should be able to record these treatments and associate them with a patient.

One option is to create a new TreatmentResource with a select field to associate treatments with a patient. However, managing treatments separately from the rest of the patient information is cumbersome for the user. Filament uses "relation managers" to solve this problem.

Relation managers are tables that display related records for an existing resource on the edit screen for the parent resource. For example, in our project, you could view and manage a patient's treatments directly below their edit form.

You can also use Filament "actions" to open a modal form to create, edit, and delete treatments directly from the patient's table.

Use the make:filament-relation-manager artisan command to quickly create a relation manager, connecting the patient resource to the related treatments:

php artisan make:filament-relation-manager PatientResource treatments description
  • PatientResource is the name of the resource class for the owner model. Since treatments belong to patients, the treatments should be displayed on the Edit Patient page.
  • treatments is the name of the relationship in the Patient model we created earlier.
  • description is the column to display from the treatments table.

This will create a PatientResource/RelationManagers/TreatmentsRelationManager.php file. You must register the new relation manager in the getRelations() method of the PatientResource:

use App\Filament\Resources\PatientResource\RelationManagers;

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

The TreatmentsRelationManager.php file contains a class that is prepopulated with a form and table using the parameters from the make:filament-relation-manager artisan command. You can customize the fields and columns in the relation manager similar to how you would in a resource:

use Filament\Forms;
use Filament\Forms\Form;
use Filament\Tables;
use Filament\Tables\Table;

public function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('description')
                ->required()
                ->maxLength(255),
        ]);
}

public function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('description'),
        ]);
}

Visit the Edit page for one of your patients. You should already be able to create, edit, delete, and list treatments for that patient.

Setting up the treatment form

By default, text fields only span half the width of the form. Since the description field might contain a lot of information, add a columnSpan('full') method to make the field span the entire width of the modal form:

use Filament\Forms;

Forms\Components\TextInput::make('description')
    ->required()
    ->maxLength(255)
    ->columnSpan('full')

Let's add the notes field, which can be used to add more details about the treatment. We can use a textarea field with a columnSpan('full'):

use Filament\Forms;
use Filament\Forms\Form;

public function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('description')
                ->required()
                ->maxLength(255)
                ->columnSpan('full'),
            Forms\Components\Textarea::make('notes')
                ->maxLength(65535)
                ->columnSpan('full'),
        ]);
}

Configuring the price field

Let's add a price field for the treatment. We can use a text input with some customizations to make it suitable for currency input. It should be numeric(), which adds validation and changes the keyboard layout on mobile devices. Add your preferred currency prefix using the prefix() method; for example, prefix('€') will add a before the input without impacting the saved output value:

use Filament\Forms;
use Filament\Forms\Form;

public function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('description')
                ->required()
                ->maxLength(255)
                ->columnSpan('full'),
            Forms\Components\Textarea::make('notes')
                ->maxLength(65535)
                ->columnSpan('full'),
            Forms\Components\TextInput::make('price')
                ->numeric()
                ->prefix('€')
                ->maxValue(42949672.95),
        ]);
}
Casting the price to an integer

Filament stores currency values as integers (not floats) to avoid rounding and precision issues — a widely-accepted approach in the Laravel community. However, this requires creating a cast in Laravel that transforms the float into an integer when retrieved and back to an integer when stored in the database. Use the following artisan command to create the cast:

php artisan make:cast MoneyCast

Inside the new app/Casts/MoneyCast.php file, update the get() and set() methods:

public function get($model, string $key, $value, array $attributes): float
{
    // Transform the integer stored in the database into a float.
    return round(floatval($value) / 100, precision: 2);
}

public function set($model, string $key, $value, array $attributes): float
{
    // Transform the float into an integer for storage.
    return round(floatval($value) * 100);
}

Now, add the MoneyCast to the price attribute in the Treatment model:

use App\Casts\MoneyCast;
use Illuminate\Database\Eloquent\Model;

class Treatment extends Model
{
    protected $casts = [
        'price' => MoneyCast::class,
    ];

    // ...
}

Setting up the treatments table

When the relation manager was generated previously, the description text column was automatically added. Let's also add a sortable() column for the price with a currency prefix. Use the Filament money() method to format the price column as money — in this case for EUR ():

use Filament\Tables;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('description'),
            Tables\Columns\TextColumn::make('price')
                ->money('EUR')
                ->sortable(),
        ]);
}

Let's also add a column to indicate when the treatment was administered using the default created_at timestamp. Use the dateTime() method to display the date-time in a human-readable format:

use Filament\Tables;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('description'),
            Tables\Columns\TextColumn::make('price')
                ->money('EUR')
                ->sortable(),
            Tables\Columns\TextColumn::make('created_at')
                ->dateTime(),
        ]);
}

You can pass any valid PHP date formatting string to the dateTime() method (e.g. dateTime('m-d-Y h:i A')).

Introducing widgets

Filament widgets are components that display information on your dashboard, especially statistics. Widgets are typically added to the default Dashboard of the panel, but you can add them to any page, including resource pages. Filament includes built-in widgets like the stats widget, to render important statistics in a simple overview; chart widget, which can render an interactive chart; and table widget, which allows you to easily embed the Table Builder.

Let's add a stats widget to our default dashboard page that includes a stat for each type of patient and a chart to visualize treatments administered over time.

Creating a stats widget

Create a stats widget to render patient types using the following artisan command:

php artisan make:filament-widget PatientTypeOverview --stats-overview

When prompted, do not specify a resource, and select "admin" for the location.

This will create a new app/Filament/Widgets/PatientTypeOverview.php file. Open it, and return Stat instances from the getStats() method:

<?php

namespace App\Filament\Widgets;

use App\Models\Patient;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;

class PatientTypeOverview extends BaseWidget
{
    protected function getStats(): array
    {
        return [
            Stat::make('Cats', Patient::query()->where('type', 'cat')->count()),
            Stat::make('Dogs', Patient::query()->where('type', 'dog')->count()),
            Stat::make('Rabbits', Patient::query()->where('type', 'rabbit')->count()),
        ];
    }
}

Open your dashboard, and you should see your new widget displayed. Each stat should show the total number of patients for the specified type.

Creating a chart widget

Let's add a chart to the dashboard to visualize the number of treatments administered over time. Use the following artisan command to create a new chart widget:

php artisan make:filament-widget TreatmentsChart --chart

When prompted, do not specify a resource, select "admin" for the location, and choose "line chart" as the chart type.

Open app/Filament/Widgets/TreatmentsChart.php and set the $heading of the chart to "Treatments".

The getData() method returns an array of datasets and labels. Each dataset is a labeled array of points to plot on the chart, and each label is a string. This structure is identical to the Chart.js library, which Filament uses to render charts.

To populate chart data from an Eloquent model, Filament recommends that you install the flowframe/laravel-trend package:

composer require flowframe/laravel-trend

Update the getData() to display the number of treatments per month for the past year:

use App\Models\Treatment;
use Flowframe\Trend\Trend;
use Flowframe\Trend\TrendValue;

protected function getData(): array
{
    $data = Trend::model(Treatment::class)
        ->between(
            start: now()->subYear(),
            end: now(),
        )
        ->perMonth()
        ->count();

    return [
        'datasets' => [
            [
                'label' => 'Treatments',
                'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
            ],
        ],
        'labels' => $data->map(fn (TrendValue $value) => $value->date),
    ];
}

Now, check out your new chart widget in the dashboard!

You can customize your dashboard page to change the grid and how many widgets are displayed.

Next steps with the Panel Builder

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
贡献者:4
讨论数量: 0
发起讨论 查看所有版本


暂无话题~