cactus-galaxy/filament-astrotomic

Astrotomic's Laravel Translatable 包的 Filament 支持。

v0.3.0 2024-05-20 00:19 UTC

This package is auto-updated.

Last update: 2024-09-09 00:31:22 UTC


README

Latest Version on Packagist Total Downloads

此包是 Filamentlaravel-translatable 的扩展。

spatie/laravel-translatable 的启发,为您的 Laravel 应用程序提供另一种翻译包的替代方案。

GalaxyStore 中查看 Filament 控面板的更多配置示例 - 一个包含 Filament、Astrotomic 包和其他内容的演示项目。

安装

您可以通过 Composer 安装此包

composer require cactus-galaxy/filament-astrotomic

发布 astrotomic/laravel-translatable 包的配置

php artisan vendor:publish --tag="translatable"

之后,您将需要配置应用程序应使用的区域设置。

'locales' => [
    'uk',
    'en',
],

将插件添加到面板

要将插件添加到面板,您必须使用 plugins() 方法在配置文件中包含它

use CactusGalaxy\FilamentAstrotomic\FilamentAstrotomicTranslatablePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            FilamentAstrotomicTranslatablePlugin::make(),
        ]);
}

准备您的模型类

您需要使您的模型可翻译。您可以在 Laravel 可翻译文档 中阅读如何操作。

准备您的资源类

您必须将 ResourceTranslatable 特性应用到您的资源类中

use CactusGalaxy\FilamentAstrotomic\Resources\Concerns\ResourceTranslatable;
use Filament\Resources\Resource;

class ProductResource extends Resource
{
    use ResourceTranslatable;

    // ...
}

使资源页面可翻译

准备您的资源类 之后,您还必须使每个资源页面的可翻译性。您可以在每个资源文件夹的 Pages 目录中找到您的资源页面。要准备一个页面,您必须将相应的 {Type}Translatable 特性应用到它上

use CactusGalaxy\FilamentAstrotomic\Resources\Pages\Record\ListTranslatable;
use Filament\Resources\Pages\ListRecords;

class ListProducts extends ListRecords
{
    use ListTranslatable;

    // ...
}
use CactusGalaxy\FilamentAstrotomic\Resources\Pages\Record\CreateTranslatable;
use Filament\Resources\Pages\CreateRecord;

class CreateProduct extends CreateRecord
{
    use CreateTranslatable;

    // ...
}
use CactusGalaxy\FilamentAstrotomic\Resources\Pages\Record\EditTranslatable;
use Filament\Resources\Pages\EditRecord;

class EditProduct extends EditRecord
{
    use EditTranslatable;

    // ...
}

如果您有资源的 ViewRecord 页面

use CactusGalaxy\FilamentAstrotomic\Resources\Pages\Record\ViewTranslatable;
use Filament\Resources\Pages\ViewRecord;

class ViewProduct extends ViewRecord
{
    use ViewTranslatable;

    // ...
}

为特定资源设置可翻译的区域设置

默认情况下,使用 Astrotomic 的 Locales 辅助方法 all() 加载可翻译的区域设置,该方法返回 translatable.locales 配置中的所有区域设置。作为替代方案,您可以通过在资源类中覆盖 getTranslatableLocales() 方法来自定义特定资源的可翻译区域设置

use CactusGalaxy\FilamentAstrotomic\Resources\Concerns\ResourceTranslatable;
use Filament\Resources\Resource;

class ProductResource extends Resource
{
    use ResourceTranslatable;

    // ...

    public static function getTranslatableLocales(): array
    {
        return ['uk', 'en'];
    }
}

在表单上使用区域设置标签

TranslatableTabs 扩展了默认的 Filament\Forms\Components\Tabs 组件,并提供了一种为每个区域设置创建标签式标签的方法。在 localeTabSchema 方法中,您可以定义每个标签的架构回调。此回调将为每个区域设置调用以生成标签架构。

localeTabSchema 支持 实用工具注入

要接受 TranslatableTab 实例作为参数以获取当前区域设置,您需要将参数命名为 $translatableTab 或使用类型提示,如下面的示例所示。

以下是如何在 ProductResource 表单中使用 TranslatableTabs 的示例

