axliro.dev
Back to articles
Axel Libori Roch

Axel Libori Roch

· 2 min read

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!

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(),
    ];
}

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!