yaroslawww / nova-flexible-content
Requires
- php: ^8.1
- laravel/framework: ^9.0|^10.0
- laravel/nova: ^4.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.16
- guzzlehttp/guzzle: ^7.7
- orchestra/testbench: ^8.5
- phpunit/phpunit: ^10.1
- psalm/plugin-laravel: ^2.8
- think.studio/nova-video-field: ^1.0
- vimeo/psalm: ^5.11
README
这是一个从优秀的包 whitecube/nova-flexible-content 分支出来的,但是重新工作后无法轻松合并到主包的功能。
一个简单且完整的 Laravel Nova 灵活字段,非常适合重复和灵活的字段组。
| Nova | 包 |
|---|---|
| V1 | V1 V2 |
| V4 | V3 V4 |
安装
composer require think.studio/nova-flexible-content # optional publish configs php artisan vendor:publish --provider="NovaFlexibleContent\ServiceProvider" --tag="config"
用法
灵活字段允许轻松管理可重复和可排序的字段组。此包对您可以在这些组内使用的字段没有约束。这意味着您可以使用所有 Laravel Nova 字段类型,也可以使用任何社区制作的字段。
布局
布局表示可以重复出现在灵活字段中的字段组。您可以根据需要添加任意多个不同的布局。如果只定义了一个布局,字段将像简单的重复字段一样行为。通过添加更多布局,您可以获得灵活内容。
布局定义
namespace App\Nova\Flexible\Layouts; use Laravel\Nova\Fields\Text; use Laravel\Nova\Fields\Markdown; use NovaFlexibleContent\Layouts\Layout; class VideoLayout extends Layout { // Optionally you limit count of this layout in flexible groups // protected int $limit = 3; protected function linksPreset() { return Preset::withLayouts([ LinkLayout::class, ]); } /** * Get the fields displayed by the layout. */ public function fields(): array { return [ Text::make('Title', 'title') ->help('Optional'), FileForFlexible::make('Video', 'video') ->prunable() ->acceptedTypes('video/mp4') ->deletable() ->help('Aspect ratio 16:9. Please do not upload a large file.'), ImageForFlexible::make('Poster', 'poster') ->prunable() ->rules(['max:' . 1024 * 10]) ->deletable() ->help('Aspect ratio 16:9.'), // Recursive flexible \NovaFlexibleContent\Flexible::make('Links', 'links') ->preset($this->linksPreset()) ->layoutsMenuButton('Add link'), ]; } }
然后使用此布局
\NovaFlexibleContent\Flexible::make('Content') ->useLayout(\App\Nova\Flexible\Layouts\VideoLayout::class) ->useLayout(\App\Nova\Flexible\Layouts\FooLayout::class) ->useLayout(\App\Nova\Flexible\Layouts\BarLayout::class);
自定义显示
您可以通过调用方法来更改显示
\NovaFlexibleContent\Flexible::make('Content') ->fullWidth() ->useSearchableLayoutsMenu() ->layoutsMenuButton('Add Video') ->limit(3) ->withGroupRemovingConfirmation('Are you sure?', 'Yes :)', 'Ahh, no');
值解析器
默认情况下,字段利用了模型表上的 JSON 列。在某些情况下,JSON 属性可能并不是最佳选择。例如,您可能希望将值存储在另一个表中(这意味着您将使用灵活内容字段而不是传统的 BelongsToMany 或 HasMany 字段)。不用担心,我们为您考虑到了这一点!
解析器定义
每个解析器必须实现 NovaFlexibleContent\Value\Resolver 协议,并因此至少具有两个方法: set 和 get。
namespace App\Nova\Flexible\Resolvers; use NovaFlexibleContent\Layouts\Collections\GroupsCollection; use NovaFlexibleContent\Layouts\Collections\LayoutsCollection; use NovaFlexibleContent\Value\Resolver; class WysiwygPageResolver implements Resolver { public function get(mixed $resource, string $attribute, LayoutsCollection $groups): GroupsCollection { return new GroupsCollection(); } public function set(mixed $resource, string $attribute, GroupsCollection $groups): string { return ''; } }
解析字段
用于解析字段内容的 get 方法。它负责从某处检索内容并返回一个布局实例(组)的集合。例如,我们可能希望从 blocks 表中检索值并将它们转换为布局实例。
public function get(mixed $resource, string $attribute, LayoutsCollection $groups): GroupsCollection { $blocks = $resource->blocks()->orderBy('order')->get(); return $blocks->map( fn($block) => $layouts->find($block->name) ?->duplicate($block->id, ['value' => $block->value]); )->filter(); }
填充字段
负责保存灵活内容的 set 方法。在我们的示例中,它应在 blocks 表中存储数据。
public function set(mixed $resource, string $attribute, GroupsCollection $groups): string { if($resource instanceof \Illuminate\Database\Eloquent\Model) { $resource::saved(function ($model) use ($groups) { // This is a quick & dirty example, syncing the models is probably a better idea. $model->blocks()->delete(); $model->blocks() ->createMany($groups->map(function($group, $index) { return [ 'name' => $group->name(), 'value' => $group->toArray(), 'order' => $index ]; })); }); } return ''; }
预设
除了可重用的布局类之外,您还可以为您的灵活字段创建 Preset 类。这允许您在任何您想要的地方重用整个灵活字段。它们还使灵活字段动态化变得更容易,例如,如果您想要有条件地添加布局。最重要的是,它们还有额外的优势,即清理 Nova 资源类,如果您的灵活字段有许多 useLayout 定义。
预设定义
namespace App\Nova\Flexible\Presets; use NovaFlexibleContent\Layouts\Preset; class WysiwygPagePreset extends Preset { /** * @var array */ protected array $usedLayouts = [ \App\Nova\Flexible\Layouts\SimpleWysiwygLayout::class, \App\Nova\Flexible\Layouts\FooLayout::class, ]; public function handle(\NovaFlexibleContent\Flexible $field) { parent::handle($field); $field->layoutsMenuButton('Add new block') ->setResolver(\App\Nova\Flexible\Resolvers\WysiwygPageResolver::class) ->help('Example help.'); } }
\NovaFlexibleContent\Flexible::make('Content') ->preset(\App\Nova\Flexible\Presets\WysiwygPagePreset::class);
显示灵活内容
字段将其值存储为单个 JSON 字符串,这意味着在您的应用程序中使用之前需要解析此字符串。
namespace App; use Illuminate\Database\Eloquent\Model; use NovaFlexibleContent\Concerns\HasFlexible; class Post extends Model { use HasFlexible; // Collect basic `Layout` instances public function getCollectedFlexibleContentAttribute() { return $this->flexible('flexible-content'); } // Cast to specified classes public function getCastedFlexibleContentAttribute() { return $this->flexible('flexible-content', [ 'wysiwyg' => \App\Nova\Flexible\Layouts\WysiwygLayout::class, 'video' => \App\Nova\Flexible\Layouts\VideoLayout::class, ]); } }
IDE 帮助器
为您的应用程序中的灵活布局创建 IDE 帮助文件。
php artisan nova-flexible-content:ide-helper:layouts
# or
php artisan nova-flexible-content:ide-helper:layouts --filename custom-file.php


