logo

模型关联

除了我们已经讨论过的各种字段,Nova 还完全支持 Laravel 的所有模型关联。在 Nova 资源中添加关联字段后,你就可以开始体验 Nova 面板的全部功能,因为资源详情页将允许你快速查看和搜索资源的相关关联模型:

Detail page Relationship

HasOne 一对一

这个 HasOne 字段对应一个 HasOne Eloquent 关系。例如,让我们假设一个 User 模型 hasOne Address 模型。我们可以将这种关系添加到我们的 User Nova 资源中,如下所示:

php
use Laravel\Nova\Fields\HasOne;

HasOne::make('Address'),

与其他类型的字段一样,关系字段将自动对字段的可显示名称进行 「驼峰式大小写」,以确定底层关系 方法/属性。不过,你可以将关系方法的名称作为第二个参数传递给字段的 make 方法,从而明确指定关系方法的名称:

php
HasOne::make('Dirección', 'address'),

HasOneOfMany 一对多检索

HasOne 关联字段可以使用 ofMany 方法转换成「拥有众多中的一个」的 Eloquent 关系。例如,我们假设一个 User 模型拥有多个 Post 模型。我们可以将「拥有众多中的一个」关系添加到我们的 User Nova 资源中,如下所示:

php
use App\Nova\Post;
use Laravel\Nova\Fields\HasOne;

HasOne::ofMany('Latest Post', 'latestPost', Post::class),

HasMany 一对多

HasMany 字段对应于 HasMany Eloquent 关联。例如,假设 User 模型拥有多个 Post 模型。我们可以将该关系添加到我们的 User Nova 资源中,如下所示:

php
use Laravel\Nova\Fields\HasMany;

HasMany::make('Posts'),

字段添加到资源后,将显示在资源的详细信息页面上。

复数资源名称

定义 HasMany 关系时,确保使用关系的复数形式,以便 Nova 可以推断出正确的单数资源名称:

php
HasMany::make('Posts'),

HasOneThrough 远程一对一

HasOneThrough 字段对应于 Eloquent 的 HasOneThrough 关联。例如,假设一个 Mechanic 模型有一辆 Car,而每辆 Car 可能有一个 Owner。虽然 MechanicOwner 之间没有直接联系,但 Mechanic 可以通过 Car 本身访问 Owner。你可以将这种关系添加到 Nova 资源中:

php
use Laravel\Nova\Fields\HasOneThrough;

HasOneThrough::make('Owner'),

HasManyThrough 远程一对多

HasManyThrough 字段对应于 Eloquent 的 HasManyThrough 关系。例如,Country 模型可能通过中间的 User 模型拥有许多 Post 模型。在此示例中,你可以轻松收集某个国家的所有博客帖子。要在 Nova 中显示这种关系,你可以将其添加到 Nova 资源中:

php
use Laravel\Nova\Fields\HasManyThrough;

HasManyThrough::make('Posts'),

BelongsTo 一对多 (反向) / 属于

BelongsTo 字段对应于 BelongsTo Eloquent 关联。例如,让我们假设一个 Post 模型 belongsTo 一个 User 模型。我们可以像下面这样在我们的 Post Nova 资源中添加这种关系:

php
use Laravel\Nova\Fields\BelongsTo;

BelongsTo::make('User'),

自定义资源类

你可以通过提供 make 方法的第二和第三个参数自定义关系字段使用的资源类,这两个参数定义了关联的名称和底层 Nova 资源类:

php
BelongsTo::make('Author', 'author', 'App\Nova\User'),

查看关联关系

在查看索引或详细视图时,将鼠标悬停在 BelongsTo 链接上,Nova 会显示一个小卡片,让你「看一下」关联信息:

Relationship Peeking

禁止查看 BelongsTo 关联

默认情况下,查看关联是启用的;但是,你可以使用 BelongsTo 字段上的 noPeeking 辅助函数阻止用户查看关联:

php
BelongsTo::make('Author')->noPeeking()

你还可以使用 peekable 方法来确定是否允许用户查看关联:

php
use Laravel\Nova\Http\Requests\NovaRequest;

BelongsTo::make('Author')->peekable(function (NovaRequest $request) {
    return $request->isResourceDetailRequest();
})

可为空的关联

