beholdr / filament-trilist
Filament插件,用于处理树形数据:treeselect和treeview
Requires
- php: ^8.1
- filament/filament: ^3.0
- illuminate/contracts: ^10.0 || ^11.0
- spatie/laravel-package-tools: ^1.15.0
Requires (Dev)
- larastan/larastan: ^2.7.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^7.9
- orchestra/testbench: ^8.0
- pestphp/pest: ^2.0
- pestphp/pest-plugin-arch: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
README
Filament插件,用于处理树形数据:treeselect输入和treeview页面。基于Trilist包。
支持
你喜欢Filament Trilist吗?请通过Boosty支持我。
功能
- treeselect输入和treeview页面
- 树项可以有多个父级
- 与关系或自定义层次结构数据一起使用
安装
您可以通过composer安装此包
composer require beholdr/filament-trilist
可选地,您可以使用以下方式发布视图
php artisan vendor:publish --tag="filament-trilist-views"
树形数据
当遵循格式时,您可以使用任何来源的层次数据
[ ['id' => 'ID', 'label' => 'Item label', 'children' => [ ['id' => 'ID', 'label' => 'Item label', 'children' => [...]], ... ] ]
例如,您可以使用类似staudenmeir/laravel-adjacency-list的特殊库来获取树形数据
Category::tree()->get()->toTree()
或使用自定义关系模式和方法,甚至使用ManyToMany
(多对多)关系。
自引用实体的示例
迁移
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { Schema::create('professions', function (Blueprint $table) { $table->id(); $table->string('label'); }); Schema::create('profession_profession', function (Blueprint $table) { $table->primary(['parent_id', 'child_id']); $table->foreignId('parent_id')->constrained('professions')->cascadeOnDelete(); $table->foreignId('child_id')->constrained('professions')->cascadeOnDelete(); }); } public function down(): void { Schema::dropIfExists('professions'); Schema::dropIfExists('profession_profession'); } };
模型
namespace App\Models; use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class Profession extends Model { protected $with = ['children']; public function parents() { return $this->belongsToMany(Profession::class, 'profession_profession', 'child_id', 'parent_id'); } public function children() { return $this->belongsToMany(Profession::class, 'profession_profession', 'parent_id', 'child_id'); } public function scopeRoot(Builder $builder) { $builder->doesntHave('parents'); } }
使用给定的模型,您可以生成如下所示的树形数据
Profession::root()->get();
treeselect输入
导入TrilistSelect
类并在您的Filament表单中使用它
use Beholdr\FilamentTrilist\Components\TrilistSelect // with custom tree data TrilistSelect::make('category_id') ->options($treeData), // or with relationship TrilistSelect::make('categories') ->relationship('categories') ->options($treeData) ->multiple(),
完整选项列表
TrilistSelect::make(string $fieldName) ->label(string $fieldLabel) ->placeholder(string | Closure $placeholder) ->disabled(bool | Closure $condition) // array of tree items ->options(array | Closure $options), // first argument defines name of the relationship, second can be used to modify relationship query ->relationship(string | Closure $relationshipName, ?Closure $modifyQueryUsing = null) // array of ids (or single id) of disabled items ->disabledOptions(string | int | array | Closure $value) // multiple selection mode, default: false ->multiple(bool | Closure $condition) // animate expand/collapse, default: true ->animated(bool | Closure $condition) // expand initial selected options, default: true ->expandSelected(bool | Closure $condition) // in independent mode children auto selected when parent is selected, default: false ->independent(bool | Closure $condition) // in leafs mode, the selected value is not grouped as the parent when all child elements are selected, default: false ->leafs(bool | Closure $condition) // tree item id field name, default: 'id' ->fieldId(string | Closure $value) // tree item label field name, default: 'label' ->fieldLabel(string | Closure $value) // tree item children field name, default: 'children' ->fieldChildren(string | Closure $value) // hook for generating custom labels, default: '(item) => item.label' ->labelHook(string | Closure $value) // enable filtering of items, default: false ->searchable(bool | Closure $condition) // enable autofocus on filter field, default: false ->autofocus(bool | Closure $condition) // search input placeholder ->searchPrompt(string | Htmlable | Closure $message) // select button label ->selectButton(string | Htmlable | Closure $message) // cancel button label ->cancelButton(string | Htmlable | Closure $message)
自定义标签
如果您想自定义标签,可以使用labelHook
方法。它应返回一个字符串,该字符串将被处理为JS(请注意转义引号和特殊字符)
TrilistSelect::make('parent_id') ->labelHook(fn () => <<<JS (item) => `\${item.label} \${item.data?.description ? '<div style=\'font-size: 0.85em; opacity: 0.5\'>' + item.data.description + '</div>' : ''}` JS)
在过滤器中使用
您可以在自定义过滤器中使用treeselect
use App\Models\Category; use Filament\Tables\Filters\Filter; use Illuminate\Database\Eloquent\Builder; Filter::make('category') ->form([ TrilistSelect::make('category_id') ->multiple() ->independent() ->options(Category::tree()->get()->toTree()) ]) ->query(function (Builder $query, array $data) { $query->when( $data['category_id'], function (Builder $query, $values) { $ids = Category::whereIn('id', $values) ->get() ->map(fn (Category $category) => $category ->descendantsAndSelf() ->pluck('id') ->toArray() )->flatten(); $query->whereIn('category_id', $ids); } ); }) ->indicateUsing(function (array $data) { if (! $data['category_id']) return null; return Category::whereIn('id', $data['category_id'])->pluck('name')->toArray(); }),
treeview页面
在您的资源目录的Pages
目录内创建自定义页面类。注意,页面类扩展自Beholdr\FilamentTrilist\Components\TrilistPage
namespace App\Filament\Resources\PostResource\Pages; use App\Filament\Resources\PostResource; use App\Models\Post; use Beholdr\FilamentTrilist\Components\TrilistPage; class TreePosts extends TrilistPage { protected static string $resource = PostResource::class; // optional page and tab title protected static ?string $title = 'Posts Tree'; // return array of tree items (see below about tree data) public function getTreeOptions(): array { return Post::root()->get()->toArray(); } }
在您的资源的静态getPages()
方法中注册创建的页面
public static function getPages(): array { return [ // ... 'tree' => Pages\TreePosts::route('/tree'), ]; }
将新创建的页面的链接添加到您的面板导航中
use App\Filament\Resources\PostResource\Pages\TreePosts; use Beholdr\FilamentTrilist\FilamentTrilistPlugin; use Filament\Navigation\NavigationItem; class AdminPanelProvider extends PanelProvider { public function panel(Panel $panel): Panel { return $panel ->navigationItems(TreePosts::getNavigationItems()) } }
treeview选项
您可以通过覆盖自定义页面类中的静态方法来设置一些树形选项
class TreeCategories extends TrilistPage { public static function getFieldLabel(): string { return 'name'; } }
getFieldId()
:树项id字段名称getFieldLabel()
:树项标签字段名称getFieldChildren()
:树项子项字段名称isAnimated()
:启用展开/折叠动画,默认:trueisSearchable()
:启用项目过滤,默认:falsegetSearchPrompt()
:搜索输入占位符
自定义标签
如果您想自定义树项的标签,可以覆盖TrilistPage
的getLabelHook()
方法。
示例
例如,如果您希望模型具有一个description
字段,并希望在项目名称下方输出描述。您的模型的所有其他属性都在item.data
属性下,所以描述将在item.data.description
下
public function getLabelHook(): string { if (! $editRoute = $this->getEditRoute()) { return 'undefined'; } $template = route($editRoute, ['record' => '#ID#'], false); return <<<JS (item) => `<a href='\${'{$template}'.replace('#ID#', item.id)}'>\${item.label}</a> \${item.data?.description ? '<div style=\'font-size: 0.85em; opacity: 0.5\'>' + item.data.description + '</div>' : ''}` JS; }
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。