yaroslawww/nova-flexible-content

此包已被 废弃 并不再维护。作者建议使用 think.studio/nova-flexible-content 包。

为 Laravel Nova 提供灵活内容与重复字段。

4.2.0 2023-10-02 15:10 UTC

README

Packagist License Packagist Version Total Downloads Build Status Code Coverage Scrutinizer Code Quality

这是一个从优秀的包 whitecube/nova-flexible-content 分支出来的,但是重新工作后无法轻松合并到主包的功能。

一个简单且完整的 Laravel Nova 灵活字段,非常适合重复和灵活的字段组。

Laravel Nova Flexible Content in action

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);

Example of Flexible layout

自定义显示

您可以通过调用方法来更改显示

\NovaFlexibleContent\Flexible::make('Content')
    ->fullWidth()
    ->useSearchableLayoutsMenu()
    ->layoutsMenuButton('Add Video')
    ->limit(3)
    ->withGroupRemovingConfirmation('Are you sure?', 'Yes :)', 'Ahh, no');

Add something amazing

值解析器

默认情况下,字段利用了模型表上的 JSON 列。在某些情况下,JSON 属性可能并不是最佳选择。例如,您可能希望将值存储在另一个表中(这意味着您将使用灵活内容字段而不是传统的 BelongsToMany 或 HasMany 字段)。不用担心,我们为您考虑到了这一点!

解析器定义

每个解析器必须实现 NovaFlexibleContent\Value\Resolver 协议,并因此至少具有两个方法: setget

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

鸣谢