如果你希望你的 BelongsTo 关联允许为空 nullable,你可以简单地将 nullable 方法链到字段的定义上:

php
use Laravel\Nova\Fields\BelongsTo;

BelongsTo::make('User')->nullable(),

标题属性

在资源创建/更新页面显示 BelongsTo 字段时,下拉选择菜单或搜索菜单将显示资源的 "title" 。例如,User 资源可以使用 name 属性作为标题。然后,当该资源显示在 BelongsTo 选择菜单中时,就会显示该属性:

Belongs To Title

要自定义资源的 "title" 属性,可以在资源类上定义一个 title 属性:

php
public static $title = 'name';

或者,你也可以覆盖资源的 title 方法:

php
/**
 * 获取代表资源的显示值。
 *
 * @return string
 */
public function title()
{
    return $this->name;
}

禁用按标题排序

默认情况下,在选择下拉菜单中列出的可关联资源将按标题排序。使用 dontReorderAssociatables 方法,可以禁用此行为,从而根据 关联查询 指定的排序对资源进行排序:

php
BelongsTo::make('User')->dontReorderAssociatables(),

过滤回收站软删除的项目

默认情况下,BelongsTo 字段将允许用户选择软删除的模型;不过,可以使用 withoutTrashed 方法禁用此功能:

php
BelongsTo::make('User')->withoutTrashed(),

BelongsToMany 多对多关联

BelongsToMany 字段对应于模型的 BelongsToMany 关联。例如,我们假定有一个 User 模型 belongsToMany Role 模型:

php
public function roles()
{
    return $this->belongsToMany(Role::class);
}

我们可以像这样将关系添加到我们的 User Nova 资源中:

php
use Laravel\Nova\Fields\BelongsToMany;

BelongsToMany::make('Roles'),

你可以通过向 make 方法提供第二和第三个参数,自定义关联字段使用的资源类:

php
BelongsToMany::make('Pseudonyms', 'pseudonyms', 'App\Nova\Author'),

字段添加到资源后,将显示在资源的详细信息页面上。

Pivot Fields 中间表字段

如果你的 BelongsToMany 关联与存储在多对多关系中间表中的其他关联字段交互,你也可以将这些字段附加到你的 BelongsToMany Nova 关联中。一旦将这些字段附加到关联字段,并且在两个相关模型/资源上都定义了关系,它们就会显示在相关资源索引上。

例如,假设我们的 User 模型 belongsToMany Role 模型。在我们的 role_user 中间表中,假设我们有一个 notes 字段,其中包含一些关于关系的简单文本注释。我们可以使用 fields 方法将此透视字段附加到 BelongsToMany 字段:

php
BelongsToMany::make('Roles')
    ->fields(function ($request, $relatedModel) {
        return [
            Text::make('Notes'),
        ];
    }),

当然,这也可以在定义相反关联时使用。所以,如果我们在 User 资源中定义了一个 BelongsToMany 字段,我们也可以在 Role 资源中定义反向关联:

php
BelongsToMany::make('Users')
    ->fields(function ($request, $relatedModel) {
        return [
            Text::make('Notes'),
        ];
    }),

必须定义中间表字段

不要忘记在模型的关系定义中使用 withPivot 方法定义中间表字段: https://laravel.com/docs/eloquent-relationships#retrieving-intermediate-table-columns

在两处分别定义这样的关联可能会让我们的代码重复,Nova 允许你通过传入一个可调用的对象给 fields 方法:

php
BelongsToMany::make('Users')->fields(new RoleUserFields),

上面的例子中,RoleUserFields 类可以只有一个 __invoke 方法,并返回一个包含附加字段的数组:

php
<?php

namespace App\Nova;

use Laravel\Nova\Fields\Text;

class RoleUserFields
{
    /**
     * 获取关联模型的附加字段。
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  \Illuminate\Database\Eloquent\Model  $relatedModel
     * @return array
     */
    public function __invoke($request, $relatedModel)
    {
        return [
            Text::make('Notes'),
        ];
    }
}

Pivot Computed Fields 中间表计算字段

Laravel Nova 还允许你在 belongsToMany 关联字段的字段列表中定义计算字段:

php
BelongsToMany::make('Users')
    ->fields(function ($request, $relatedModel) {
        return [
            Text::make('Notes'),

            Boolean::make('Has Notes', function ($pivot) {
                return ! empty($pivot->notes);
            }),
        ];
    }),

