Search code examples
laravellaravel-nova

Display nested relation variable on resource index


I am developing a Laravel admin using Laravel Nova. I have DB structure:

works
   id
   device_id
   work_type

devices
   id
   serial_number
   device_type_id

device_types
   id
   device_name
   brand_id

brands
   id
   brand

This is the Work model class

class Work extends Model {

    public function device() 
    {
        return $this->belongsTo( device::class );
    }

}

This is the Device model class

class Device extends Model {

    public function deviceType() 
    {
        return $this->belongsTo( DeviceType::class, 'device_type_id' );
    }

}

This is the DeviceType model class

class DeviceType extends Model
{
    public function brand()
    {
        return $this->belongsTo( Brand::class );
    }

}

This is my actual Nova resource


class Work extends Resource
{
    
    public function fields(Request $request)
    {
        return [
                BelongsTo::make( 'Serial number', 'device', 'App\Nova\Device' )->searchable()
         ];
    }
}

On the Laravel Nova "Works" index page, I would like something like that: | Work ID | Serial number | Device type | Brand | | -------- | -------------- | ----------- | ------- | | 1234 | 12345678901234 | S20 | Samsung | | 2345 | 99999999999999 | S21 | Samsung |

I have no idea how to display "Device type" and "Brand" columns.

These columns also should be sortable.


Solution

  • To display device_type and brand as columns on your Works index page is fairly simple. Just add the device_type and brand as custom attributes to your Works model. Afterwards, you can access them in your Nova resource.

    Work model

    class Work extends Model {
    
        public function device() 
        {
            return $this->belongsTo( device::class );
        }
    
        public function getBrandAttribute(): string
        {
            return $this->device->deviceType->brand->brand ?? '';
        }
    
        public function getDeviceTypeAttribute(): string
        {
            return $this->device->deviceType->device_name ?? '';
        }
    
    }
    

    Work Nova Resource

    class Work extends Resource
    {
        
        // Override indexQuery to your liking, joining tables
        public static function indexQuery(NovaRequest $request, $query)
        {
            return $query->join('devices', 'devices.id',  '=','works.device_id')
                ->join('device_types', 'device_types.id', '=', 'device_type_id')
                ->join('brands', 'brands.id', '=', 'brand_id')
                ->select([
                    'works.*', 
                    'devices.device_type_id as device_type_id', 
                    'devices_types.device_name as device_name', 
                    'devices_types.brand_id as brand_id', 
                    'brands.brand as brand'
                ]);
        }
    
        public function fields(Request $request)
        {
            return [
                BelongsTo::make( 'Serial number', 'device', 'App\Nova\Device' )
                    ->searchable(),
    
                Text::make('Device name', 'device_name')
                    ->onlyOnIndex()
                    ->sortable(),
    
                Text::make('Brand', 'brand')
                    ->onlyOnIndex()
                    ->sortable(),
             ];
        }
    }
    

    Unfortunately, sorting is not out-of-the-box possible with Laravel Nova on these custom attributes, since the sorting is done on Database level.

    Alternatively, you could try and updating the indexQuery function as described here. There might be packages out there that add this functionality.

    Edit

    I have tinkered around with this in one of my own projects, it does seem to work by overriding the indexQuery. However, I do not have the models set-up the way you do so I did not test my answer. I also do not know how well this performs on large scale. See example above, and let me know if it works. Btw, you can remove the custom attributes from your model if you decide to override the indexQuery method.