When working with Laravel Nova, it can sometimes be useful to display a counter of the results of a relationship in the index of a resource. This can enhance the user experience and provide a clearer view of the relationship between resources. In this post, we will see how to do it.
To start, we need to preload the relationship counter to avoid N+1 issues and improve performance. This can be achieved as follows:
1 2// app\Nova\Post.php3 4public static function indexQuery(NovaRequest $request, Builder $query): Builder5{6 return $query->withCount('comments');7}
Code highlighting powered by torchlight.dev.
Now that we have the counter, we can easily display it in a Number Field within the resource:
1 2// app\Nova\Post.php3 4Number::make('Comments', 'comments_count')5 ->sortable()
Code highlighting powered by torchlight.dev.
This way, we will have the counter available in the listing, ensuring that we avoid N+1 issues. But what if we want this number to also be a link that takes us to the filtered listing of the resource by the relationship? This functionality is also possible!
To add the functionality of linking the counter to a filtered view, we will follow these steps:
3.1. Create a Filter and Register it in the Resource
First, we create a custom filter to be able to filter the results by the relationship. This filter must be registered in the resource where we want to apply it:
1 2// app\Nova\Filters\PostFilter.php 3 4final class PostFilter extends Filter 5{ 6 /** 7 * The filter's component. 8 * 9 * @var string10 */11 public $component = 'select-filter';12 13 public function name(): string14 {15 return __('Post');16 }17 18 /**19 * Apply the filter to the given query.20 */21 public function apply(NovaRequest $request, Builder $query, mixed $value): Builder22 {23 return $query->where('post_id', $value);24 }25 26 /**27 * Get the filter's available options.28 *29 * @return array<mixed>30 */31 public function options(NovaRequest $request): array32 {33 $posts = Post::whereHas('comments')34 ->select(['id', 'title'])35 ->get();36 37 return $posts->mapWithKeys(fn ($post) => [$post->title => $post->id])->toArray();38 }39}
Code highlighting powered by torchlight.dev.
1 2// app\Nova\Comment.php3 4public function filters(NovaRequest $request): array5{6 return [7 new PostFilter(),8 ];9}
Code highlighting powered by torchlight.dev.
3.2. Convert the Number Field into a Text Field with a Link
Finally, we convert the Number Field into a custom Text Field. This way, when the counter is greater than 0, we will display a link to the resource, applying the corresponding filter:
1 2// app\Nova\Post.php 3 4Text::make(__('Comments'), 'comments_count', function (): string { 5 if (0 === $this->comments_count) { 6 return '0'; 7 } else { 8 $filterBase64 = base64_encode((string)json_encode([ 9 [10 'class' => PostFilter::class,11 'value' => $this->id,12 ],13 ]));14 15 return '<a href="'.route('nova.pages.index', ['resource' => 'comment-resources', 'comment-resources_filter' => $filterBase64]).'">'.$this->comments_count.'</a>';16 }17 })18 ->asHtml()19 ->sortable()20 ->onlyOnIndex(),
Code highlighting powered by torchlight.dev.
These small improvements can make a big difference in the admin panels we develop. By offering a quick way to navigate to related resources, we are improving the usability and efficiency for admins and teams managing the data.
Make the difference!