Pivot Actions 中间表动作

通常情况下,Nova 动作 对资源进行操作。不过,你也可以将操作附加到 belongsToMany 字段,以便对中间表记录进行操作。为此,你可以将 actions 方法链到字段的定义上:

php
BelongsToMany::make('Roles')
    ->actions(function () {
        return [
            new Actions\MarkAsActive,
        ];
    }),

将操作附加到字段后,你就可以在父资源详细信息页面的关系索引中选择操作并执行它。

动作

要了解有关 Nova 动作的更多信息,请查看文档 action documentation.

标题属性

在资源创建/更新页面显示 BelongsToMany 字段时,下拉选择菜单或搜索菜单将显示资源的 "title"。例如,Role 资源可以使用 name 属性作为标题。然后,当该资源显示在 BelongsToMany 选择菜单中时,就会显示该属性:

Belongs To Many Title

要自定义资源的 "title" 属性,可以在资源类上定义一个 title 属性:

php
public static $title = 'name';

或者,你也可以覆盖资源的 title 方法:

php
/**
 * 获取代表资源的显示值。
 *
 * @return string
 */
public function title()
{
    return $this->name;
}

禁用按标题排序

默认情况下,在选择下拉菜单中列出的可关联资源将按标题排序。使用 dontReorderAttachables 方法,可以禁用此行为,从而根据 关联查询 指定的排序对资源进行排序:

php
BelongsToMany::make('Roles')->dontReorderAttachables(),

允许重复关系

默认情况下,Laravel Nova 会确保「属于多个」关系是唯一的。不过,如有必要,你可以指示 Nova 允许重复的关联条目。

要开始使用,你应该在模型上定义关联时使用 withPivot 方法,确保透视记录的 id 列可用。在这个示例中,让我们想象一下,User 可能会购买 Book 一次或多次:

php
public function books()
{
    return $this->belongsToMany(Book::class)
                ->using(BookPurchase::class)
                ->withPivot('id', 'notes')
                ->withTimestamps();
}

接下来,我们可以使用 allowDuplicateRelations 方法定义允许重复关联的 Nova 关系:

php
BelongsToMany::make('Books')
    ->fields(function () {
        return [
            Text::make('Notes'),
        ];
    })->allowDuplicateRelations(),

MorphOne 一对一 (多态)

MorphOne 字段对应于 Eloquent 的 morphOne 关系。例如,假设 PostImage 模型有一对一的多态关系。我们可以像下面这样把这种关系添加到我们的 Post Nova 资源中:

php
use Laravel\Nova\Fields\MorphOne;

MorphOne::make('Image'),

MorphOneOfMany 一对多检索(多态)

使用 ofMany 方法,可以将 MorphOne 关系字段转换为 "morph one of many" 的 Eloquent 关系。例如,假设 PostComment 模型有一对多的多态关系。我们可以像下面这样将这种关系添加到我们的 Post Nova 资源中:

php
use App\Nova\Comment;
use Laravel\Nova\Fields\MorphOne;

MorphOne::ofMany('Latest Comment', 'latestComment', Comment::class),

MorphMany 一对多(多态)

MorphMany 字段对应于 MorphMany Eloquent 关系。例如,假设 PostComment 模型有一对多的多态关系。我们可以像下面这样将这种关系添加到我们的 Post Nova 资源中:

php
use Laravel\Nova\Fields\MorphMany;

MorphMany::make('Comments'),

MorphTo 一对一 (多态)

MorphTo 字段对应于 Eloquent 的 MorphTo 关系。例如,假设 Comment 模型与 PostVideo 模型都有多态关系。我们可以像下面这样把这种关系添加到我们的 Comment Nova 资源中:

php
use App\Nova\Post;
use App\Nova\Video;
use Laravel\Nova\Fields\MorphTo;

MorphTo::make('Commentable')->types([
    Post::class,
    Video::class,
]),

如上例所示,types 方法用于指示 MorphTo 字段可关联的资源类型。Nova 将使用这些信息在创建和更新页面上填充 MorphTo 字段的类型选择菜单:

Morph To Type

MorphTo 标题属性

在资源创建/更新页面显示 MorphTo 字段时,将自动显示可用资源的 标题属性

