Axel Libori Roch
How to Show a Relationship Counter in the Laravel Nova Resource Index
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.
1. Preload the Relationship Counter
To start, we need to preload the relationship counter to avoid N+1 issues and improve performance. This can be achieved as follows:
// app\Nova\Post.php
public static function indexQuery(NovaRequest $request, Builder $query): Builder
{
return $query->withCount('comments');
}
2. Display the Counter in a Number Field
Now that we have the counter, we can easily display it in a Number Field within the resource:
// app\Nova\Post.php
Number::make('Comments', 'comments_count')
->sortable();
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!
3. Add a Link to the Counter
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:
// app\Nova\Filters\PostFilter.php
final class PostFilter extends Filter
{
/**
* The filter's component.
*
* @var string
*/
public $component = 'select-filter';
public function name(): string
{
return __('Post');
}
/**
* Apply the filter to the given query.
*/
public function apply(NovaRequest $request, Builder $query, mixed $value): Builder
{
return $query->where('post_id', $value);
}
/**
* Get the filter's available options.
*
* @return array<mixed>
*/
public function options(NovaRequest $request): array
{
$posts = Post::whereHas('comments')
->select(['id', 'title'])
->get();
return $posts->mapWithKeys(fn ($post) => [$post->title => $post->id])->toArray();
}
}
// app\Nova\Comment.php
public function filters(NovaRequest $request): array
{
return [
new PostFilter(),
];
}
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:
// app\Nova\Post.php
Text::make(__('Comments'), 'comments_count', function (): string {
if ($this->comments_count === 0) {
return '0';
}
$filterBase64 = base64_encode((string) json_encode([
[
'class' => PostFilter::class,
'value' => $this->id,
],
]));
return '<a href="' . route('nova.pages.index', [
'resource' => 'comment-resources',
'comment-resources_filter' => $filterBase64,
]) . '">' . $this->comments_count . '</a>';
})
->asHtml()
->sortable()
->onlyOnIndex();
4. Benefits of This Implementation
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!