Skip to content

Nova Resource Conventions

Class Structure

  1. Resources should be final by default:
php
/** @extends \App\Nova\Resources\Resource<\App\Modules\YourModule\Models\YourModel> */
final class YourResource extends Resource
{
}
  1. Always use PHPDoc with template annotation to specify the model type:
php
/** @extends \App\Nova\Resources\Resource<\App\Modules\Course\Models\Course> */

Organization

Resources are organized by module/domain directory:

  • app/Course/Nova/ - Course-related resources
  • app/Member/Nova/ - Member-related resources
  • app/Payment/Nova/ - Payment-related resources
  • etc.

Common Patterns

  1. Resources extend the base \App\Nova\Resources\Resource class

  2. Resources are placed in a namespace matching their module

  3. Resources are named after their model without the "Model" suffix

  4. Every field must have a help text using translation keys:

    php
    Text::make('Title', 'title')
        ->help(__('nova-your-resource.title')),
    
    BelongsTo::make('Kitchen', 'kitchen')
        ->help(__('nova-your-resource.kitchen')),

Resource Components

Each resource typically includes:

  1. Fields definition:
php
public function fields(NovaRequest $request): array
{
    return [
        // Fields here
    ];
}
  1. Custom field types from App\Nova\Fields:
php
use App\Nova\Fields\DateTime;use App\Nova\Fields\Money;

public function fields(NovaRequest $request): array
{
    return [
        DateTime::make('Created At'),
        Money::make('Amount'),
    ];
}
  1. Filters in a dedicated directory per module:
php
public function filters(NovaRequest $request): array
{
    return [
        new Filters\YourFilter,
    ];
}
  1. Actions in a dedicated directory per module:
php
public function actions(NovaRequest $request): array
{
    return [
        new Actions\YourAction,
    ];
}

Best Practices

  1. Keep resources focused on their primary responsibility
  2. Use custom fields for consistent display of specific data types
  3. Group related functionality in dedicated directories (Actions, Filters, etc.)
  4. Follow Laravel Nova's conventions for method names and structure
  5. Use type hints and docblocks consistently
  6. Place business logic in the model or dedicated services, not in the resource
  7. Always provide help text for every field using translation keys (to make editing help text friendly for non-technical colleagues)

Examples

Basic Resource

php
namespace App\Course\Nova;

use App\Nova\Resources\Resource;use Laravel\Nova\Http\Requests\NovaRequest;

/** @extends \App\Nova\Resources\Resource<\App\Modules\Course\Models\Course> */
final class Course extends Resource
{
    public static $model = \App\Modules\Course\Models\Course::class;

    public function fields(NovaRequest $request): array
    {
        return [
            ID::make()->sortable()->help(__('nova-courses.help.fields.id')),
            Text::make('Title')->help(__('nova-courses.help.fields.title')),
            DateTime::make('Created At')->help(__('nova-courses.help.fields.created_at')),
        ];
    }
}

Resource with Custom Components

php
namespace App\Nova\Payment;

use App\Nova\Resources\Resource;use Laravel\Nova\Http\Requests\NovaRequest;

/** @extends \App\Nova\Resources\Resource<\App\Modules\Payment\Models\Transaction> */
final class Transaction extends Resource
{
    public static $model = \App\Modules\Payment\Models\Transaction::class;

    public function fields(NovaRequest $request): array
    {
        return [
            ID::make()->sortable()->help(__('nova-transaction.id')),
            Money::make('Amount', 'amount')->help(__('nova-transaction.amount')),
            BelongsTo::make('Order', 'order')->help(__('nova-transaction.order')),
        ];
    }

    public function filters(NovaRequest $request): array
    {
        return [
            new \App\Modules\Payment\Nova\Filters\TransactionState(),
            new \App\Modules\Payment\Nova\Filters\TransactionCurrencyFilter(),
        ];
    }

    public function actions(NovaRequest $request): array
    {
        return [
            new \App\Modules\Payment\Nova\Actions\MarkOrderAsPaidAction(),
            new \App\Modules\Payment\Nova\Actions\OpenTransactionOnGatewayDashboardAction(),
        ];
    }
}