可为空的 MorphTo 关联

如果希望你的 MorphTo 关联是 nullable 的,请将 nullable 方法链到字段的定义上:

php
use App\Nova\Post;
use App\Nova\Video;
use Laravel\Nova\Fields\MorphTo;

MorphTo::make('Commentable')->types([
    Post::class,
    Video::class,
])->nullable(),

查看 MorphTo 关联

在查看索引或详细视图时,将鼠标悬停在 MorphTo 链接上,Nova 会显示一个小卡片,让你「看一下」关联信息:

Relationship Peeking

禁止查看 MorphTo 关联

默认情况下,查看关联是启用的;但是,你可以使用 MorphTo 字段上的 noPeeking 辅助函数阻止用户查看关联:

php
MorphTo::make('Author')->noPeeking()

你还可以使用 peekable 方法来确定是否允许用户查看关联:

php
use Laravel\Nova\Http\Requests\NovaRequest;

MorphTo::make('Author')->peekable(function (NovaRequest $request) {
    return $request->isResourceDetailRequest();
})

MorphTo 关联上设置默认值

MorphTo 字段设置默认值时,除了使用 default 方法设置字段初始值外,还需要使用 defaultResource 方法指定要使用的资源的类名:

php
use App\Nova\Post;

MorphTo::make('Commentable')
    ->default(1)
    ->defaultResource(Post::class)

MorphToMany 多对多(多态)

MorphToMany 字段对应于 MorphToMany Eloquent 关系。例如,让我们假设一个 Post 模型与 Tag 模型有多对多多态关系。 我们可以将这种关系添加到我们的 Post Nova 资源中,如下所示:

php
use Laravel\Nova\Fields\MorphToMany;

MorphToMany::make('Tags'),

Pivot Fields 中间表字段

如果你的 morphToMany 关系与存储在多对多关系的中间表中的其他字段进行交互,则你也可以将其附加到 morphToMany Nova 关系中。将这些字段附加到关系字段后,它们将显示在相关的资源索引上。

例如,在我们的 taggables 中间表中,假设我们有一个 notes 字段,其中包含一些关于关系的简单文本注释。我们可以使用 fields 方法将该中间字段附加到 MorphToMany 字段:

php
MorphToMany::make('Tags')
    ->fields(function ($request, $relatedModel) {
        return [
            Text::make('Notes'),
        ];
    }),

当然,我们很可能也会在关联的反方向定义这个字段。 所以,如果我们在 Post 资源上定义 MorphToMany 字段,我们将在 Tag 资源中定义它的反向关联:

php
MorphToMany::make('Posts')
    ->fields(function ($request, $relatedModel) {
        return [
            Text::make('Notes'),
        ];
    }),

必须定义中间表字段

不要忘记在模型的关系定义中使用 withPivot 方法定义中间表字段: https://laravel.com/docs/eloquent-relationships#retrieving-intermediate-table-columns

在两处分别定义这样的关联可能会让我们的代码重复,Nova 允许你通过传入一个可调用的对象给 fields 方法:

php
MorphToMany::make('Users')->fields(new TaggableFields),

上面的例子中,TaggableFields 类可以只有一个 __invoke 方法,并返回一个包含附加字段的数组:

php
<?php

namespace App\Nova;

use Laravel\Nova\Fields\Text;

class TaggableFields
{
    /**
     * 获取关联模型的附加字段。
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  \Illuminate\Database\Eloquent\Model  $relatedModel
     * @return array
     */
    public function __invoke($request, $relatedModel)
    {
        return [
            Text::make('Notes'),
        ];
    }
}

标题属性

在资源创建/更新页面显示 MorphToMany 字段时,下拉选择菜单或搜索菜单将显示资源的 "title"。例如, Tag 资源可以使用 name 属性作为标题。然后,当资源显示在 MorphToMany 选择菜单中时,就会显示该属性:

Morph To Many Title

要自定义资源的 "title" 属性,可以在资源类上定义一个 title 属性:

php
public static $title = 'name';

或者,你也可以覆盖资源的 title 方法:

php
/**
 * 获取代表资源的显示值。
 *
 * @return string
 */
public function title()
{
    return $this->name;
}

可折叠关联

默认情况下,BelongsToMany, HasMany, 和 MorphToMany 关系字段显示在资源详细页面上。但是,如果一个资源有许多性能密集型关系,导致页面速度变慢,那么这很快就会变得繁琐。

