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



我在Nova中有一个(相对)基本的需求,我似乎不能弄清楚,我慢慢地开始觉得我用错误的方式来处理事情。所以,我有一个User, Company, DeviceTransfer模型和各自的资源,关于资源设置的一切都是默认的。模式如下:

  • users: id, company_id
  • companies: id, type_id, name,其中type_id指向三种预填充类型(制造商,经销商,客户端)之一
  • devices: id, imei
  • transfers: id, from_company_id, to_company_id, accepted_at
  • TransferDevice是多对多的。

转移背后的想法是制造商转移到经销商,经销商转移到客户,所以这真的只是一个单向的事情。

现在问题出现在以下逻辑的关键点:在我的Transfer资源页面中,我希望根据当前经过身份验证的用户所属的公司类型显示不同的字段。基本上,如果公司是:

  • 制造商,然后显示DEALER列,其中包含传输的toCompany关系;
  • 经销商,然后显示CONTRAGENT列,其中填充了传输的fromCompanytoCompany关系(取决于哪个匹配当前auth()公司)
  • 客户端,然后显示DEALER列,其中包含传输的fromCompany

所有描述的逻辑与以下代码(AppNovaTransfer.php原样)一起工作良好,直到我想最终在详细信息页面上显示传输的设备:

<?php
namespace AppNova;
use IlluminateHttpRequest;
use LaravelNovaFieldsID;
use LaravelNovaFieldsBelongsTo;
use LaravelNovaFieldsBelongsToMany;
use LaravelNovaHttpRequestsNovaRequest;
class Transfer extends Resource
{
    public static $model = AppModelsTransfer::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 ActionsAcceptTransfer())->showOnTableRow()->canSee(function ($request) {
                if ($request instanceof LaravelNovaHttpRequestsActionRequest) {
                    return true;  
                }
                
                return $this->resource->exists
                        && $this->resource->toCompany->is(auth()->company())
                        && $this->resource->accepted_at === null;
                
            })->canRun(function ($request) {
                return true;
            })
        ];
    }
}

现在发生的奇怪的事情是,fields()方法在多个ajax请求上被调用多次与Nova在幕后,当填充设备关系表时,它在没有资源的情况下被调用,虽然调用从来没有真正需要(就我所能掌握的Nova背后的机制)或至少在抓取关系时,你必须仍然有模型信息(至少ID)某处获取…所以基本上,如果我是经销商公司的用户,我无法看到正在传输的设备(目前抛出calling is() on null异常)。

现在,这恰好是一个大问题,因为它阻碍了我转移所需的大部分东西,而且到目前为止,我通常不喜欢我的方法,所以……实现这一multi-layer资源的正确方法是什么?理想情况下,我想定义三个不同的传输资源类,并以某种方式告诉nova根据用户公司的类型使用哪一个(因为分支很可能会变得更复杂,因此与当前的方法相比更难看),但我不知道怎么做。

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

任何解释(关于fields()的多个调用和为什么资源是空的)或一般结构建议来解决这种情况将非常感谢!提前感谢!

编辑:我能够通过利用viaResourceId来规避这个错误,所以我最终使用:

代替$this
$transfer = $this->id ? $this->resource : AppModelsTransfer::find($request->viaResourceId);

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

下面是我如何处理这个问题的一个例子:

public function fields(NovaRequest $request)
{
    /** @var AppModelsUser $user */
    $user = $this->id ? $this->resource : AppModelsUser::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 [...];
}

最新更新