use CactusGalaxy\FilamentAstrotomic\Forms\Components\TranslatableTabs;
use CactusGalaxy\FilamentAstrotomic\Resources\Concerns\ResourceTranslatable;
use CactusGalaxy\FilamentAstrotomic\TranslatableTab;
use Filament\Forms\Form;
use Filament\Resources\Resource;

class ProductResource extends Resource
{
    use ResourceTranslatable;

    // ...
   
    public static function form(Form $form): Form
    {
        return $form->columns(1)->schema([
            Forms\Components\TextInput::make('slug')
                ->unique(ignoreRecord: true)
                ->required()
                ->readOnly()
                ->helperText('Генерується автоматично при зміні назви')
                ->maxLength(255),

            TranslatableTabs::make()
                ->localeTabSchema(fn (TranslatableTab $tab) => [
                    Forms\Components\TextInput::make($tab->makeName('name'))
                        // required only for the main locale
                        ->required($tab->isMainLocale())
                        ->maxLength(255)
                        // generate slug for the item based on the main locale
                        ->live(onBlur: true)
                        ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) use ($tab) {
                            if ($tab->isMainLocale()) {
                                $set('slug', Str::slug($state));
                            }
                        }),
                ]),

            // ...
        ]);
    }
    
    // ...
}

translatable-tabs.png

使用此代码,您将为每个区域设置获得带有 name 字段的标签。在标签中 name 字段将仅对 主区域设置 必需。它还将根据主区域设置生成项目的 slug。

默认情况下,$tab->makeName('name') 使用数组语法命名 - {$locale}.{$name},但您可以通过在 TranslatableTabs 上调用 makeNameUsing 来更改它,例如,使用 纯语法

TranslatableTabs::make()
    // plain syntax
    ->makeNameUsing(fn (string $name, string $locale) => "{$name}:{$locale}")
    // or use an alias
    ->makeNameUsingPlainSyntax()
    // ..

预加或后加标签页

有时您需要在本地化标签页之前或之后添加标签页。您可以使用 prependTabsappendTabs 方法来实现这一点

use Filament\Forms\Components\Tabs\Tab;


TranslatableTabs::make()
    ->localeTabSchema(fn (TranslatableTab $tab) => [
        // ...
    ])
    ->prependTabs([
        Tab::make('Tab before localized')
            ->schema([
                // ...
            ])
        // ...
    ])
    ->appendTabs(fn () => [ // you also can pass a callback or array
        Tab::make('Tab after localized')
            ->schema([
                // ...
            ])
        // ...
    ])

处理带翻译的模态表单

如果您想在模态表单中使用翻译,您需要做一些更改,以正确修改并填充您的表单。

编辑表格操作

例如,我们有一个 ProductResource,但没有编辑页面。

为了在编辑操作模态中处理翻译,您需要覆盖资源类中 EditAction 类的 mutateRecordDataUsing 方法。

如果您的 Column 路径为 translation.*,确保在返回之前从记录数据中取消设置 translation 关系,否则记录数据将保存不正确。

use App\Models\Product;
use CactusGalaxy\FilamentAstrotomic\Forms\Components\TranslatableTabs;
use CactusGalaxy\FilamentAstrotomic\Resources\Concerns\ResourceTranslatable;
use Filament\Forms\Form;
use Filament\Tables;
use Filament\Tables\Table;


class ProductResource extends Resource
{
    use ResourceTranslatable;

    // ...

    public static function form(Form $form): Form
    {
        // ... form with `TranslatableTabs`
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('translation.name'),
                // ...
            ])
            // ...
            ->actions([
                Tables\Actions\EditAction::make()->mutateRecordDataUsing(function (Product $record, array $data) {
                    return self::mutateTranslatableData($record, $data);
                })->mutateFormDataUsing(function (Product $record, array $data) {
                    $record->unsetRelation('translation');

                    return $data;
                }),
                // ...
            ]);
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListProducts::route('/'),
            // edit route is missing 
        ];
    }

选择带有模态选项

有一个更复杂的例子,使用了 Select 组件。例如,我们需要为产品管理(创建或编辑)一个类别。

在定义了可翻译字段的 CategoryResource 形式中。

use CactusGalaxy\FilamentAstrotomic\Forms\Components\TranslatableTabs;
use CactusGalaxy\FilamentAstrotomic\Resources\Concerns\ResourceTranslatable;
use CactusGalaxy\FilamentAstrotomic\TranslatableTab;
use Filament\Forms;
use Filament\Forms\Form;