因此,Nova 允许你将关联标记为 collapsable。当关联可折叠时,用户可以折叠给定资源的某些关联,Nova 会在后续页面加载时记住他们的偏好。在 Nova 的用户界面中展开关联之前,不会从数据库中检索折叠关联:

php
MorphToMany::make('Tags')->collapsable(),

你还可以通过 collapsedByDefault 方法来指示关联应始终默认折叠:

php
MorphToMany::make('Tags')->collapsedByDefault(),

关联搜索

默认情况下,在资源创建/更新页面显示 BelongsTo, MorphTo, BelongsToManyMorphToMany 关联字段时,会显示一个简单的下拉选择菜单。但是,如果资源模型有很多记录,这很快就会变得很麻烦。例如,试想一个下拉选择菜单中包含 10,000 多个用户!

你可以将关系标记为 searchable,而不是显示下拉选择菜单。当关系被标记为 searchable 时,将显示一个漂亮的搜索输入控件:

Belongs To Search

要将关系标记为 searchable,可将 searchable 方法链到字段的定义上。如果要有条件地确定某个字段是否可搜索,可以向 searchable 方法传递一个闭包:

php
BelongsTo::make('User')->searchable(),

BelongsTo::make('User')->searchable(function ($request) {
    return true;
}),

你还可以在定义字段时调用 withSubtitles 方法,指示关联字段显示 资源愿子标题

php
BelongsTo::make('User')->searchable()->withSubtitles(),

关联查询筛选

如果你想自定义相关查询,可以通过调用 relatableQueryUsing 方法来实现:

php
use Illuminate\Database\Eloquent\Builder;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Http\Requests\NovaRequest;

BelongsTo::make('User')
    ->relatableQueryUsing(function (NovaRequest $request, Builder $query) {
        $query->whereIn('teams', ['editor', 'writer']);
    }),

当需要根据另一个字段的值调整查询时,relatableQueryUsing 方法也可能被证明是有用的:

php
use Illuminate\Database\Eloquent\Builder;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\FormData;
use Laravel\Nova\Http\Requests\NovaRequest;

BelongsTo::make('User')
    ->dependsOn('topic', function (BelongsTo $field, NovaRequest $request, FormData $formData) {
        if ($formData->topic === 'laravel-nova') {
            $field->relatableQueryUsing(function (NovaRequest $request, Builder $query) {
                $query->whereIn('email', ['[email protected]', '[email protected]']);
            });
        }
    }),

限制关联数据数量

通过在搜索资源的类上定义 relatableSearchResults 属性,可以限制搜索字段时返回结果的数量:

php
/**
 * 搜索无 Scout 的相关资源时显示的结果数量。
 *
 * @var int|null
 */
public static $relatableSearchResults = 200;

创建内联关联

为方便起见,当 BelongsToMorphTo 关联字段显示在资源创建或更新页面上时,你可以通过模式窗口内联创建相关资源,而无需离开创建/更新页面:

Creating Inline Relations

要启用此功能,请在定义关联字段时调用 showCreateRelationButton 方法:

php
BelongsTo::make('User')->showCreateRelationButton(),

你还可以向 showCreateRelationButton 方法传递一个闭包,按照条件是否启用内联资源创建:

php
BelongsTo::make('User')->showCreateRelationButton(function ($request) {
    //
}),

你也可以在 "附加"和 "更新附加" 页面创建相关的多对多关系。要启用此功能,请在定义 BelongsToManyMorphToMany关系时调用 showCreateRelationButton

php
BelongsToMany::make('Roles')->showCreateRelationButton(),

要隐藏内联创建按钮,请在定义关系字段时调用 hideCreateRelationButton 方法:

php
BelongsTo::make('User')->hideCreateRelationButton(),

内联关系创建过程将遵守定义的任何 授权策略

内联创建模式尺寸

你可以使用 modalSize 方法调整模态大小:

php
// 可以使用 "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "7xl".
BelongsTo::make('User')->showCreateRelationButton()->modalSize('7xl'),

内联创建限制

内联关系创建只支持创建 一层 关系。这意味着你不能在现有的内联创建模式内触发额外的内联创建模式。相反,你必须选择一个已经存在的资源。