grandcreation/nova-nested-many

Laravel Nova - Nested Many

dev-main 2023-12-04 02:56 UTC

This package is auto-updated.

Last update: 2024-09-04 04:52:14 UTC


README

  1. 要求
  2. 安装
  3. 用法
  4. HasNestedMany 字段
    1. 依赖项
    2. 传播
    3. 默认子项
    4. 附加选项
    5. 钩子
  5. 可嵌套资源
    1. 可嵌套标题
    2. 可嵌套授权
    3. 可嵌套操作
      1. 可嵌套基本操作
      2. 可嵌套软删除操作
      3. 可嵌套自定义操作
      4. 与 Nova 操作的差异
      5. 嵌套对象
  6. 更新日志
  7. 鸣谢

要求

  • php: ^7.4 | ^8
  • laravel/nova: ^4

安装

composer require lupennat/nova-nested-many

用法

在资源上全局注册 trait HasNestedResource

namespace App\Nova;

use Laravel\Nova\Resource as NovaResource;
use Lupennat\NestedMany\HasNestedResource;

abstract class Resource extends NovaResource
{
    use HasNestedResource;
}

HasMany 一样使用 HasManyNested 字段。

namespace App\Nova;

use Laravel\Nova\Fields\ID;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Gravatar;
use Laravel\Nova\Fields\Password;
// Add use statement here.
use Lupennat\NestedMany\Fields\HasManyNested;

class User extends Resource
{

    public function fields(Request $request)
    {
        return [
            ID::make()->sortable(),

            Gravatar::make(),

            Text::make('Name')
                ->sortable()
                ->rules('required', 'max:255'),

            Text::make('Email')
                ->sortable()
                ->rules('required', 'email', 'max:254')
                ->creationRules('unique:users,email')
                ->updateRules('unique:users,email,{{resourceId}}'),

            Password::make('Password')
                ->onlyOnForms()
                ->creationRules('required', 'string', 'min:6')
                ->updateRules('nullable', 'string', 'min:6'),

            // Add HasManyNested here.
            HasManyNested::make('Posts'),
        ];
    }

}

HasManyNested 默认在详情页、更新页和创建页上可见。在索引页上不可用。

实现 Nestable 合同并使用 trait HasNested 为每个将用于 HasNestedMany 的相关模型。

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Lupennat\NestedMany\Models\Contracts\Nestable;
use Lupennat\NestedMany\Models\HasNested;

class Post extends Model implements Nestable
{
    use HasNested;

}

HasNestedMany 字段

依赖项

HasNestedMany 字段支持 Nova dependsOn

HasNestedMany::make('Posts', Post::class)
    ->dependsOn('name', function(HasNestedMany $field, NovaRequest $novaRequest, FormData $formData) {
        if ($formData->name === 'xxx') {
            $field->show();
        } else {
            $field->hide();
        }
    })

传播

HasNestedMany 字段可以将父字段值传播到相关资源。

HasNestedMany::make('Posts', Post::class)->propagate(['name'])

在相关资源中,可以通过在请求上调用 getNestedPropagated 方法检索传播的字段。

namespace App\Nova;

use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;

class Post extends Resource
{

    public function fields(NovaRequest $request)
    {
        return array_filter([
            ID::make(),
            BelongsTo::make(__('User'), 'user', User::class),
            Select::section(__('Section'), 'section')
                ->options(['sport' => 'Sport', 'news' => 'News'])
                ->rules('required'),
            Text::title(__('Title'), 'title')
            ->rules('required'),
            $request->getNestedPropagated('name') === 'xxx' ?
                Text::make(__('Extra Field'), 'extra')->hide() : null
        ]);
    }

}

默认子项

您可以使用 defaultChildren 方法在子项为空时生成默认的相关资源集合。

defaultChildren 仅在创建页上工作。

HasNestedMany::make('Posts', Post::class)
    ->defaultChildren([
        ['title' => 'first post', 'section' => 'sport'],
        ['title' => 'second post', 'section' => 'news'],
    ])

如果您想覆盖现有的子项,可以使用

HasNestedMany::make('Posts', Post::class)
    ->defaultChildren([
        ['title' => 'first post', 'section' => 'sport'],
        ['title' => 'second post', 'section' => 'news'],
    ], true)

附加选项

钩子

您可以在 HasNestedMany 填充数据库前后指定回调。

namespace App\Nova;

use Illuminate\Http\Request;
use Lupennat\NestedMany\Fields\HasManyNested;
use Laravel\Nova\Http\Requests\NovaRequest;

class User extends Resource
{

    public function fields(Request $request)
    {
        return [
            HasNestedMany::make('Posts', Post::class)
                ->beforeFill(function(\App\Models\User $user, NovaRequest $request) {
                    // do stuff
                })
                ->afterFill(function(\App\Models\User $user, NovaRequest $request) {
                    // do stuff
                })
        ];
    }

}

可嵌套资源

资源 trait HasNestedResource 提供了与嵌套相关的新的功能。

可嵌套标题

面板和选项卡视图都使用 nestedTitle 方法检索资源的标题。

如果找不到方法,将回退到原始的 nova 资源标题

namespace App\Nova;

class User extends Resource
{

    public function nestedTitle()
    {
        return $this->resource->name;
    }

}

可嵌套授权

Nested Many 将使用 Laravel Nova 资源授权进行创建/更新/删除。您可以在 3 个新方法中为 Nested Many 定义不同的策略

namespace App\Nova;

class User extends Resource
{

    public static function authorizedToCreateNested(Request $request)
    {
        return true;
    }