class CategoryResource extends Resource
{
    use ResourceTranslatable;

    // ...

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                TranslatableTabs::make()
                    ->localeTabSchema(fn (TranslatableTab $tab) => [
                        Forms\Components\TextInput::make($tab->makeName('name'))
                            ->required($tab->isMainLocale())
                            ->maxLength(255),
                    ]),
            ]);
    }

    // ...

}

然后是 ProductResource,我们可以使用带有模态选项的 Select 组件。请注意,我们需要调用 fillEditOptionActionFormUsing 方法并修改记录数据

use App\Models\Category;
use Filament\Forms;
use Filament\Forms\For;


Forms\Components\Select::make('category_id')
    ->required()
    ->native(false)
    ->searchable()
    ->options(
        Category::query()
            ->joinTranslations()
            ->pluck('name', 'categories.id')
            ->toArray()
    )
    // Configure create action - https://filamentphp.com/docs/3.x/forms/fields/select#creating-a-new-option-in-a-modal
    ->createOptionModalHeading('Create')
    ->createOptionForm(fn (Form $form) => CategoryResource::form($form))
    ->createOptionUsing(function (array $data) {
        $optionRecord = Category::create($data);

        return $optionRecord->id;
    })
    // Configure edit action - https://filamentphp.com/docs/3.x/forms/fields/select#editing-the-selected-option-in-a-modal
    ->editOptionModalHeading('Edit')
    ->editOptionForm(fn (Form $form) => CategoryResource::form($form))
    ->fillEditOptionActionFormUsing(function (string $state) {
        if (!$state) {
            return [];
        }

        $optionRecord = Category::find($state);

        return CategoryResource::mutateTranslatableData($record, $record->attributesToArray());
    })
    ->updateOptionUsing(function (array $data, string $state) {
        $optionRecord = Category::find($state);

        $optionRecord->update($data);

        return $optionRecord->id;
    })

select-with-buttons.png

select-modal-edit.png

在列表中为可翻译模型设置列

Filament 默认支持列的嵌套,这意味着您可以在列路径中使用 . 来访问嵌套属性,您不需要为可翻译模型设置特殊列。

Tables\Columns\TextColumn::make('translation.name'),

但是,对可翻译列进行搜索对于每个文本列来说更复杂。为了解决和添加通过列进行搜索的选项,我们建议将以下行添加到您的 ServiceProvider 中的 boot 方法以配置您的列。

来自 GalaxyStoreGalaxyStore

use Astrotomic\Translatable\Translatable;
use Astrotomic\Translatable\Contracts\Translatable as TranslatableContract;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;


// in `boot` method

TextColumn::configureUsing(function (TextColumn $column): void {
    // match `translations.title` or `translation.title`
    if (Str::match('@^translations?\.(\w+)$@', $column->getName())) {
        $column
            ->searchable(query: function (Builder $query, string $search) use ($column): Builder {
                $columnName = Str::after($column->getName(), '.');
                if ($query->hasNamedScope('whereTranslationLike')) {
                    /* @var Translatable|TranslatableContract $query */
                    return $query->whereTranslationLike($columnName, "%{$search}%");
                }

                return $query->where($columnName, 'like', "%{$search}%");
            });
    }
});

也许在某个功能中,有人会为此创建一个列,但当前您可以使用此代码来配置您的列。

测试

composer test

变更日志

有关最近更改的更多信息,请参阅 CHANGELOG

贡献

有关详细信息,请参阅 CONTRIBUTING

快速示例,您可以克隆 GalaxyStore 和此包到一个文件夹

Projects
|-FilamentAstrotomic
|-GalaxyStore

repositories 部分添加到您的 GalaxyStore/composer.json 文件

{
    "repositories": [
        {
            "type": "path",
            "url": "../FilamentAstrotomic",
            "options": {
                "symlink": true
            }
        }
    ]
}

然后执行 composer

composer require "cactus-galaxy/filament-astrotomic:@dev"

或手动将包添加到 composer.json 文件

{
    "require": {
        "cactus-galaxy/filament-astrotomic": "@dev"
    }
}

安全漏洞

有关如何报告安全漏洞的更多信息,请参阅我们的 安全策略

致谢

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件