在 Laravel Nova 中根据资源的模型属性定义字段

问题描述 投票:0回答:2

我在 Nova 有一个(相对)基本的需求,但我似乎无法弄清楚,我慢慢开始觉得我处理事情的方式是错误的。 所以,我有一个

User
Company
Device
Transfer
模型以及各自的资源,关于资源设置的所有内容都是默认的。 架构如下:

  • users
    id, company_id
  • companies
    id, type_id, name
    ,其中
    type_id
    指向三种预填充类型之一(制造商、经销商、客户)
  • devices
    id, imei
  • transfers
    id, from_company_id, to_company_id, accepted_at
  • 并且
    Transfer
    Device
    处于多对多关系中。

转让背后的想法是制造商转让给经销商,经销商转让给客户,所以这实际上只是一种单向的事情。

现在问题出现在以下逻辑关键点: 在我的

Transfer
资源页面中,我想根据当前经过身份验证的用户所属公司的类型显示不同的字段。基本上,如果公司是:

  • 制造商,然后显示一个
    DEALER
    列,其中填充了转移的
    toCompany
    关系;
  • 经销商,然后显示一个
    CONTRAGENT
    列,其中填充了转账的
    fromCompany
    toCompany
    关系(取决于当前 auth() 公司的数学)
  • 客户端,然后显示一个
    DEALER
    列,其中填充了转账的
    fromCompany

所有描述的逻辑都可以与以下代码一起正常工作(

App\Nova\Transfer.php
原样),直到我想最终在详细信息页面上显示传输的设备:

<?php

namespace App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\BelongsToMany;
use Laravel\Nova\Http\Requests\NovaRequest;

class Transfer extends Resource
{
    public static $model = \App\Models\Transfer::class;

    public static $title = 'id';

    public static $search = [
        'id',
    ];
    
    public static $with = [
        'fromCompany',
        'toCompany'
    ];

    public function fields(Request $request)
    {
        $company = auth()->company();
        
        if($company->hasType('manufacturer'))
        {
            $contragentTitle = 'Dealer';
            $contragent = 'toCompany';
        } 
        else if($company->hasType('dealer'))
        {
            //\Debugbar::info($this); //showing empty resource when populating the devices
            $contragentTitle = 'Contragent';
            $contragent = $this->fromCompany->is($company) ? 'toCompany' : 'fromCompany'; //exception here, since the resource is empty and fromCompany is null
        } 
        else
        {
            $contragentTitle = 'Dealer';
            $contragent = 'fromCompany';
        }
        
        $contragentCompanyField = BelongsTo::make("$contragentTitle company", $contragent, Company::class);
        
        if($company->hasType('dealer'))
        {
            $contragentCompanyField->displayUsing(function ($contragentCompany) use ($contragent){
                return $contragentCompany->title() . " (".($contragent == 'toCompany' ? 'Outgoing' : "Incoming").')';
            });
        }
        
        return [
            ID::make(__('ID'), 'id')->sortable(),
            $contragentCompanyField,
            BelongsToMany::make('Devices') //problematic field, when removed, everything is fine...
        ];
    }
    
    public static function indexQuery(NovaRequest $request, $query)
    {
        if(auth()->check())
        {
            return $query->where(function($subQuery){
                return $subQuery->where('from_company_id', auth()->company()->id)->orWhere('to_company_id', auth()->company()->id);
            });
        }
    }

    public function cards(Request $request)
    {
        return [];
    }

    public function filters(Request $request)
    {
        return [];
    }

    public function lenses(Request $request)
    {
        return [];
    }

    //action is working fine (additional canRun added to avoid policy conflicts)
    public function actions(Request $request)
    {
        return [
            (new Actions\AcceptTransfer())->showOnTableRow()->canSee(function ($request) {
                if ($request instanceof \Laravel\Nova\Http\Requests\ActionRequest) {
                    return true;  
                }
                
                return $this->resource->exists
                        && $this->resource->toCompany->is(auth()->company())
                        && $this->resource->accepted_at === null;
                
            })->canRun(function ($request) {
                return true;
            })
        ];
    }
}

现在发生的奇怪的事情是,

fields()
方法在Nova后台的多个ajax请求中被多次调用,并且在填充设备关系表时,它在没有资源的情况下被调用,尽管实际上从来不需要调用(据我所知,Nova 背后的机制)或者至少在获取关系时,您仍然必须在某处获取模型信息(至少是 ID)...所以基本上,如果我是经销商公司,我看不到正在转移的设备(当前抛出
calling is() on null
异常)。

现在,这恰好是一个大问题,因为它阻碍了我转账所需的大部分内容,但总的来说,到目前为止我不喜欢我的方法,所以......实现这一目标的正确方法是什么

multi-layer
资源?理想情况下,我想定义三种不同的传输资源类,并以某种方式告诉 nova 根据用户公司的类型使用哪一种(因为分支很可能会变得更加复杂,因此按照当前的方法变得更加丑陋),但我可以'不知道该怎么做。

我也考虑过将整个逻辑转移到一个单独的 Nova 工具中,但我真的对它们还不太了解,也不知道这是否是正确的选择......唯一阻止我的是我仍然不会能够优雅地解决多层问题,并且必须自己编写许多其他有用的 Nova CRUD 逻辑和视图...

任何解释(关于

fields()
的多次调用以及为什么资源为空)或解决这种情况的一般结构建议将不胜感激!非常感谢!

编辑: 我能够通过利用

viaResourceId
来规避该错误,因此我最终使用了
$this
来代替:

$transfer = $this->id ? $this->resource : \App\Models\Transfer::find($request->viaResourceId);

但是混乱的代码和不必要的调用仍然是一个悬而未决的问题。再次提前致谢!

laravel laravel-8 laravel-nova
2个回答
0
投票

这是我如何处理此问题的示例:

public function fields(NovaRequest $request)
{
    /** @var \App\Models\User $user */
    $user = $this->id ? $this->resource : \App\Models\User::find($request->viaResourceId);
    
    if ($user && $user->whatEver()) {
        // display special fields in preview/detail view
        return [...];
    }
    
    // display for index and if no model is found
    return [...];
}

0
投票
public function fields(NovaRequest $request)
{
    /** @var \App\Models\User $user */
    $user = $request->findModel();
    
    if ($user && $user->whatEver()) {
        // display special fields in preview/detail view
        return [...];
    }
    
    // display for index and if no model is found
    return [...];
}

从请求中检索具有所有属性的模型(Nova 4.34.3) 在 BelongsToMany 字段中,您可以使用

$request->findParentModel()
代替。

还检查 NovaRequest 对象上的这些方法:

findModelOrFail
findModelQuery
findRelatedModel

© www.soinside.com 2019 - 2024. All rights reserved.