    public function authorizedToUpdateNested(Request $request)
    {
        return false;
    }

    public function authorizedToDeleteNested(Request $request)
    {
        return $this->authorizedToDelete($request);
    }

}

可嵌套操作

您可以通过定义嵌套操作通过服务器操作相关内容。

Nested 操作可以通过 $keepOpened 属性或 keepOpened() 方法在操作执行后保持模态打开。

可嵌套基本操作

默认情况下,NestedMany 有 3 个基本操作 NestedBasicAddActionNestedBasicDeleteActionNestedBasicRestoreAction,并且您可以通过资源上的 3 个方法自定义它们。

namespace App\Nova;

use Lupennat\NestedMany\Actions\Basics\NestedBasicAddAction;
use Lupennat\NestedMany\Actions\Basics\NestedBasicDeleteAction;
use Lupennat\NestedMany\Actions\Basics\NestedBasicRestoreAction;
use Laravel\Nova\Http\Requests\NovaRequest;

class Post extends Resource
{

    /**
     * Get the nested create action on the entity.
     */
    public function nestedAddAction(NovaRequest $request): NestedBasicAddAction
    {
        return new \App\Nova\NestedActions\MyCustomAddActionExtendsBasicAddAction();
    }

    /**
     * Get the nested delete action on the entity.
     */
    public function nestedDeleteAction(NovaRequest $request): NestedBasicDeleteAction
    {
        return parent::nestedDeleteAction($request)->withConfirmation();
    }

    /**
     * Get the nested delete action on the entity.
     */
    public function nestedRestoreAction(NovaRequest $request): NestedBasicRestoreAction
    {
        return parent::nestedRestoreAction($request)->withConfirmation();
    }

}

可嵌套软删除操作

NestedDeleteAction 自动支持 softDelete 逻辑(不是真正的 eloquent softDelete),要启用软删除/恢复逻辑,您需要在相关模型上设置 protected $nestedHasSoftDelete = true

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Lupennat\NestedMany\Models\Contracts\Nestable;
use Lupennat\NestedMany\Models\HasNested;

class Post extends Model implements Nestable
{
    use HasNested;

    protected $nestedHasSoftDelete = true;

}

可嵌套自定义操作

您可以使用 nested-many:action Artisan 命令生成嵌套操作。默认情况下,所有操作都放在 app/Nova/NestedActions 目录中

php artisan nested-many:action DuplicatePost

您可以通过传递 --destructive 选项生成破坏性操作

php artisan nested-many:action DeleteAllPosts --destructive

Nested Actions 与 Nova Actions 有很多相似之处,主要区别是 handle 方法应始终返回一个 NestedObject 集合。

您可以使用 $this->getNewNested() 方法生成新的 NestedObject

要了解如何定义 Nova 操作,让我们看看一个示例。在这个例子中,我们将定义一个复制帖子的操作

<?php

namespace App\Nova\NestedActions;

use Illuminate\Support\Collection;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
use Lupennat\NestedMany\Models\Nested;
use Lupennat\NestedMany\Actions\NestedAction;

class DuplicatePost extends NestedAction
{

    /**
     * Handle any post-validation processing.
     *
     * @param \Illuminate\Contracts\Validation\Validator $validator
     * @param \Illuminate\Support\Collection<int,\Lupennat\NestedMany\Models\Nested> $resources
     *
     * @return void
     */
    protected function afterValidation(NovaRequest $request, Collection $resources, $validator)
    {
       // do validation stuff
    }


    /**
     * Perform the action on the given models.
     *
     * @param \Laravel\Nova\Fields\ActionFields $actionFields
     * @param \Illuminate\Support\Collection<int,\Lupennat\NestedMany\Models\Nested> $children
     * @param string|null $selectedUid
     *
     * @return \Illuminate\Support\Collection<int,\Lupennat\NestedMany\Models\Nested>
     */
    public function handle(ActionFields $fields, Collection $children, $selectedUid): Collection
    {
        $selectedNested = $children->where(Nested::UIDFIELD, $selectedUid)->first();

        $children->push(tap($this->getNewNested(), function ($newResource) use ($selectedNested, $fields) {
            foreach ($selectedNested->getAttributes() as $key => $value) {
                if($key !== $selectedNested->getKeyName()) {
                    $newResource->{$key} = $value;
                }
            }
            $newResource->active();
        }));

        return $children;
    }

    /**
     * Get the fields available on the action.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @return array
     */
    public function fields(NovaRequest $request)
    {
        return [];
    }
}

您可以通过在资源中调用 nestedActions 方法注册自定义 NestedActions

namespace App\Nova;

use Lupennat\NestedMany\Actions\Basics\NestedBasicAddAction;

class Post extends Resource
{
    /**
     * Get the actions available on the entity.
     *
     * @return array<\Lupennat\NestedMany\Actions\NestedAction>
     */
    public function nestedActions(NovaRequest $request): array
    {
        return [
            \App\Nova\NestedActions\DuplicatePost::create()
        ];
    }
}

嵌套对象

使用 NestedActions 时,所有 Eloquent 模型默认都包装在扩展 Laravel Fluent 类的 Nested 类中。
所有模型属性都复制到 Nested 属性上,这样您可以直接修改 Nested 对象的属性值。

它提供了便捷的方法来操作对象,而无需对数据库执行查询。

当调用toModel时,如果注册了Mutators,嵌套类将对当前对象应用更改到原始Eloquent模型。

对嵌套对象执行的每个更改,在用户点击父表单页面上的创建/更新按钮之前,都不会存储在数据库中。

鸣谢

NestedForm字段基于原始的Nova嵌套表单