Download
Requirements
Laravel Nova has a few requirements you should be aware of before installing:
- Composer
- Laravel Framework 8.0+
- Laravel Mix 6
- Node.js (Version 14)
- NPM
#Browser Support
Nova supports modern versions of the following browsers:
- Apple Safari
- Google Chrome
- Microsoft Edge
- Mozilla Firefox
#Installing Nova Via Composer
Zip Downloads
Previous releases of Laravel Nova allowed Nova to be installed by downloading Zip archives of the source code; however, Nova 4 installation is always performed via Composer.
You may install Nova as a Composer package via our private Satis repository. To get started, add the Nova repository to your application's composer.json
file:
"repositories": [
{
"type": "composer",
"url": "https://nova.laravel.com"
}
],
Or, you may use the following CLI command to add the Composer repository to your composer.json
file:
composer config repositories.nova '{"type": "composer", "url": "https://nova.laravel.com"}' --file composer.json
Next, you may add laravel/nova
to your list of required packages in your composer.json
file:
"require": {
"php": "^8.0",
"laravel/framework": "^9.0",
"laravel/nova": "~4.0"
},
After your composer.json
file has been updated, run the composer update
command in your console terminal:
composer update --prefer-dist
When running composer update
, you will be prompted to provide a username and password. You should use your Nova website email for the username and a license key should be used as the password. These credentials will authenticate your Composer session as having permission to download the Nova source code.
To avoid manually typing these credentials, you may create a Composer auth.json file while using your license key in place of your password:
composer config http-basic.nova.laravel.com your-nova-account-email@your-domain.com your-license-key
Finally, run the nova:install
and migrate
Artisan commands. The nova:install
command will install Nova's service provider and public assets within your application:
php artisan nova:install
php artisan migrate
After running this command, verify that the App\Providers\NovaServiceProvider
was added to the providers
array in your app
configuration file. If it wasn't, you should add it manually. Of course, if your application does not use the App
namespace, you should update the provider class name as needed.
The default App\Nova\User
Nova resource references the App\Models\User
model. If you place your models in a different directory or namespace, you should adjust this value within the resource:
public static $model = 'App\\Models\\User';
If you don't have a Nova admin user yet in your users
table, you can add one by running the nova:user
Artisan command and following the prompts:
php artisan nova:user
That's it! Next, you may navigate to your application's /nova
path in your browser and you should be greeted with the Nova dashboard which includes links to various parts of this documentation.
#Registering a Nova License Key and Production URL
Nova requires a license key a production URL to be used in production environments. Nova will check your license key and the current host against the values from the license details found in your Nova account.
You can generate license keys and register the production URL for your project inside the license's page on your Nova account at https://nova.laravel.com/licenses :
Wildcard subdomains
You can register a wildcard subdomain for your production URL for use in multi-tenant scenarios (e.g. *.laravel.com
).
You can register your license key by setting the license_key
option in your config/nova.php
configuration file:
'license_key' => env('NOVA_LICENSE_KEY', ''),
Since Nova can be used in staging and local development environments, Nova will not check your license key when used on localhost
or these local TLDs specified in IETF RFC 2606 :
.test
.example
.invalid
.localhost
.local
Nova will also not check the current license key when the hostname contains commonly-used staging subdomains:
admin.
staging.
stage.
test.
testing.
dev.
development.
#Verifying Your Nova License Key Configuration
To verify everything has been configured correctly, you should run the following command:
php artisan nova:check-license
#Authenticating Nova in Continuous Integration (CI) Environments
It's not advised to store your Composer auth.json
file inside your project's version control repository. However, there may be times you wish to download Nova inside a CI environment like CodeShip . For instance, you may wish to run tests for any custom tools you create. To authenticate Nova in these situations, you can use Composer to set the configuration option inside your CI system's pipeline, injecting environment variables containing your Nova username and license key:
composer config http-basic.nova.laravel.com ${NOVA_USERNAME} ${NOVA_LICENSE_KEY}
#Authorizing Access To Nova
Within your app/Providers/NovaServiceProvider.php
file, there is a gate
method. This authorization gate controls access to Nova in non-local environments. By default, any user can access the Nova dashboard when the current application environment is local
. You are free to modify this gate as needed to restrict access to your Nova installation:
/**
* Register the Nova gate.
*
* This gate determines who can access Nova in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewNova', function ($user) {
return in_array($user->email, [
'taylor@laravel.com',
]);
});
}
#Customization
#Branding
Although Nova's interface is intended to be an isolated part of your application that is managed by Nova, you can make some small customizations to the branding logo and color used by Nova to make the interface more cohesive with the rest of your application.
#Brand Logo
To customize the logo used at the top left of the Nova interface, you may specify a configuration value for the brand.logo
configuration item within your application's config/nova.php
configuration file. This configuration value should contain an absolute path to the SVG file of the logo you would like to use:
'brand' => [
'logo' => resource_path('/img/example-logo.svg'),
// ...
],
SVG Sizing
You may need to adjust the size and width of your SVG logo by modifying its width in the SVG file itself.
#Brand Color
To customize the color used as the "primary" color within the Nova interface, you may specify a value for the brand.colors
configuration item within your application's config/nova.php
configuration file. This color will be used as the primary button color as well as the color of various emphasized items throughout the Nova interface. This configuration value should be a valid RGB value:
'brand' => [
// ...
'colors' => [
"400" => "24, 182, 155, 0.5",
"500" => "24, 182, 155",
"600" => "24, 182, 155, 0.75",
]
],
#Customizing Nova's footer
There are times you may wish to customize Nova's default footer text to include relevant information for your users, such as your application version, IP addresses, or other information. You can do this by setting Nova's footer within App\Providers\NovaServiceProvider
:
use Laravel\Nova\Nova;
use Illuminate\Support\Facades\Blade;
/**
* Boot any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Nova::footer(function ($request) {
return Blade::render('
@env(\'prod\')
This is production!
@endenv
');
});
}
#Customizing Nova's Authentication Guard
Nova uses the default authentication guard defined in your auth
configuration file. If you would like to customize this guard, you may set the guard
value within Nova's configuration file:
'guard' => env('NOVA_GUARD', null),
#Customizing Nova's Password Reset Functionality
Nova uses the default password reset broker defined in your auth
configuration file. If you would like to customize this broker, you may set the passwords
value within Nova's configuration file:
'passwords' => env('NOVA_PASSWORDS', null),
#Customizing Nova's Storage Disk Driver
Nova uses the default storage disk driver defined in your filesystems
configuration file. If you would like to customize this disk, you may set the storage_disk
value within Nova's configuration file:
'storage_disk' => env('NOVA_STORAGE_DISK', 'public'),
#Customizing Nova's Initial Path
When visiting Nova, the Main
dashboard is typically loaded by default. However, you are free to define a different initial path that should be loaded using Nova's initialPath
method. Typically, this method may be invoked from the register
method of your application's App\Providers\NovaServiceProvider
service provider:
use Laravel\Nova\Nova;
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Nova::initialPath('/resources/users');
// ...
}
#Enabling RTL Support
If you wish to display Nova's content "right-to-left" (RTL), you can enable this behavior by calling the enableRTL
method from your App\Providers\NovaServiceProvider
service provider:
use Laravel\Nova\Nova;
/**
* Boot any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Nova::enableRTL();
}
The enableRTL
method also accept a closure that allows you to enable RTL support for specific users or in other custom scenarios:
use Illuminate\Http\Request;
use Laravel\Nova\Nova;
Nova::enableRTL(fn (Request $request) => $request->user()->wantsRTL());
#Disabling Nova's Theme Switcher
If you wish to completely hide Nova's light/dark mode switcher and instead have Nova honor the system preference only, you can call the withoutThemeSwitcher
method from your App/Providers/NovaServiceProvider
:
use Laravel\Nova\Nova;
/**
* Boot any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Nova::withoutThemeSwitcher();
}
#Error Reporting
Nova uses its own internal exception handler instead of using the default App\Exceptions\ExceptionHandler
. If you need to integrate third-party error reporting tools with your Nova installation, you should use the Nova::report
method. Typically, this method should be invoked from the register
method of your application's App\Providers\NovaServiceProvider
class:
use Laravel\Nova\Nova;
Nova::report(function ($exception) {
if (app()->bound('sentry')) {
app('sentry')->captureException($exception);
}
});
#Updating Nova
To update your Nova installation, you may run the composer update
command:
composer update
#Updating Nova's Assets
After updating to a new Nova release, you should be sure to update Nova's JavaScript and CSS assets using the nova:publish
Artisan command and clear any cached views using the view:clear
Artisan command. This will ensure the newly-updated Nova version is using the latest versions of Nova's assets and views:
php artisan nova:publish
php artisan view:clear
The nova:publish
command will re-publish Nova's public assets, configuration, views, and language files. This command will not overwrite any existing configuration, views, or language files. If you would like the command to overwrite existing files, you may use the --force
flag when executing the command:
php artisan nova:publish --force
#Keeping Nova's Assets Updated
To ensure Nova's assets are updated when a new version is downloaded, you may add a Composer hook inside your project's composer.json
file to automatically publish Nova's latest assets:
"scripts": {
"post-update-cmd": [
"@php artisan nova:publish"
]
}
#Code Distribution
Nova's license does not allow the public distribution of its source code. So, you may not build an application using Nova and distribute that application public via open source repository hosting platforms or any other code distribution platform.
If you would like to develop a third party package that augments Nova's functionality, you are free to do so. However, you may not distribute the Nova source code along with your package.
Responsive Design
Nova 4 introduces a thoroughly refreshed user interface that is now fully responsive, so you can manage your data on the go:
And, by popular demand, the Nova interface now supports "dark mode":
#Painless Branding
A new brand
configuration option has been added to the nova
configuration file, allowing you to easily customize the "primary color" and logo used within the Nova interface without the need to create a custom Nova "theme":
To learn more, check out branding documentation.
#Collapsable Relations
Nova 4 allows you to collapse relations for a given resource type so that they are no longer loaded by default when viewing the relation. Nova will store your collapsed relations in your browser's local storage so that Nova can remember your preferences. We think you will find that collapsable relationships lead to drastic performance improvements when viewing resource types that have dozens of relationships:
#Action Callbacks
The new Action::then
method allows you to easily execute code after an action has finished executing against all of its selected resources. This feature allows you to easily generate reports or send notifications when an action has finished executing:
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [
(new Actions\EmailAccountProfile)->then(function ($models) {
$models->each(function ($model) {
//
});
}),
];
}
#Batchable Actions
Actions may now take advantage of Laravel's job batching functionality, offering the ability to register batch callbacks that perform additional tasks once the entire batch of actions has finished executing:
/**
* Prepare the given batch for execution.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Bus\PendingBatch $batch
* @return void
*/
public function withBatch(ActionFields $fields, PendingBatch $batch)
{
$batch->then(function (Batch $batch) {
// All jobs completed successfully...
$selectedModels = $batch->resourceIds;
})->catch(function (Batch $batch, Throwable $e) {
// First batch job failure detected...
})->finally(function (Batch $batch) {
// The batch has finished executing...
});
}
To learn more about making actions batchable, please consult the action batching documentation.
#New Fields
Nova 4 introduces several new field types, including Color
, UiAvatar
, MultiSelect
, and URL
. For more information on using these fields, please consult the field documentation.
#Dependent Fields
Nova 4 also introduces support for "dependent" fields. The new dependsOn
method allows you to specify that a field's configuration depends on one or more other field's values. The dependsOn
method accepts an array
of dependent field attributes and a closure that modifies the configuration of the current field instance.
Dependent fields allow advanced customization, such as toggling read-only mode, validation rules, and more based on the state of another field:
use Laravel\Nova\Fields\FormData;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
Select::make('Purchase Type', 'type')
->options([
'personal' => 'Personal',
'gift' => 'Gift',
]),
// Recipient field configuration is customized based on purchase type...
Text::make('Recipient')
->readonly()
->dependsOn(
['type'],
function (Text $field, NovaRequest $request, FormData $formData) {
if ($formData->type === 'gift') {
$field->readonly(false)->rules(['required', 'email']);
}
}
),
#Filterable Fields
Nova 4 introduces a new filterable
method that allows you to enable convenient, automatic filtering functionality for a given field on resources, relationships, and lenses without creating a custom filter. The Nova generated filter will automatically be made available via the resource filter menu on the resource's index:
DateTime::make('Created At')->filterable(),
#Notifications
Nova 4 also introduces support for "Nova notifications", a brand new notification menu within Nova that you may use to display information for your Nova users. For example, you could use this menu to notify users that a report has been generated or that an invoice needs attention:
To send a Nova notification, you simply need to pass a NovaNotification
instance to a notifiable user:
use Laravel\Nova\Notifications\NovaNotification;
$request->user()->notify(
NovaNotification::make()
->message('Your report is ready to download.')
->action('Download', 'https://example.com/report.pdf')
->icon('download')
->type('info')
);
To learn more about Nova notifications, please consult the comprehensive notification documentation.
#Impersonation
After deploying your application to production, you may occasionally need to "impersonate" another user of your application in order to debug problems your customers are reporting. Thankfully, Nova now includes built-in functionality to handle this exact scenario.
To enable user impersonation, add the Laravel\Nova\Auth\Impersonatable
trait to your application's User
model:
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Impersonatable, Notifiable;
// ...
}
Once the Impersonatable
trait has been added to your application's User
model, an "Impersonate" action will be available via the inline action menu for the corresponding resource:
#Custom Menus
In addition, Nova now supports totally custom side-bar and user menus. In fact, you can fully customize every link entry in Nova's left-side navigation bar, including the creation of menu sections, menu groups, and more:
To learn more about building your own custom menus, please consult the custom menu documentation.
#Progress Metric
The new "progress" metric allows you to track your progress towards a given goal. For example, you may wish to track your new user registrations for the month against a target goal. It's a cinch with the new progress metric:
To learn more about building and defining metrics, please consult the metric documentation.
#Resource Replication
Sometimes, you may want to create a new resource while using all of the data from an existing resource as a starting point. Nova's new resource replication feature does just that. After clicking the replicate button, you'll be whisked away to a resource creation form with all of the replicated resource's data hydrated into the form and ready for tweaking:
#Resource Preview Modal
The brand new resource preview modal gives you a sneak peek at the resource's data without leaving the resource index page, providing a great way to learn more about a resource without interrupting your flow:
To learn more about adding fields to the resource preview modal, check out the field documentation.
#HasOne::ofMany Support
The HasOne
relationship field can now be transformed into an "has one of many" Eloquent relationship using the ofMany
method. For example, let's assume a User
model hasMany
Post
models. We may add the "has one of many" relationship to our User
Nova resource like so:
use App\Nova\Post;
use Laravel\Nova\Fields\HasOne;
HasOne::ofMany('Latest Post', 'latestPost', Post::class),
Of course, "morph one of many" relationships are also supported:
use App\Nova\Comment;
use Laravel\Nova\Fields\MorphOne;
MorphOne::ofMany('Latest Comment', 'latestComment', Comment::class),
#Inline HasOne Creation
Resources that have HasOne
relationships may now create the data for those relationships directly in the parent resource's creation form. This new ability makes it a breeze to create the parent resource and its child in one, convenient operation:
#Search Improvements
Typically, Nova searches your database columns using simple LIKE
clauses. However, if you are using MySQL or Postgres, you may now take advantage of any full-text indexes you have defined:
use Laravel\Nova\Query\Search\SearchableText;
/**
* Get the searchable columns for the resource.
*
* @return array
*/
public static function searchableColumns()
{
return ['id', new SearchableText('title')];
}
In addition, Nova 4 even allows you to search within a resource's relationships and JSON columns:
use Laravel\Nova\Query\Search\SearchableJson;
use Laravel\Nova\Query\Search\SearchableRelation;
public static function searchableColumns()
{
return [
'id',
new SearchableRelation('author', 'name')
new SearchableJson('meta->tag')
];
}
To learn more about Nova's search features, please consult the comprehensive search documentation.
Dependency Upgrades
Nova's upstream dependencies have been upgraded. You will find a complete list of our dependency upgrades below:
#Server
- PHP 7.3+
- Laravel Framework 8.0+
- Updated
doctrine/dbal
from^2.9
to^2.13.3|^3.1.2
- Updated
laravel/ui
from^2.0|^3.0
to^3.3
- Updated
symfony/*
from^5.0
to^5.4|^6.0
- Removed
cakephp/chronos
andmoontoast/math
dependencies
#Client
- Updated supported Laravel Mix version from
v1
tov6
- Removed
flatpickr
andmoment.js
#Updating Composer Dependencies
You should update your laravel/nova
dependency to ^4.0
in your application's composer.json
file:
"laravel/nova": "^4.0",
After purchasing a Nova 4.0 license, you should update your Composer auth.json file to use your newly generated license key instead of your previous Nova 3 API token or account password.
Next, install your updated your Composer dependencies:
composer update mirrors
composer update
After updating your application's Composer dependencies, you should migrate your database:
php artisan migrate
#Updating Configuration, Assets, and Translations
Next, you should update your application's Nova configuration, assets, and translation files. To get started, you may run the following commands to update your assets and translations.
You may wish to store a copy of your current translation file before running this command so you can easily port any custom translations back into the new file after running these commands. In addition, we will generate a "Main" dashboard for your Nova installation:
php artisan nova:dashboard Main
php -r "file_exists('./resources/views/vendor/nova/layout.blade.php') && unlink('./resources/views/vendor/nova/layout.blade.php');"
php artisan vendor:publish --tag=nova-assets --force
php artisan vendor:publish --tag=nova-lang --force
php artisan view:clear
Next, let's update the Nova configuration file. First, ensure that the middleware
and api_middleware
configuration options within your application's nova
configuration file appear as follows:
use Laravel\Nova\Http\Middleware\Authenticate;
use Laravel\Nova\Http\Middleware\Authorize;
use Laravel\Nova\Http\Middleware\BootTools;
use Laravel\Nova\Http\Middleware\DispatchServingNovaEvent;
use Laravel\Nova\Http\Middleware\HandleInertiaRequests;
return [
// ...
'middleware' => [
'web',
HandleInertiaRequests::class,
DispatchServingNovaEvent::class,
BootTools::class,
],
'api_middleware' => [
'nova',
Authenticate::class,
Authorize::class,
],
// ...
];
Next, ensure your application's nova
configuration file contains a storage_disk
configuration option:
'storage_disk' => env('NOVA_STORAGE_DISK', 'public'),
Once your configuration has been updated, you should review the following list of changes and upgrade your application accordingly.
#Registering a Nova license key and production URL
Nova requires a license key a production URL to be used in production environments. Nova will check your license key and the current host against the values from the license details found in your Nova account. You can learn more in the installation docs.
#Updating Third-Party Nova Packages
If your application relies on Nova tools or packages developed by third-parties, it is possible that these packages are not yet compatible with Nova 4.0 and will require an update from their maintainers.
#High Impact Changes
#Nova Request
Nova 4 updates a variety of methods to accept a Laravel\Nova\Http\Requests\NovaRequest
instance instead of an Illuminate\Http\Request
instance. An overview of the methods that have been updated is provided below so you may update your method signatures accordingly.
#Resources
The fields
, fieldsForIndex
, fieldsForDetail
, fieldsForCreate
, fieldsForUpdate
, cards
, filters
, lenses
, and actions
methods:
class Resource
{
public function fields(NovaRequest $request) {}
public function fieldsForIndex(NovaRequest $request) {}
public function fieldsForDetail(NovaRequest $request) {}
public function fieldsForCreate(NovaRequest $request) {}
public function fieldsForUpdate(NovaRequest $request) {}
public function cards(NovaRequest $request) {}
public function filters(NovaRequest $request) {}
public function lenses(NovaRequest $request) {}
public function actions(NovaRequest $request) {}
}
#Lenses
The fields
, filters
, and actions
methods:
class Lens
{
public function fields(NovaRequest $request) {}
public function filters(NovaRequest $request) {}
public function actions(NovaRequest $request) {}
}
#Actions
The fields
method:
class Action
{
public function fields(NovaRequest $request) {}
}
#Filters
The apply
and options
methods:
class Filter
{
public function apply(NovaRequest $request, $query, $value) {}
public function options(NovaRequest $request) {}
}
#Main Dashboard Class
In previous releases of Nova, the "Main" dashboard cards were defined via the cards
method of your application's NovaServiceProvider
. However, in Nova 4, a dedicated Main
dashboard class must be created via the following command:
php artisan nova:dashboard Main
Next, move the contents of the cards
method from your NovaServiceProvider
to the cards
method of your new App\Nova\Dashboards\Main
class and register the Main
dashboard within the dashboards
method of your NovaServiceProvider
:
use App\Nova\Dashboards\Main;
/**
* Get the extra dashboards that should be displayed on the Nova dashboard.
*
* @return array
*/
protected function dashboards()
{
return [
new Main,
];
}
#Dashboard Methods
In Nova 4, the cards
and uriKey
methods defined on dashboard classes are no longer static. You should update your methods accordingly:
/**
* Get the displayable name of the dashboard.
*
* @return string
*/
public function label()
{
return 'Post Stats';
}
/**
* Get the URI key for the dashboard.
*
* @return string
*/
public function uriKey()
{
return 'posts-dashboard';
}
#Client-Side Timezone Detection
Nova 4 removes the ability to rely on the client machine timezone in order to display timezone related information. Instead, Nova 4 utilizes the application's "server side" timezone as defined by the timezone option in your application's app
configuration file.
Please refer to our documentation regarding timezone customization for more information.
#Date
/ DateTime
Fields & HTML5
Nova 4 utilizes native <input type="date" />
and <input type="datetime-local" />
elements to render the Date
and DateTime
fields. Therefore, the following methods have been removed from Nova 4:
firstDayOfWeek()
format()
pickerFormat()
pickerDisplayFormat()
incrementPickerHourBy()
incrementPickerMinuteBy()
#Algolia Place Field
Unfortunately, Algolia is retiring their "Places" API on May 31, 2022; therefore, the Place
field has been deprecated and we encourage you to migrate to Text
fields for street addresses and cities.
#Medium Impact Changes
#Updating Custom Tool, Cards, Fields, Filters
Prerequisites
To ease upgrading custom packages to Nova 4, please review and copy the following files from Laravel Nova's src/Console/stubs
directory to your own custom package:
nova.mix.js
packages.json
webpack.mix.js
Since Nova 4 upgrades our frontend dependencies to Inertia, Vue 3, and Tailwind 2, it is necessary to review all custom tools and upgrade them accordingly. A general overview of the necessary changes can be found below; however, your custom Nova packages may require additional changes if they are depending on third-party packages that only support Vue 2 or prior versions of Tailwind.
#Vue 3
This change primarily affects the installation of custom tools that utilize Vue routing.
Nova 4 has been updated to use Vue 3, in order to upgrade all custom cards, custom fields, custom filters, resource tools, and tools to support Vue 3, please make the following changes to your application's webpack.mix.js
:
// Before...
mix.js("resources/js/field.js", "js");
// After...
require("./nova.mix");
mix
.js("resources/js/field.js", "js")
.vue({ version: 3 })
.nova("vendor/package");
#Replacing Vue Router With Inertia.js
This change primarily affects the installation of custom tools that utilize Vue routing.
Nova 4 has replaced Vue router with Inertia.js . Therefore, custom tools should migrate from registering Vue routes to registering Inertia.js page components and backend routes. For example, given the following Nova 3 Vue router registration:
// Within tool.js...
Nova.booting((Vue, router) => {
router.addRoutes([
{
name: "sidebar-tool",
path: "/sidebar-tool",
component: require("./components/Tool"),
},
]);
});
When using Nova 4, you should register the tool component with Inertia like so:
// Within tool.js...
Nova.booting((Vue) => {
Nova.inertia("SidebarTool", require("./component/Tool").default);
});
Once your Vue component has been registered, you should define a server-side route definition for your tool so that it may be rendered:
// Within ToolServiceProvider.php...
use Laravel\Nova\Nova;
Nova::router()
->group(function ($router) {
$router->get('sidebar-tool', function ($request) {
return inertia('SidebarTool');
});
});
#Removal Of laravel-nova
NPM Dependency
This change primarily affects the installation of custom tools that utilize Vue routing.
Prerequisite
To ease upgrading custom packages to support Nova 4, please review and copy the following files from Laravel Nova's src/Console/stubs
especially the following files:
nova.mix.js
packages.json
webpack.mix.js
Previous versions of Nova required the laravel-nova
NPM package. In 4.0, this is no longer the case as each mixin has been integrated into Nova itself. To upgrade any custom packages you've created, you must update your webpack.mix.js
file to define an alias to vendor/laravel/nova/resources/js/mixins/packages.js
:
Typically, custom Nova tools, resource tools, cards, and other custom packages that are being developed within a nova-components
directory of a Laravel application can reference Nova's own packages.js
file by defining a laravel-nova
alias that points to the file within the Nova installation that is located within your root application's vendor
directory. This alias is typically defined within the custom package's nova.mix.js
file:
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
Custom Nova packages that are developed outside of a nova-components
directory should declare laravel/nova
as a "dev" Composer dependency, and then define a laravel-nova
Mix alias that points to the packages.js
file within your custom package's vendor
directory:
'laravel-nova': path.join(
__dirname,
'vendor/laravel/nova/resources/js/mixins/packages.js'
),
In order to compile custom packages assets with laravel-nova
mixins you are required to prepare laravel/nova
's node_modules
by running the following command:
npm run nova:install
# Or use the explicit command...
npm --prefix='vendor/laravel/nova' ci
#Event Cancellation On Save
Nova 3 ignores event cancellation when creating or updating a resource. For example, the following code will still persist the User
resource to the database, even though the even listener returns false
:
User::updating(function ($model) {
return false;
});
However, this code will throw a Laravel\Nova\Exceptions\ResourceSaveCancelledException
exception in Nova 4.
#Field::default
Method Only Applies To Create, Attach, & Action Requests
Nova 4 will no longer resolve default values for "index" and "detail" requests. If you need to define a model's default attribute values, please utilize Eloquent's $attributes
property:
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The model's attributes.
*
* @var array
*/
protected $attributes = [
'timezone' => 'UTC',
];
}
#Relationship Name Conventions
Given the following field definition, Nova 3 will assume the relationship method is named purchased_books
; however, Nova 4 will correctly assume the relationship method is named purchasedBooks
.
BelongsToMany::make('Purchased Books'),
#Action::actionClass
Method Removed
Nova 4 no longer allows adding custom CSS styles to an action confirmation modal's buttons via the action's actionClass
method.
#Low Impact Changes
#Eloquent User Provider Required
Nova 4 requires the authentication user provider to be eloquent
in order to resolve information regarding the currently authenticated user.
In your application's default config/auth.php
configuration file, the Eloquent user provider is specified and it is instructed to use the App\Models\User
model when retrieving users. You may change these values within your configuration file based on the needs of your application.
#Reduce Encoded Filter String Length
Nova 4 introduces a shorter key-value map in filter string URLs which reduces the overall length of the URL. This change doesn't affect bookmarked URLs; however, third party package tool developers who interact deeply with Vuex may wish to ensure their packages still work after this change.
#Action::showOnTableRow
Method
The Action::showOnTableRow
method has been deprecated. Instead, we suggest updating your code to use the showInline
method:
// Before...
(new ConsolidateTransaction)->showOnTableRow(),
// After...
(new ConsolidateTransaction)->showInline(),
(new ConsolidateTransaction)->onlyInline(),
(new ConsolidateTransaction)->exceptInline(),
#Authorization Precedence
Nova 4 introduce the following tweaks to authorization order / precedence:
- Authorizing if a user can
view
a resource no longer depends on theviewAny
permission. - Actions can be executed regardless of
view
andviewAny
permissions. - Destructive actions will now authorize via their own
canRun
method before falling back to the model's policy.
Further detail regarding Nova authorization is available within the resource policy documentation and action authorization documentation.
#Update Published Stubs
Due to various changes in Nova 4.0, you should re-publish the Nova "stubs" if you have previously published them. You can accomplish this by executing the nova:stubs
Artisan command with the --force
option:
php artisan nova:stubs --force
Bug Reports
If you discover a bug in Laravel Nova, please open an issue on the Nova issues GitHub repository .
#Support Questions
Laravel Nova's GitHub issue trackers are not intended to provide Nova help or support. Instead, use one of the following channels:
#Security Vulnerabilities
If you discover a security vulnerability within Laravel Nova, please send an email to nova@laravel.com
. All security vulnerabilities will be promptly addressed.
Code of Conduct
The Laravel code of conduct is derived from the Ruby code of conduct. Any violations of the code of conduct may be reported to Taylor Otwell (taylor@laravel.com):
- Participants will be tolerant of opposing views.
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
- When interpreting the words and actions of others, participants should always assume good intentions.
- Behavior which can be reasonably considered harassment will not be tolerated.
Introduction
Laravel Nova is a beautiful administration dashboard for Laravel applications. Of course, the primary feature of Nova is the ability to administer your underlying database records using Eloquent. Nova accomplishes this by allowing you to define a Nova "resource" that corresponds to each Eloquent model in your application.
#Defining Resources
By default, Nova resources are stored in the app/Nova
directory of your application. You may generate a new resource using the nova:resource
Artisan command:
php artisan nova:resource Post
The most basic and fundamental property of a resource is its model
property. This property tells Nova which Eloquent model the resource corresponds to:
/**
* The model the resource corresponds to.
*
* @var string
*/
public static $model = 'App\Models\Post';
Freshly created Nova resources only contain an ID
field definition. Don't worry, we'll add more fields to our resource soon.
Reserved Resource Names
Nova contains a few reserved words which may not be used for resource names:
- Card
- Dashboard
- Field
- Impersonate
- Metric
- Resource
- Search
- Script
- Style
- Tool
#Registering Resources
Automatic Registration
By default, all resources within the app/Nova
directory will automatically be registered with Nova. You are not required to manually register them.
Before resources are available within your Nova dashboard, they must first be registered with Nova. Resources are typically registered in your application's app/Providers/NovaServiceProvider.php
file. This file contains various configuration and bootstrapping code related to your Nova installation.
As mentioned above, you are not required to manually register your resources; however, if you choose to do so, you may do so by overriding the resources
method of your NovaServiceProvider
.
There are two approaches to manually registering resources. You may use the resourcesIn
method to instruct Nova to register all Nova resources within a given directory. Alternatively, you may use the resources
method to manually register individual resources:
use App\Nova\User;
use App\Nova\Post;
/**
* Register the application's Nova resources.
*
* @return void
*/
protected function resources()
{
Nova::resourcesIn(app_path('Nova'));
Nova::resources([
User::class,
Post::class,
]);
}
Once your resources are registered with Nova, they will be available in the Nova sidebar:
If you do not want a resource to appear in the sidebar, you may override the displayInNavigation
property of your resource class:
/**
* Indicates if the resource should be displayed in the sidebar.
*
* @var bool
*/
public static $displayInNavigation = false;
#Customizing Resource Menus
You can customize the resource's menu by defining a menu
method on your resource class:
use Illuminate\Http\Request;
/**
* Get the menu that should represent the resource.
*
* @return \Laravel\Nova\Menu\MenuItem
*/
public function menu(Request $request)
{
return parent::menu($request)->withBadge(function () {
return static::$model::count();
});
}
Please refer to the documentation on menu customization for more information.
#Grouping Resources
If you would like to separate resources into different sidebar groups, you may override the group
property of your resource class:
/**
* The logical group associated with the resource.
*
* @var string
*/
public static $group = 'Admin';
#Resource Table Style Customization
Nova supports a few visual customization options for your resources.
#Table Styles
Sometimes it's convenient to show more data on your resource index tables. To accomplish this, you can use the "tight" table style option designed to increase the visual density of your table rows. To accomplish this, override the static $tableStyle
property or the static tableStyle
method on your resource class:
/**
* The visual style used for the table. Available options are 'tight' and 'default'.
*
* @var string
*/
public static $tableStyle = 'tight';
This will display your table rows with less visual height, enabling more data to be shown:
#Column Borders
You can instruct Nova to display column borders by overriding the static $showColumnBorders
property or the static showColumnBorders
method on your resource class:
/**
* Whether to show borders for each column on the X-axis.
*
* @var bool
*/
public static $showColumnBorders = true;
Setting this property to true
will instruct Nova to display the table with borders on every table item:
#Eager Loading
If you routinely need to access a resource's relationships within your fields, resource title, or resource subtitle, it may be a good idea to add the relationship to the with
property of your resource. This property instructs Nova to always eager load the listed relationships when retrieving the resource.
For example, if you access a Post
resource's user
relationship within the Post
resource's subtitle
method, you should add the user
relationship to the Post
resource's with
property:
/**
* The relationships that should be eager loaded on index queries.
*
* @var array
*/
public static $with = ['user'];
#Resource Replication
Sometimes, you may want to create a new resource while using all of the data from an existing resource as a starting point. Nova's resource replication feature does just that. After clicking the replicate button, you'll be whisked away to a resource creation form with all of the replicated resource's data hydrated into the form and ready for tweaking:
To customize the replication model, you can override the replicate
method on the resource class:
/**
* Return a replicated resource.
*
* @return static
*
* @throws \InvalidArgumentException
*/
public function replicate()
{
return tap(parent::replicate(), function ($resource) {
$model = $resource->model();
$model->name = 'Duplicate of '.$model->name;
});
}
#Resource Events
All Nova operations use the typical save
, delete
, forceDelete
, restore
Eloquent methods you are familiar with. Therefore, it is easy to listen for model events triggered by Nova and react to them. The easiest approach is to simply attach a Laravel model observer to a model:
namespace App\Providers;
use App\Models\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
}
If you would like to attach an observer whose methods are invoked only during Nova related HTTP requests, you may register observers using the make
method provided by the Laravel\Nova\Observable
class. Typically, this should be done within your application's NovaServiceProvider
:
use App\Models\User;
use Laravel\Nova\Observable;
use App\Observers\UserObserver;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Observable::make(User::class, UserObserver::class);
}
Alternatively, you can determine if the current HTTP request is serving a Nova related request within the Observer
itself using Nova's whenServing
method:
namespace App\Observers;
use App\Models\User;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Nova;
class UserObserver
{
/**
* Handle the User "created" event.
*
* @param \App\Models\User $user
* @return void
*/
public function created(User $user)
{
Nova::whenServing(function (NovaRequest $request) use ($user) {
// Only invoked during Nova requests...
});
// Always invoked...
}
}
#Resource Hooks
Nova also allows you to define the following static methods on a resource to serve as hooks that are only invoked when the corresponding resource action is executed from within Laravel Nova:
afterCreate
afterUpdate
afterDelete
afterForceDelete
For example, you may want to send an email verification notification after a user has been created within Nova:
use App\Models\User;
use App\Nova\Resource;
use Illuminate\Database\Eloquent\Model;
use Laravel\Nova\Http\Requests\NovaRequest;
class User extends Resource
{
/**
* Register a callback to be called after the resource is created.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function afterCreate(NovaRequest $request, Model $model)
{
$model->sendEmailVerificationNotification();
}
}
#Preventing Conflicts
If a model has been updated since it was last retrieved by Nova, Nova will automatically respond with a 409 Conflict
HTTP status code and display an error message to prevent unintentional model changes. This may occur if another user updates the model after you have opened the "Edit" page on the resource. This feature is also known as the Nova "Traffic Cop".
#Disabling Traffic Cop
If you are not concerned with preventing conflicts, you can disable the Traffic Cop feature by setting the trafficCop
property to false
on a given resource class:
/**
* Indicates whether Nova should check for modifications between viewing and updating a resource.
*
* @var bool
*/
public static $trafficCop = false;
You may also override the trafficCop
method on the resource if you have more intense customization needs in order to determine if this feature should be enabled:
/**
* Indicates whether Nova should check for modifications between viewing and updating a resource.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
public static function trafficCop(Request $request)
{
return static::$trafficCop;
}
Time Synchronization
If you are experiencing issues with traffic cop you should ensure that your system time is correctly synchronized using NTP.
#Resource Polling
Nova can automatically fetch the latest records for a resource at a specified interval. To enable polling, override the polling
property of your Resource class:
/**
* Indicates whether the resource should automatically poll for new resources.
*
* @var bool
*/
public static $polling = true;
To customize the polling interval, you may override the pollingInterval
property on your resource class with the number of seconds Nova should wait before fetching new resource records:
/**
* The interval at which Nova should poll for new resources.
*
* @var int
*/
public static $pollingInterval = 5;
#Toggling Resource Polling
By default, when resource polling is enabled, there is no way to disable it once the page loads. You can instruct Nova to display a start / stop toggle button for resource polling by setting the showPollingToggle
property on your resource class to true
:
/**
* Indicates whether to show the polling toggle button inside Nova.
*
* @var bool
*/
public static $showPollingToggle = true;
Nova will then display a clickable button that you may use to enable / disable polling for the resource:
#Redirection
Nova allows you to easily customize where a user is redirected after performing resource actions such as creating or updating a resource:
Behind the scenes, Nova's redirect features use Inertia.js's visit
method. Because of this, redirection is limited to paths within Laravel Nova. You may invoke the URL::remote
method to redirect to an external URL:
use Laravel\Nova\URL;
return URL::remote('https://nova.laravel.com');
#After Creating Redirection
You may customize where a user is redirected after creating a resource using by overriding your resource's redirectAfterCreate
method:
/**
* Return the location to redirect the user after creation.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Nova\Resource $resource
* @return \Laravel\Nova\URL|string
*/
public static function redirectAfterCreate(NovaRequest $request, $resource)
{
return '/resources/'.static::uriKey().'/'.$resource->getKey();
}
#After Updating Redirection
You may customize where a user is redirected after updating a resource using by overriding your resource's redirectAfterUpdate
method:
/**
* Return the location to redirect the user after update.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Nova\Resource $resource
* @return \Laravel\Nova\URL|string
*/
public static function redirectAfterUpdate(NovaRequest $request, $resource)
{
return '/resources/'.static::uriKey().'/'.$resource->getKey();
}
#After Deletion Redirection
You may customize where a user is redirected after deleting a resource using by overriding your resource's redirectAfterDelete
method:
/**
* Return the location to redirect the user after deletion.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return \Laravel\Nova\URL|string|null
*/
public static function redirectAfterDelete(NovaRequest $request)
{
return null;
}
#Pagination
Nova has the ability to show pagination links for your Resource listings. You can choose between three different styles: "simple", "load-more", and "links", depending on your application's needs:
By default, Nova Resources are displayed using the "simple" style. However, you may customize this to use either the load-more
or links
styles by changing the value of the pagination
configuration option within your application's config/nova.php
configuration file:
'pagination' => 'links',
#Customizing Pagination
If you would like to customize the selectable maximum result amounts shown on each resource's "per page" filter menu, you can do so by customizing the resource's perPageOptions
property:
/**
* The pagination per-page options configured for this resource.
*
* @return array
*/
public static $perPageOptions = [50, 100, 150];
Alternatively, you can override the perPageOptions
method on your application's base Resource
class, which is created when you install Nova:
/**
* The pagination per-page options configured for this resource.
*
* @return array
*/
public static function perPageOptions()
{
return [50, 100, 150];
}
Customizing perPageOptions
& Resource Fetching
Changing the value of perPageOptions
on your Resource
will cause Nova to fetch the number of resources equal to the first value in the perPageOptions
array.
#CSV Export
Occasionally you may need to export a group of resource records as a CSV file so that you can interact with the data in a spreadsheet application or import the data into another system. Thankfully, Nova includes built-in support for exporting resource data.
To get started, add the Laravel\Nova\Actions\ExportAsCsv
action to your Nova resource:
use Laravel\Nova\Actions\ExportAsCsv;
use Laravel\Nova\Http\Requests\NovaRequest;
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [
ExportAsCsv::make(),
];
}
If you would like to allow the user to name the CSV file that is downloaded, you may invoke the nameable
method when registering the action:
return [
ExportAsCsv::make()->nameable(),
];
If you would like to customize and format the fields that are included in the generated CSV, you may invoke the withFormat
method when registering the action:
return [
ExportAsCsv::make()->withFormat(function ($model) {
return [
'ID' => $model->getKey(),
'Name' => $model->name,
'Email Address' => $model->email,
];
}),
];
#Resource Index Search Debounce
You may wish to customize the search debounce timing of an individual resource's index listing. For example, the queries executed to retrieve some resources may take longer than others. You can customize an individual resource's search debounce by setting the debounce
property on the resource class:
/**
* The debounce amount (in seconds) to use when searching this resource.
*
* @var float
*/
public static $debounce = 0.5; // 0.5 seconds
#Keyboard Shortcuts
You may press the C
key on a resource index to navigate to the "Create Resource" page. On the resource detail page, the E
key may be used to navigate to the "Update Resource" page.
Defining Fields
Each Nova resource contains a fields
method. This method returns an array of fields, which generally extend the Laravel\Nova\Fields\Field
class. Nova ships with a variety of fields out of the box, including fields for text inputs, booleans, dates, file uploads, Markdown, and more.
To add a field to a resource, you may simply add it to the resource's fields
method. Typically, fields may be created using their static make
method. This method accepts several arguments; however, you usually only need to pass the "human readable" name of the field. Nova will automatically "snake case" this string to determine the underlying database column:
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Text::make('Name')->sortable(),
];
}
#Field Column Conventions
As noted above, Nova will "snake case" the displayable name of the field to determine the underlying database column. However, if necessary, you may pass the column name as the second argument to the field's make
method:
Text::make('Name', 'name_column'),
#Showing / Hiding Fields
Often, you will only want to display a field in certain situations. For example, there is typically no need to show a Password
field on a resource index listing. Likewise, you may wish to only display a Created At
field on the creation / update forms. Nova makes it a breeze to hide / show fields on certain pages.
The following methods may be used to show / hide fields based on the display context:
showOnIndex
showOnDetail
showOnCreating
showOnUpdating
showOnPreview
hideFromIndex
hideFromDetail
hideWhenCreating
hideWhenUpdating
onlyOnIndex
onlyOnDetail
onlyOnForms
exceptOnForms
You may chain any of these methods onto your field's definition in order to instruct Nova where the field should be displayed:
Text::make('Name')->hideFromIndex(),
Alternatively, you may pass a callback to the following methods.
showOnIndex
showOnDetail
showOnCreating
showOnUpdating
hideFromIndex
hideFromDetail
hideWhenCreating
hideWhenUpdating
For show*
methods, the field will be displayed if the given callback returns true
:
Text::make('Name')->showOnIndex(function () {
return $this->name === 'Taylor Otwell';
}),
For hide*
methods, the field will be hidden if the given callback returns true
:
Text::make('Name')->hideFromIndex(function () {
return $this->name === 'Taylor Otwell';
}),
#Resource Preview Modal
You may also define which fields should be included in the resource's "preview" modal. This modal can be displayed for a given resource by the user when viewing the resource's index:
Text::make('Title')->showOnPreview(),
Markdown::make('Content')->showOnPreview(),
#Dynamic Field Methods
If your application requires it, you may specify a separate list of fields for specific display contexts. For example, imagine you have a resource with the following list of fields:
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
Text::make('First Name'),
Text::make('Last Name'),
Text::make('Job Title'),
];
}
On your detail page, you may wish to show a combined name via a computed field, followed by the job title. In order to do this, you could add a fieldsForDetail
method to the resource class which returns a separate list of fields that should only be displayed on the resource's detail page:
/**
* Get the fields displayed by the resource on detail page.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fieldsForDetail(NovaRequest $request)
{
return [
Text::make('Name', function () {
return sprintf('%s %s', $this->first_name, $this->last_name);
}),
Text::make('Job Title'),
];
}
The available methods that may be defined for individual display contexts are:
fieldsForIndex
fieldsForDetail
fieldsForInlineCreate
fieldsForCreate
fieldsForUpdate
Dynamic Field Methods Precedence ::
The fieldsForIndex
, fieldsForDetail
, fieldsForInlineCreate
, fieldsForCreate
, and fieldsForUpdate
methods always take precedence over the fields
method.
#Default Values
There are times you may wish to provide a default value to your fields. Nova offers this functionality via the default
method, which accepts a value or callback. This value will be used as the field's default input value on the resource's creation view:
BelongsTo::make('Name')->default($request->user()->getKey()),
Text::make('Uuid')->default(function ($request) {
return Str::orderedUuid();
}),
#Field Placeholder Text
By default, the placeholder text of a field will be it's name. You can override the placeholder text of a field that supports placeholders by using the placeholder
method:
Text::make('Name')->placeholder('My New Post'),
#Field Hydration
On every create or update request that Nova receives for a given resource, each field's corresponding model attribute will automatically be filled before the model is persisted to the database. If necessary, you may customize the hydration behavior of a given field using the fillUsing
method:
Text::make('Name', 'name')
->fillUsing(function ($request, $model, $attribute, $requestAttribute) {
$model->{$attribute} = Str::title($request->input($attribute));
}),
#Field Panels
If your resource contains many fields, your resource "detail" page can become crowded. For that reason, you may choose to break up groups of fields into their own "panels":
You may accomplish this by creating a new Panel
instance within the fields
method of a resource. Each panel requires a name and an array of fields that belong to that panel:
use Laravel\Nova\Panel;
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
new Panel('Address Information', $this->addressFields()),
];
}
/**
* Get the address fields for the resource.
*
* @return array
*/
protected function addressFields()
{
return [
Text::make('Address', 'address_line_1')->hideFromIndex(),
Text::make('Address Line 2')->hideFromIndex(),
Text::make('City')->hideFromIndex(),
Text::make('State')->hideFromIndex(),
Text::make('Postal Code')->hideFromIndex(),
Country::make('Country')->hideFromIndex(),
];
}
You may limit the amount of fields shown in a panel by using the limit
method:
(new Panel('Profile', [
Text::make('Full Name'),
Date::make('Date of Birth'),
Text::make('Place of Birth'),
]))->limit(1),
Panels with a defined field limit will display a Show All Fields button in order to allow the user to view all of the defined fields when needed.
#Sortable Fields
When attaching a field to a resource, you may use the sortable
method to indicate that the resource index may be sorted by the given field:
Text::make('Name', 'name_column')->sortable(),
#Field Types
Relationship Fields
This portion of the documentation only discusses non-relationship fields. To learn more about relationship fields, check out their documentation.
Nova ships with a variety of field types. So, let's explore all of the available types and their options:
- Avatar
- Badge
- Boolean
- Boolean Group
- Code
- Color
- Country
- Currency
- Date
- DateTime
- File
- Gravatar
- Heading
- ID
- Image
- KeyValue
- Markdown
- MultiSelect
- Number
- Password
- Select
- Sparkline
- Status
- Stack
- Text
- Textarea
- Timezone
- Trix
- UI-Avatar
- URL
- Vapor File
- Vapor Image
#Avatar Field
The Avatar
field extends the Image field and accepts the same options and configuration:
use Laravel\Nova\Fields\Avatar;
Avatar::make('Avatar'),
If a resource contains an Avatar
field, that field will be displayed next to the resource's title when the resource is displayed in search results:
You may use the squared
method to display the image's thumbnail with squared edges. Additionally, you may use the rounded
method to display its thumbnails with fully-rounded edges:
Avatar::make('Avatar')->squared(),
#Badge Field
The Badge
field can be used to display the status of a Resource
in the index and detail views:
use Laravel\Nova\Fields\Badge;
Badge::make('Status', function () {
return User::statuses[$this->status];
}),
By default, the Badge
field supports four variations: info
, success
, danger
, and warning
. You may define your possible field values and their associated badge types using the map
method:
Badge::make('Status')->map([
'draft' => 'danger',
'published' => 'success',
]),
Alternatively, you may use the types
method to completely replace the built-in badge types and their associated CSS classes. The CSS classes may be provided as a string or an array:
Badge::make('Status')->types([
'draft' => 'font-medium text-gray-600',
'published' => ['font-bold', 'text-green-600'],
]),
If you only wish to supplement the built-in badge types instead of overwriting all of them, you may use the addTypes
method:
Badge::make('Status')->addTypes([
'draft' => 'custom classes',
]),
Editing Badge Types
By default the Badge
field is not shown on a resource's edit or update pages. If you wish to modify the underlying value represented by the Badge
field on your edit forms, define another field in combination with the onlyOnForms
field option.
If you'd like to display your badge with an associated icon, you can use the withIcons
method to direct Nova to display an icon:
Badge::make('Status')->map([
'draft' => 'danger',
'published' => 'success',
])->withIcons(),
If you'd like to customize the icons used when display Badge
fields you can use the icons
method:
Badge::make('Status')->map([
'draft' => 'danger',
'published' => 'success',
])->icons([
'danger' => 'exclamation-circle',
'success' => 'check-circle',
]),
#Boolean Field
The Boolean
field may be used to represent a boolean / "tiny integer" column in your database. For example, assuming your database has a boolean column named active
, you may attach a Boolean
field to your resource like so:
use Laravel\Nova\Fields\Boolean;
Boolean::make('Active'),
#Customizing True / False Values
If you are using values other than true
, false
, 1
, or 0
to represent "true" and "false", you may instruct Nova to use the custom values recognized by your application. To accomplish this, chain the trueValue
and falseValue
methods onto your field's definition:
Boolean::make('Active')
->trueValue('On')
->falseValue('Off'),
#Boolean Group Field
The BooleanGroup
field may be used to group a set of Boolean checkboxes, which are then stored as JSON key-values in the database column they represent. You may create a BooleanGroup
field by providing a set of keys and labels for each option:
use Laravel\Nova\Fields\BooleanGroup;
BooleanGroup::make('Permissions')->options([
'create' => 'Create',
'read' => 'Read',
'update' => 'Update',
'delete' => 'Delete',
]),
The user will be presented with a grouped set of checkboxes which, when saved, will be converted to JSON format:
{
"create": true,
"read": false,
"update": false,
"delete": false
}
Before using this field type, you should ensure that the underlying Eloquent attribute is configured to cast to an array
(or equivalent) within your Eloquent model class:
protected $casts = [
'permissions' => 'array'
];
Sometimes, you may wish to exclude values that are true
or false
from display to avoid cluttering the representation of the field. You may accomplish this by invoking the hideFalseValues
or hideTrueValues
methods on the field definition:
BooleanGroup::make('Permissions')->options([
'create' => 'Create',
'read' => 'Read',
'update' => 'Update',
'delete' => 'Delete',
])->hideFalseValues(),
BooleanGroup::make('Permissions')->options([
'create' => 'Create',
'read' => 'Read',
'update' => 'Update',
'delete' => 'Delete',
])->hideTrueValues(),
If the underlying field is empty, Nova will display "No Data". You may customize this text using the noValueText
method:
BooleanGroup::make('Permissions')->options([
'create' => 'Create',
'read' => 'Read',
'update' => 'Update',
'delete' => 'Delete',
])->noValueText('No permissions selected.'),
#Code Field
The Code
fields provides a beautiful code editor within your Nova administration panel. Generally, code fields should be attached to TEXT
database columns:
use Laravel\Nova\Fields\Code;
Code::make('Snippet'),
You may also attach Code
fields to JSON
database columns. By default, the field will display the value as a JSON string. You may cast the underlying Eloquent attribute to array
, collection
, object
, or json
based on your application's needs:
use Laravel\Nova\Fields\Code;
Code::make('Options')->json(),
Code Fields On The Index
By default, Nova will never display a Code
field on a resource index listing.
#Editing JSON
If you intend to use a given Code
field instance to only edit JSON, you may chain the json
method onto your field definition:
Code::make('Options')->json(),
Code Field JSON Validation
Nova does not automatically apply the json
validation rule to Code
fields. This rule must be manually specified during validation if you wish for it to be applied.
#Syntax Highlighting
You may customize the language syntax highlighting of the Code
field using the language
method:
Code::make('Snippet')->language('php'),
The Code
field's currently supported languages are:
dockerfile
htmlmixed
javascript
markdown
nginx
php
ruby
sass
shell
sql
twig
vim
vue
xml
yaml-frontmatter
yaml
#Color Field
The Color
field generate a color picker using the HTML5 color
input element:
use Laravel\Nova\Fields\Color;
Color::make('Color', 'label_color'),
#Country Field
The Country
field generates a Select
field containing a list of the world's countries. The field will store the country's corresponding two-letter code:
use Laravel\Nova\Fields\Country;
Country::make('Country', 'country_code'),
#Currency Field
The Currency
field generates a Number
field that is automatically formatted using the brick/money
PHP package. Nova will use USD
as the default currency; however, this can be changed by modifying the nova.currency
configuration value:
use Laravel\Nova\Fields\Currency;
Currency::make('Price'),
You may override the currency on a per-field basis using the currency
method:
Currency::make('Price')->currency('EUR'),
Prerequisites
The ext-intl
PHP extension is required to display formatted currency. Or, you may install the symfony/polyfill-intl-icu
Composer package which offers support for the "en" locale.
You may use the min
, max
, and step
methods to set their corresponding attributes on the generated input
control:
Currency::make('price')->min(1)->max(1000)->step(0.01),
Currency Step Limitation
If you plan to customize the currency "step" amount using the step
method, you should ensure you always call the step
method after the currency
, asMinorUnits
, and asMajorUnits
methods. Calling these methods after the step
method will override the step
method's behavior.
The field's locale will respect the value in your application's app.locale
configuration value. You can override this behavior by providing a locale code to the locale
method:
Currency::make('Price')->locale('fr'),
#Date Field
The Date
field may be used to store a date value (without time). For more information about dates and timezones within Nova, check out the additional date / timezone documentation:
use Laravel\Nova\Fields\Date;
Date::make('Birthday'),
#DateTime Field
The DateTime
field may be used to store a date-time value. For more information about dates and timezones within Nova, check out the additional date / timezone documentation:
use Laravel\Nova\Fields\DateTime;
DateTime::make('Updated At')->hideFromIndex(),
#Email Field
The Email
field may be used to display a column with a mailto:
link on the index and detail views:
use Laravel\Nova\Fields\Email;
Email::make(),
Email::make('Customer Email', 'customer_email'),
#File Field
To learn more about defining file fields and handling uploads, please refer to the comprehensive file field documentation.
use Laravel\Nova\Fields\File;
File::make('Attachment'),
#Gravatar Field
The Gravatar
field does not correspond to any column in your application's database. Instead, it will display the "Gravatar" image of the model it is associated with.
By default, the Gravatar URL will be generated based on the value of the model's email
column. However, if your user's email addresses are not stored in the email
column, you may pass a custom column name to the field's make
method:
use Laravel\Nova\Fields\Gravatar;
// Using the "email" column...
Gravatar::make(),
// Using the "email_address" column...
Gravatar::make('Avatar', 'email_address'),
You may use the squared
method to display the image's thumbnail with squared edges. Additionally, you may use the rounded
method to display the images with fully-rounded edges:
Gravatar::make('Avatar', 'email_address')->squared(),
#Heading Field
The Heading
field does not correspond to any column in your application's database. It is used to display a banner across your forms and can function as a separator for long lists of fields:
use Laravel\Nova\Fields\Heading;
Heading::make('Meta'),
If you need to render HTML content within the Heading
field, you may invoke the asHtml
method when defining the field:
Heading::make('<p class="text-danger">* All fields are required.</p>')->asHtml(),
Headings & The Index Page
Heading
fields are automatically hidden from the resource index page.
#Hidden Field
The Hidden
field may be used to pass any value that doesn't need to be changed by the user but is required for saving the resource:
Hidden::make('Slug'),
Hidden::make('Slug')->default(Str::random(64)),
Combined with default values, Hidden
fields are useful for passing things like related IDs to your forms:
Hidden::make('User', 'user_id')->default(function ($request) {
return $request->user()->id;
}),
#ID Field
The ID
field represents the primary key of your resource's database table. Typically, each Nova resource you define should contain an ID
field. By default, the ID
field assumes the underlying database column is named id
; however, you may pass the column name as the second argument to the make
method if necessary:
use Laravel\Nova\Fields\ID;
ID::make(),
ID::make('ID', 'id_column'),
If your application contains very large integer IDs, you may need to use the asBigInt
method in order for the Nova client to correctly render the integer:
ID::make()->asBigInt(),
#Image Field
The Image
field extends the File field and accepts the same options and configurations. The Image
field, unlike the File
field, will display a thumbnail preview of the underlying image when viewing the resource:
use Laravel\Nova\Fields\Image;
Image::make('Photo'),
By default, the Image
field allows the user to download the linked file. To disable downloads, you may use the disableDownload
method on the field definition:
Image::make('Photo')->disableDownload(),
You may use the squared
method to display the image's thumbnail with squared edges. Additionally, you may use the rounded
method to display its thumbnails with fully-rounded edges.
File Fields
To learn more about defining file fields and handling uploads, check out the comprehensive file field documentation.
#KeyValue Field
The KeyValue
field provides a convenient interface to edit flat, key-value data stored inside JSON
column types. For example, you might store profile information inside a JSON column type named meta
:
use Laravel\Nova\Fields\KeyValue;
KeyValue::make('Meta')->rules('json'),
Given the field definition above, the following interface would be rendered by Nova:
#Customizing KeyValue Labels
You can customize the text values used in the component by calling the keyLabel
, valueLabel
, and actionText
methods when defining the field. The actionText
method customizes the "add row" button text:
KeyValue::make('Meta')
->keyLabel('Item')
->valueLabel('Label')
->actionText('Add Item'),
KeyValue Fields & The Index Page
By default, Nova will never display a KeyValue
field on a resource index listing.
If you would like to disable the user's ability to edit the keys of the field, you may use the disableEditingKeys
method to accomplish this. Disabling editing keys with the disableEditingKeys
method will automatically disable adding rows as well:
KeyValue::make('Meta')->disableEditingKeys(),
You may also remove the user's ability to add new rows to the field by chaining the disableAddingRows
method onto the field's definition:
KeyValue::make('Meta')->disableAddingRows(),
In addition, you may also wish to remove the user's ability to delete exisiting rows in the field. You may accomplish this by invoking the disableDeletingRows
method when defining the field:
KeyValue::make('Meta')->disableDeletingRows(),
#Markdown Field
The Markdown
field provides a WYSIWYG Markdown editor for its underlying Eloquent attribute. Typically, this field will correspond to a TEXT
column in your database. The Markdown
field will store the raw Markdown text within the associated database column:
use Laravel\Nova\Fields\Markdown;
Markdown::make('Biography'),
By default, Markdown fields will not display their content when viewing a resource's detail page. Instead, the content will be hidden behind a "Show Content" link that will reveal the field's content when clicked. You may specify that the Markdown field should always display its content by calling the alwaysShow
method on the field itself:
Markdown::make('Biography')->alwaysShow(),
The Markdown field uses the markdown-it
npm package to parse Markdown content. By default, it uses a parsing strategy similar to GitHub Flavoured Markdown, which does not allow HTML within the Markdown content. However, you can change the parsing strategy using the preset
method. The currently supported presets are default
, commonmark
, and zero
:
Markdown::make('Biography')->preset('commonmark'),
#MultiSelect Field
The MultiSelect
field provides a Select
field that allows multiple selection options. This field pairs nicely with model attributes that are cast to array
or equivalent:
use Laravel\Nova\Fields\MultiSelect;
MultiSelect::make('Sizes')->options([
'S' => 'Small',
'M' => 'Medium',
'L' => 'Large',
]),
On the resource index and detail pages, the MultiSelect
field's "key" value will be displayed. If you would like to display the label values instead, you may invoke the displayUsingLabels
method when defining the field:
MultiSelect::make('Size')->options([
'S' => 'Small',
'M' => 'Medium',
'L' => 'Large',
])->displayUsingLabels(),
You may also display multi-select options in groups by providing an array structure that contains keys and label
/ group
pairs:
MultiSelect::make('Sizes')->options([
'MS' => ['label' => 'Small', 'group' => "Men's Sizes"],
'MM' => ['label' => 'Medium', 'group' => "Men's Sizes"],
'WS' => ['label' => 'Small', 'group' => "Women's Sizes"],
'WM' => ['label' => 'Medium', 'group' => "Women's Sizes"],
])->displayUsingLabels(),
#Number Field
The Number
field provides an input
control with a type
attribute of number
:
use Laravel\Nova\Fields\Number;
Number::make('price'),
You may use the min
, max
, and step
methods to set the corresponding HTML attributes on the generated input
control:
Number::make('price')->min(1)->max(1000)->step(0.01),
#Password Field
The Password
field provides an input
control with a type
attribute of password
:
use Laravel\Nova\Fields\Password;
Password::make('Password'),
The Password
field will automatically preserve the password that is currently stored in the database if the incoming password field is empty. Therefore, a typical password field definition might look like the following:
Password::make('Password')
->onlyOnForms()
->creationRules('required', Rules\Password::defaults())
->updateRules('nullable', Rules\Password::defaults()),
#Password Confirmation Field
The PasswordConfirmation
field provides an input that can be used for confirming another Password
field. This field will only be shown on forms and will not attempt to hydrate an underlying attribute on the Eloquent model:
PasswordConfirmation::make('Password Confirmation'),
When using this field, you should define the appropriate validation rules on the corresponding Password
field:
Password::make('Password')
->onlyOnForms()
->creationRules('required', Rules\Password::defaults(), 'confirmed')
->updateRules('nullable', Rules\Password::defaults(), 'confirmed'),
PasswordConfirmation::make('Password Confirmation'),
#Select Field
The Select
field may be used to generate a drop-down select menu. The Select
menu's options may be defined using the options
method:
use Laravel\Nova\Fields\Select;
Select::make('Size')->options([
'S' => 'Small',
'M' => 'Medium',
'L' => 'Large',
]),
On the resource index and detail pages, the Select
field's "key" value will be displayed. If you would like to display the labels instead, you may use the displayUsingLabels
method:
Select::make('Size')->options([
'S' => 'Small',
'M' => 'Medium',
'L' => 'Large',
])->displayUsingLabels(),
You may also display Select
options in groups by providing an array structure that contains keys and label
/ group
pairs:
Select::make('Size')->options([
'MS' => ['label' => 'Small', 'group' => 'Men Sizes'],
'MM' => ['label' => 'Medium', 'group' => 'Men Sizes'],
'WS' => ['label' => 'Small', 'group' => 'Women Sizes'],
'WM' => ['label' => 'Medium', 'group' => 'Women Sizes'],
])->displayUsingLabels(),
If you need more control over the generation of the Select
field's options, you may provide a closure to the options
method:
Select::make('Size')->options(function () {
return array_filter([
Size::SMALL => Size::MAX_SIZE === SIZE_SMALL ? 'Small' : null,
Size::MEDIUM => Size::MAX_SIZE === SIZE_MEDIUM ? 'Medium' : null,
Size::LARGE => Size::MAX_SIZE === SIZE_LARGE ? 'Large' : null,
]);
}),
#Searchable Select Fields
At times it's convenient to be able to search or filter the list of options available in a Select
field. You can enable this by invoking the searchable
method on the field:
Select::make('Size')->searchable()->options([
'S' => 'Small',
'M' => 'Medium',
'L' => 'Large',
])->displayUsingLabels(),
After marking a select field as searchable
, Nova will display an input
field which allows you to filter the list of options based on its label:
#Slug Field
Sometimes you may need to generate a unique, human-readable identifier based on the contents of another field, such as when generating a "slug" for a blog post title. You can automatically generate these "slugs" using the Slug
field:
Slug::make('Slug')->from('Title'),
By default, the field will convert a string like 'My Blog Post' to a slug like 'my-blog-post'. If you would like the field to use underscores instead of dashes, you may use the separator
method to define your own custom "separator":
Slug::make('Slug')->from('Title')->separator('_'),
#Sparkline Field
The Sparkline
field may be used to display a small line chart on a resource's index or detail page. The data provided to a Sparkline
may be provided via an array
, a callable
(which returns an array), or an instance of a Trend
metric class:
// Using an array...
Sparkline::make('Post Views')->data([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
// Using a callable...
Sparkline::make('Post Views')->data(function () {
return json_decode($this->views_data);
}),
#Using Trend Metrics
If the data needed by your Sparkline
field requires complicated database queries to compute, you may wish to encapsulate the data retrieval within a trend metric which can then be provided to the Sparkline
field:
Sparkline::make('Post Views')->data(new PostViewsOverTime($this->id)),
In the example above, we're providing the post's id
to the metric's constructor. This value will become the resourceId
property of the request that is available within the trend metric. For example, within the metric, we could access this post ID via $request->resourceId
:
return $this->countByDays(
$request,
PostView::where('post_id', '=', $request->resourceId)
);
Default Ranges
When providing data to a Sparkline
field via a trend metric, the Sparkline
field will always use the first range defined in the ranges
method of the metric.
#Customizing The Chart
If a bar chart better suits your data, you may invoke the asBarChart
method when defining the field:
Sparkline::make('Post Views')
->data([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
->asBarChart(),
By default, a Sparkline
will appear on a resource's detail page. You can customize the dimensions of the chart using the height
and width
methods:
Sparkline::make('Post Views')
->data([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
->height(200)
->width(600),
#Status Field
The Status
field may be used to display a "progress state" column. Internally, Nova uses the Status
field to indicate the current state (waiting, running, or finished) of queued actions. However, you are free to use this field for your own purposes as needed:
The loadingWhen
and failedWhen
methods may be used to instruct the field which words indicate a "loading" state and which words indicate a "failed" state. In this example, we will indicate that database column values of waiting
or running
should display a "loading" indicator:
use Laravel\Nova\Fields\Status;
Status::make('Status')
->loadingWhen(['waiting', 'running'])
->failedWhen(['failed']),
#Stack Field
As your resource classes grow, you may find it useful to be able to group fields together to simplify your index and detail views. A Stack
field allows you to display fields like BelongsTo
, Text
, and others in a vertical orientation:
Stack::make('Details', [
Text::make('Name'),
Text::make('Slug')->resolveUsing(function () {
return Str::slug(optional($this->resource)->name);
}),
]),
Stack
fields are not shown on forms, and are only intended for stacking lines of text on the index and detail resource views.
#Line Fields
To gain more control over how the individual fields in a Stack
are displayed, you may use the Line
field, which provides methods for controlling the display of the line's text. Line
fields offer the following presentational methods:
asHeading
asSubTitle
asSmall
asBase
In addition to the Line
field's presentational methods, you may also pass any additional Tailwind classes to the field to customize the appearance of the Line
:
Stack::make('Details', [
Line::make('Title')->extraClasses('italic font-medium text-80'),
]),
#Passing Closures to Line Fields
In addition to passing BelongsTo
, Text
, and Line
fields to the Stack
field, you may also pass a closure. The result of the closure will automatically be converted to a Line
instance:
Stack::make('Details', [
Line::make('Name')->asHeading(),
fn () => optional($this->resource)->position
]),
#Text Field
The Text
field provides an input
control with a type
attribute of text
:
use Laravel\Nova\Fields\Text;
Text::make('Name'),
Text fields may be further customized by setting any attribute on the field. This can be done by calling the withMeta
method and providing an extraAttributes
array containing key / value pairs of HTML attributes:
Text::make('Name')->withMeta([
'extraAttributes' => [
'placeholder' => 'David Hemphill',
],
]),
#Text Field Suggestions
To offer auto-complete suggestions when typing into the Text
field, you may invoke the suggestions
method when defining the field. The suggestions
method should return an array
of suggestions:
Text::make('Name')->required()
->suggestions([
'David Hemphill',
'Taylor Otwell',
'James Brooks',
]),
#Formatting Text As Links
To format a Text
field as a link, you may invoke the asHtml
method when defining the field:
Text::make('Twitter Profile', function () {
$username = $this->twitterUsername;
return "<a href='https://twitter.com/{$username}'>@{$username}</a>";
})->asHtml(),
#Copying Text Field Values to the Clipboard
Sometimes you may wish to copy the value of a field into the system clipboard for pasting elsewhere. You can enable this on the detail view for a resource by calling the copyable
method on the Text
field:
Text::make('Twitter Profile')->copyable(),
#Textarea Field
The Textarea
field provides a textarea
control:
use Laravel\Nova\Fields\Textarea;
Textarea::make('Biography'),
By default, Textarea fields will not display their content when viewing a resource's detail page. Instead, the contents of the field will be hidden behind a "Show Content" link, which will reveal the content when clicked. However, if you would like, you may specify that the Textarea
field should always display its content by invoking the alwaysShow
method on the field:
Textarea::make('Biography')->alwaysShow(),
You may specify the Textarea
height by invoking the rows
method on the field:
Textarea::make('Excerpt')->rows(3),
Textarea
fields may be further customized by setting any attribute on the field. This can be done by calling the withMeta
method and providing an extraAttributes
array containing key / value pairs of HTML attributes:
Textarea::make('Excerpt')->withMeta(['extraAttributes' => [
'placeholder' => 'Make it less than 50 characters']
]),
#Timezone Field
The Timezone
field generates a Select
field containing a list of the world's timezones:
use Laravel\Nova\Fields\Timezone;
Timezone::make('Timezone'),
#Trix Field
The Trix
field provides a Trix editor for its associated field. Typically, this field will correspond to a TEXT
column in your database. The Trix
field will store its corresponding HTML within the associated database column:
use Laravel\Nova\Fields\Trix;
Trix::make('Biography'),
By default, Trix fields will not display their content when viewing a resource on its detail page. Instead, the content will be hidden behind a "Show Content" link, which will reveal the field's content when clicked. If you would like, you may specify that the Trix field should always display its content by invoking the alwaysShow
method when defining the field:
Trix::make('Biography')->alwaysShow(),
#Trix File Uploads
If you would like to allow users to drag-and-drop photos into the Trix
field, you may chain the withFiles
method onto the field's definition. When calling the withFiles
method, you should pass the name of the filesystem disk that photos should be stored on:
use Laravel\Nova\Fields\Trix;
Trix::make('Biography')->withFiles('public'),
In addition, you should define two database tables to store pending and persisted Trix uploads. To do so, create a migration with the following table definitions:
Schema::create('nova_pending_trix_attachments', function (Blueprint $table) {
$table->increments('id');
$table->string('draft_id')->index();
$table->string('attachment');
$table->string('disk');
$table->timestamps();
});
Schema::create('nova_trix_attachments', function (Blueprint $table) {
$table->increments('id');
$table->string('attachable_type');
$table->unsignedInteger('attachable_id');
$table->string('attachment');
$table->string('disk');
$table->string('url')->index();
$table->timestamps();
$table->index(['attachable_type', 'attachable_id']);
});
Finally, in your app/Console/Kernel.php
file, you should register a daily job to prune any stale attachments from the pending attachments table and storage. For convenience, Laravel Nova provides the job implementation needed to accomplish this:
use Laravel\Nova\Trix\PruneStaleAttachments;
$schedule->call(new PruneStaleAttachments)->daily();
#UI-Avatar Field
The UiAvatar
field does not correspond to any column in your application's database. Instead, this field will generate a simple avatar containing the user's initials. This field is powered by ui-avatars.com .
By default, the UiAvatar
image will be generated based on the value of the model's name
column. However, if your user's names are not stored in the name
column, you may pass a custom column name to the field's make
method:
use Laravel\Nova\Fields\UiAvatar;
// Using the "name" column...
UiAvatar::make(),
// Using a custom column...
UiAvatar::make('Avatar', 'full_name'),
If necessary, you may invoke the resolveUsing
method to specify a closure that should be invoked to determine the name that should be used to generate the avatar:
UiAvatar::make()->resolveUsing(function () {
return implode(' ', explode('@', $this->email));
}),
You may use the squared
method to display the image's thumbnail with squared edges. Additionally, you may use the rounded
method to display the images with fully-rounded edges:
UiAvatar::make('Avatar', 'fullname')->squared(),
Additional options available when defining UiAvatar
fields include:
Option | Method | Description |
---|---|---|
Font Size | fontSize(0.4) | Set a font size between 0.1 to 1 . |
Bold | bold() | Set font weight to bold. |
Background Color | backgroundColor('1D4ED7') | Set the hex color for the image background. |
Text Color | color('FFFFFF') | Set the hex color for the image text. |
#URL Field
The URL
field renders URLs as clickable links instead of plain text:
URL::make('GitHub URL'),
The URL
field also supports customizing the generated link's text by invoking the displayUsing
method when defining the field. The displayUsing
method accepts a closure that should return the link's text:
URL::make('Receipt')
->displayUsing(fn () => "{optional($this->user)->name}'s receipt")
By providing a closure as the second argument to the URL
field, you may use the field to render a link for a computed value that does not necessarily correspond to a column within the associated model's database table:
URL::make('Receipt', fn () => $this->receipt_url)
#Vapor File Field
The Vapor file field provides convenience and compatibility for uploading files when deploying applications to a serverless environment using Laravel Vapor :
use Laravel\Nova\Fields\VaporFile;
VaporFile::make('Document'),
When uploading a file using a VaporFile
field, Nova will first generate a signed storage URL on Amazon S3. Next, Nova will upload the file directly to temporary storage in the Amazon S3 bucket. When the resource is saved, Nova will move the file to permanent storage.
Vapor Storage
For more information on how file storage is handled for Vapor applications, please refer to the Laravel Vapor storage documentation .
#Vapor Image Field
Vapor file fields provide convenience and compatibility for uploading image files when deploying applications in a serverless environment using Laravel Vapor :
use Laravel\Nova\Fields\VaporImage;
VaporImage::make('Avatar'),
Vapor image files support many of the same methods available to Image
fields.
File Fields
To learn more about defining file fields and handling uploads, check out the additional file field documentation.
#Validating Vapor Image / File Fields
In order to validate the size or other attributes of a Vapor file, you will need to inspect the file directly via the Storage
facade:
use Illuminate\Support\Facades\Storage;
VaporFile::make('Document')
->rules('bail', 'required', function ($attribute, $value, $fail) use ($request) {
if (Storage::size($request->input('vaporFile')[$attribute]['key']) > 1000000) {
return $fail('The document size may not be greater than 1 MB');
}
}),
#Computed Fields
In addition to displaying fields that are directly associated with columns in your database, Nova allows you to create "computed fields". Computed fields may be used to display computed values that are not associated with a database column. Since they are not associated with a database column, computed fields may not be sortable
. These fields may be created by passing a callable (instead of a column name) as the second argument to the field's make
method:
Text::make('Name', function () {
return $this->first_name.' '.$this->last_name;
}),
The model instance will be passed to the computed field callable, allowing you to access the model's properties while computing the field's value:
Text::make('Name', function ($model) {
return $model->first_name.' '.$model->last_name;
}),
Model Attribute Access
As you may have noticed in the example above, you may also use $this
to access the resource's underlying model attributes and relationships.
By default, Vue will escape the content of a computed field. If you need to render HTML content within the field, invoke the asHtml
method when defining your field:
Text::make('Status', function () {
return view('partials.status', [
'isPassing' => $this->isPassing(),
])->render();
})->asHtml(),
#Customization
#Readonly Fields
There are times where you may want to allow the user to only create and update certain fields on a resource. You can mark fields as "read only" by invoking the readonly
method on the field, which will disable the field's corresponding input. You may pass a boolean argument to the readonly
method to dynamically control whether a field should be "read only":
Text::make('Email')->readonly(optional($this->resource)->trashed()),
You may also pass a closure to the readonly
method, and the result of the closure will be used to determine if the field should be "read only". The closure will receive the current NovaRequest
as its first argument:
Text::make('Email')->readonly(function ($request) {
return ! $request->user()->isAdmin();
}),
If you only want to mark a field as "read only" when creating or attaching resources, you may use the isCreateOrAttachRequest
and isUpdateOrUpdateAttachedRequest
methods available via the NovaRequest
instance, respectively:
Text::make('Email')->readonly(function ($request) {
return $request->isUpdateOrUpdateAttachedRequest();
}),
#Required Fields
By default, Nova will use a red asterisk to indicate a field is required:
Nova does this by looking for the required
rule inside the field's validation rules to determine if it should show the required state. For example, a field with the following definition would receive a "required" indicator:
Text::make('Email')->rules('required'),
When you have complex required
validation requirements, you can manually mark the field as required by passing a boolean to the required
method when defining the field. This will inform Nova that a "required" indicator should be shown in the UI:
Text::make('Email')->required(true),
In addition, you may also pass a closure to the required
method to determine if the field should be marked as required. The closure will receive an instance of NovaRequest
. The value returned by the closure will be used to determine if field is required:
use Illuminate\Validation\Rule;
Text::make('Email')->required(function ($request) {
return $this->notify_via_email;
}),
required()
Limitations
The required()
method will only add a "required" indicator to the Nova UI. You must still define the related requirement rules()
that should apply during validation.
#Nullable Fields
By default, Nova attempts to store all fields with a value, however, there are times where you may prefer that Nova store a null
value in the corresponding database column when the field is empty. To accomplish this, you may invoke the nullable
method on your field definition:
Text::make('Position')->nullable(),
You may also set which values should be interpreted as a null
value using the nullValues
method, which accepts an array or a closure as its only argument:
Text::make('Position')->nullable()->nullValues(['', '0', 'null']),
Text::make('Position')->nullable()->nullValues(function ($value) {
return $value == '' || $value == 'null' || (int)$value === 0;
}),
#Field Help Text
If you would like to place "help" text beneath a field, you may invoke the help
method when defining your field:
Text::make('Tax Rate')->help(
'The tax rate to be applied to the sale'
),
If necessary, you may include HTML within your field's help text to further customize the help text:
Text::make('First Name')->help(
'<a href="#">External Link</a>'
),
Text::make('Last Name')->help(
view('partials.help-text', ['name' => $this->name])->render()
),
#Field Stacking
By default, Nova displays fields next to their labels, however some fields like "Code", "Markdown", and "Trix" may benefit from the extra width that can be gained by placing the field under their corresponding labels. Fields can be stacked underneath their label using the stacked
method:
Trix::make('Content')->stacked(),
#Field Text Alignment
You may change the text alignment of fields using the textAlign
method:
Text::make('Phone Number')->textAlign('left'),
The following alignments are valid:
left
center
right
#Field Resolution / Formatting
The resolveUsing
method allows you to customize how a field is formatted after it is retrieved from your database but before it is sent to the Nova front-end. This method accepts a callback which receives the raw value of the underlying database column:
Text::make('Name')->resolveUsing(function ($name) {
return strtoupper($name);
}),
If you would like to customize how a field is formatted only when it is displayed on a resource's "index" or "detail" pages, you may use the displayUsing
method. Like the resolveUsing
method, this method accepts a single callback:
Text::make('Name')->displayUsing(function ($name) {
return strtoupper($name);
}),
#Filterable Fields
The filterable
method allows you to enable convenient, automatic filtering functionality for a given field on resources, relationships, and lenses. The Nova generated filter will automatically be made available via the resource filter menu on the resource's index:
DateTime::make('Created At')->filterable(),
The filterable
method also accepts a closure as an argument. This closure will receive the filter query, which you may then customize in order to filter the resource results to your liking:
Text::make('Email')->filterable(function ($request, $query, $value, $attribute) {
$query->where($attribute, 'LIKE', "{$value}%");
}),
The generated filter will be a text filter, select filter, number range filter, or date range filter depending on the underlying field type that was marked as filterable.
#Dependent Fields
The dependsOn
method allows you to specify that a field's configuration depends on one or more other field's values. The dependsOn
method accepts an array
of dependent field attributes and a closure that modifies the configuration of the current field instance.
Dependent fields allow advanced customization, such as toggling read-only mode, validation rules, and more based on the state of another field:
use Laravel\Nova\Fields\FormData;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
Select::make('Purchase Type', 'type')
->options([
'personal' => 'Personal',
'gift' => 'Gift',
]),
// Recipient field configuration is customized based on purchase type...
Text::make('Recipient')
->readonly()
->dependsOn(
['type'],
function (Text $field, NovaRequest $request, FormData $formData) {
if ($formData->type === 'gift') {
$field->readonly(false)->rules(['required', 'email']);
}
}
),
The following field types may depend on other fields:
- BelongsTo
- Boolean
- BooleanGroup
- Color
- Code
- Country
- Currency
- File
- Heading
- Hidden
- Image
- KeyValue
- Markdown
- Number
- Password
- PasswordConfirmation
- Status
- Textarea
- Text
- URL
- VaporFile
- VaporImage
The following field types may not be depended upon by other fields since they do not live-report their changes to Nova:
- Code
- File
- Image
- KeyValue
- Status
- Trix
- VaporFile
- VaporImage
#Toggling Field Visibility
One common use-case for dependent fields is toggling field visibility based on the value of another field. You can accomplish this using the hide
and show
methods:
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Fields\FormData;
use Laravel\Nova\Http\Requests\NovaRequest;
Boolean::make('Anonymous Comment', 'anonymous')
->default(true),
BelongsTo::make('User')
->hide()
->rules('sometimes')
->dependsOn('anonymous', function (BelongsTo $field, NovaRequest $request, FormData $formData) {
if ($formData->anonymous === false) {
$field->show()->rules('required');
}
}),
#Accessing Request Resource IDs
When interacting with dependent fields, you may retrieve the current resource and related resource IDs via the resource
method:
Currency::make('Price')
->dependsOn('book', function ($field, NovaRequest $request, $formData) {
$bookId = (int) $formData->resource('book', $formData->book);
if ($bookId == 1) {
$field->rules([
'required', 'numeric', 'min:10', 'max:199'
])->help('Price starts from $10-$199');
return;
}
$field->rules([
'required', 'numeric', 'min:0', 'max:99'
])->help('Price starts from $0-$99');
}),
Nova offers two types of date fields: Date
and DateTime
. As you may have guessed, the Date
field does not store time information while the DateTime
field does:
use Laravel\Nova\Fields\Date;
use Laravel\Nova\Fields\DateTime;
Date::make('Birthday'),
DateTime::make('Created At'),
#Options
#Steps
By default, Nova will set a minimum "step" of 1 day for Date
fields and 1 second for DateTime
fields. You may modify the "step" value for both of these fields by providing an integer or Carbon\CarbonInterval
to the field's step
methods:
use Carbon\CarbonInterval;
Date::make('Expired On')->step(7),
Date::make('Expired On')->step(CarbonInterval::weeks(1)),
DateTime::make('Published At')->step(60),
DateTime::make('Published At')->step(CarbonInterval::minutes(1)),
#Minimum and Maximum Values
Sometimes you may wish to explicitly define minimum and maximum values for Date
or DateTime
fields. This can be done by passing a valid date expression, a date format supported by strtotime
, or an instance of Carbon\CarbonInterface
to the min
and max
methods of these fields:
use Carbon\Carbon;
Date::make('Expired On')
->min('tomorrow')
->max('next week'),
Date::make('Expired On')
->min(Carbon::tomorrow())
->max(Carbon::today()->addWeek(1)),
#Timezones
By default, Nova users will always see dates presented in your application's "server-side" timezone as defined by the timezone
configuration option in your application's config/app.php
configuration file.
#Customizing The Timezone
Sometimes you may wish to explicitly define the Nova user's timezone instead of using the application's timezone configuration. For example, perhaps your application allows users to select their own timezone so that they always see consistent date timezones even when traveling around the world.
To accomplish this, you may use the Nova::userTimezone
method. Typically you should call this method in the boot
method of your application's NovaServiceProvider
:
use Laravel\Nova\Nova;
use Illuminate\Http\Request;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Nova::userTimezone(function (Request $request) {
return $request->user()->timezone;
});
}
Nova offers several types of file fields: File
, Image
, Avatar
, VaporFile
, and VaporImage
. The File
field is the most basic form of file upload field, and is the base class for both the Image
and Avatar
fields. In the following documentation, we will explore each of these fields and discuss their similarities and differences.
#Overview
To illustrate the behavior of Nova file upload fields, let's assume our application's users can upload "profile photos" to their account. So, our users
database table will have a profile_photo
column. This column will contain the path to the profile photo on disk, or, when using a cloud storage provider such as Amazon S3, the profile photo's path within its "bucket".
#Defining The Field
Next, let's attach the file field to our User
resource. In this example, we will create the field and instruct it to store the underlying file on the public
disk. This disk name should correspond to a disk name in your application's filesystems
configuration file:
use Laravel\Nova\Fields\File;
File::make('Profile Photo')->disk('public'),
#Disabling File Downloads
By default, the File
field allows the user to download the corresponding file. To disable this, you may call the disableDownload
method on the field definition:
File::make('Profile Photo')->disableDownload(),
#How Files Are Stored
When a file is uploaded using this field, Nova will use Laravel's Flysystem integration to store the file on the disk of your choosing and the file will be assigned a randomly generated filename. Once the file is stored, Nova will store the relative path to the file in the file field's underlying database column.
To illustrate the default behavior of the File
field, let's take a look at an equivalent Laravel route that would store the file in the same way:
use Illuminate\Http\Request;
Route::post('/photo', function (Request $request) {
$path = $request->profile_photo->store('/', 'public');
$request->user()->update([
'profile_photo' => $path,
]);
});
Of course, once the file has been stored, you may retrieve it within your application using the Laravel Storage
facade:
use Illuminate\Support\Facades\Storage;
Storage::get($user->profile_photo);
Storage::url($user->profile_photo);
Customization
The documentation above only demonstrates the default behavior of the File
field. To learn more about how to customize its behavior, check out the customization documentation.
#The Local Disk
If you are using the public
disk in conjunction with the local
driver, you should run the php artisan storage:link
Artisan command to create a symbolic link from public/storage
to storage/app/public
. To learn more about file storage in Laravel, check out the Laravel file storage documentation .
#Images
The Image
field behaves exactly like the File
field; however, instead of only displaying the path to the file within the Nova dashboard, an Image
field will show a thumbnail preview of the underlying file. All of the configuration and customization options of the Image
field mirror that of the File
field:
use Laravel\Nova\Fields\Image;
Image::make('Profile Photo')->disk('public'),
To set the width of the Image
field when being displayed, you can use the maxWidth
method:
Image::make('Profile Photo')->maxWidth(100),
#Avatars
The Avatar
field behaves exactly like the File
field; however, instead of only displaying the path to the file within the Nova dashboard, an Avatar
field will show a thumbnail preview of the underlying file. All of the configuration and customization options of the Avatar
field mirror that of the File
field:
use Laravel\Nova\Fields\Avatar;
Avatar::make('Poster')->disk('public'),
In addition to displaying a thumbnail preview of the underlying file, an Avatar
field will also be automatically displayed in Nova search results. An Avatar
field is not limited to "user" resources - you may attach Avatar
fields to any resource within your Nova application:

#Storing Metadata
In addition to storing the path to the file within the storage system, you may also instruct Nova to store the original client filename and its size (in bytes). You may accomplish this using the storeOriginalName
and storeSize
methods. Each of these methods accept the name of the column you would like to store the file information:
use Illuminate\Http\Request;
use Laravel\Nova\Fields\File;
use Laravel\Nova\Fields\Text;
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
// ...
File::make('Attachment')
->disk('s3')
->storeOriginalName('attachment_name')
->storeSize('attachment_size'),
Text::make('Attachment Name')->exceptOnForms(),
Text::make('Attachment Size')
->exceptOnForms()
->displayUsing(function ($value) {
return number_format($value / 1024, 2).'kb';
}),
];
}
One benefit of storing the original client filename is the ability to create file download responses using the original filename that was used to upload the file. For example, you may do something like the following in one of your application's routes:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
Route::get('/download', function (Request $request) {
$user = $request->user();
return Storage::download(
$user->attachment, $user->attachment_name
);
});
File Downloads
When using the storeOriginalName
method, the file field's "Download" link within the Nova dashboard will automatically download the file using its original name.
#Pruning & Deletion
File fields are deletable by default, but you can override this behavior by using the deletable
method:
File::make('Photo')->disk('public')->deletable(false),
The File
field, as well as the Image
and Avatar
fields, may be marked as prunable
. The prunable
method will instruct Nova to delete the underlying file from storage when the associated model is deleted from the database:
File::make('Profile Photo')->disk('public')->prunable(),
Non-Nova Deletes
Nova will only automatically prune files for model deletes that are initiated within Nova. Other portions of your application may need to implement their own file deletion logic.
#Customization
#Customizing File Storage
Previously we learned that, by default, Nova stores files using the store
method of the Illuminate\Http\UploadedFile
class. However, you may fully customize this behavior based on your application's needs.
#Customizing The Name / Path
If you only need to customize the name or path of the stored file on disk, you may use the path
and storeAs
methods of the File
field:
use Illuminate\Http\Request;
File::make('Attachment')
->disk('s3')
->path($request->user()->id.'-attachments')
->storeAs(function (Request $request) {
return sha1($request->attachment->getClientOriginalName());
}),
#Customizing The Entire Storage Process
However, if you would like to take total control over the file storage logic of a field, you may use the store
method. The store
method accepts a callable which receives the incoming HTTP request and the model instance associated with the request:
use Illuminate\Http\Request;
File::make('Attachment')
->store(function (Request $request, $model) {
return [
'attachment' => $request->attachment->store('/', 's3'),
'attachment_name' => $request->attachment->getClientOriginalName(),
'attachment_size' => $request->attachment->getSize(),
];
}),
As you can see in the example above, the store
callback is returning an array of keys and values. These key / value pairs are mapped onto your model instance before it is saved to the database, allowing you to update one or many of the model's database columns after your file is stored.
Here's another example of customizing the storage process. In this example, we're using the store
method to store the original file in public storage, create thumbnails using Laravel's queue system, and finally populating values in the resource's media
relationship:
use Laravel\Nova\Http\Requests\NovaRequest;
File::make('Attachment')
->store(function (NovaRequest $request, $model) {
return function () use ($resource, $request) {
$media = $resource->media()->updateOrCreate([], [
'path'=> $request->file('attachment')->store('/path', 'public')
]);
OptimizeMedia::dispatch($media);
};
}),
#Invokables
Of course, performing all of your file storage logic within a Closure can cause your resource to become bloated. For that reason, Nova allows you to pass an "invokable" object to the store
method:
File::make('Attachment')->store(new StoreAttachment),
The invokable object should be a simple PHP class with a single __invoke
method:
<?php
namespace App\Nova;
use Laravel\Nova\Http\Requests\NovaRequest;
class StoreAttachment
{
/**
* Store the incoming file upload.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @param string $requestAttribute
* @param string $disk
* @param string $storagePath
* @return array
*/
public function __invoke(NovaRequest $request, $model, $attribute, $requestAttribute, $disk, $storagePath)
{
return [
'attachment' => $request->attachment->store('/', 's3'),
'attachment_name' => $request->attachment->getClientOriginalName(),
'attachment_size' => $request->attachment->getSize(),
];
}
}
#Customizing File Deletion
When a file is deleted from the Nova administration panel, Nova will automatically remove the underlying file from storage and insert NULL
into the field's associated column.
If you would like to override this behavior and provide your own file deletion implementation, you may use the delete
method. Like the store
method discussed above, the delete
method accepts a callable which receives the incoming HTTP request and the model instance associated with the request:
use Illuminate\Support\Facades\Storage;
use Laravel\Nova\Http\Requests\NovaRequest;
File::make('Attachment')
->disk('s3')
->delete(function (NovaRequest $request, $model, $disk, $path) {
if (! $path) {
return;
}
Storage::disk($disk)->delete($path);
return [
'attachment' => null,
'attachment_name' => null,
'attachment_size' => null,
];
}),
As you can see in the example above, the delete
callback is returning an array of keys and values. These key / value pairs are mapped onto your model instance before it is saved to the database, allowing you to update one or many of the model's database columns after your file is stored. Typically, when deleting a field, you will insert NULL
into the relevant database columns.
#Invokables
Of course, performing all of your file deletion logic within a Closure can cause your resource to become bloated. For that reason, Nova allows you to pass an "invokable" object to the delete
method:
File::make('Attachment')->delete(new DeleteAttachment);
The invokable object should be a simple PHP class with a single __invoke
method:
<?php
namespace App\Nova;
use Illuminate\Support\Facades\Storage;
use Laravel\Nova\Http\Requests\NovaRequest;
class DeleteAttachment
{
/**
* Delete the field's underlying file.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @param string|null $disk
* @param string|null $path
* @return array
*/
public function __invoke(NovaRequest $request, $model, $disk, $path)
{
if (! $path) {
return;
}
Storage::disk($disk)->delete($path);
return [
'attachment' => null,
'attachment_name' => null,
'attachment_size' => null,
];
}
}
#Customizing Previews
By default, Nova will use the Storage::url
method to determine the URL that should be used to display image previews on the resource detail page and edit form. However, you may customize the generation of this URL using the preview
method.
The preview
method accepts a callable which should return the preview URL. The field's underlying column value is passed to the callable as the first parameter, while the name of the field's storage disk is passed as the second parameter:
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;
Image::make('Profile Photo')
->disk('public')
->preview(function ($value, $disk) {
return $value
? Storage::disk($disk)->url($value)
: null;
}),
Preview Size
By default, the Nova resource detail page will display previews at a width of 318 pixels (636 pixels for "retina displays").
#Customizing Thumbnails
By default, Nova will use the Storage::url
method to determine the URL that should be used to display thumbnail previews on the resource index page and within search results (when using the Avatar
field). However, you may customize the generation of this URL using the thumbnail
method.
The thumbnail
method accepts a callable which should return the thumbnail URL. The field's underlying column value is passed to the callable as the first parameter, while the name of the field's storage disk is passed as the second parameter:
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;
Image::make('Profile Photo')
->disk('public')
->thumbnail(function ($value, $disk) {
return $value
? Storage::disk($disk)->url($value)
: null;
}),
Thumbnail Size
By default, Nova will display thumbnails at a width of 32 pixels (64 pixels for "retina displays").
#Customizing Downloads
By default, Nova will use the Storage::download
method to determine the file and filename that should be used for downloading the file. However, you may customize the generation of this URL using the download
method. The download
method accepts a callable which should return the result of your own invocation of the Storage::download
method:
use Laravel\Nova\Fields\Image;
use Illuminate\Support\Facades\Storage;
Image::make('Profile Photo')
->disk('public')
->download(function ($request, $model, $disk, $value) {
return Storage::disk($disk)->download($value, 'avatar');
}),
#Customizing Accepted File Types
By default, the File
field will allow any type of file to be uploaded; however, you may customize the accepted file types using the acceptedTypes
method:
File::make('Disk Image')->acceptedTypes('.dmg,.exe')
When using the acceptedTypes
method, Nova adds the accepts
attribute to the file input element; therefore, all of the following media types may be provided to the acceptedTypes
method:
.dmg
.dmg,.exe,.deb
image/*
audio/*
video/*
- All media types listed at http://www.iana.org/assignments/media-types/
File Type Validation
Since the acceptedTypes
method only performs client-side validation, you should also validate the file type using server-side validation rules.
In addition to the variety of fields we've already discussed, Nova has full support for all of Laravel's relationships. Once you add relationship fields to your Nova resources, you'll start to experience the full power of the Nova dashboard, as the resource detail page will allow you to quickly view and search a resource's related models:
#HasOne
The HasOne
field corresponds to a hasOne
Eloquent relationship. For example, let's assume a User
model hasOne
Address
model. We may add the relationship to our User
Nova resource like so:
use Laravel\Nova\Fields\HasOne;
HasOne::make('Address'),
Like other types of fields, relationship fields will automatically "camel case" the displayable name of the field to determine the underlying relationship method / attribute. However, you may explicitly specify the name of the relationship method by passing it as the second argument to the field's make
method:
HasOne::make('Dirección', 'address'),
#HasOneOfMany
The HasOne
relationship field can be transformed into an "has one of many" Eloquent relationship using the ofMany
method. For example, let's assume a User
model hasMany
Post
models. We may add the "has one of many" relationship to our User
Nova resource like so:
use App\Nova\Post;
use Laravel\Nova\Fields\HasOne;
HasOne::ofMany('Latest Post', 'latestPost', Post::class),
#HasMany
The HasMany
field corresponds to a hasMany
Eloquent relationship. For example, let's assume a User
model hasMany
Post
models. We may add the relationship to our User
Nova resource like so:
use Laravel\Nova\Fields\HasMany;
HasMany::make('Posts'),
Once the field has been added to your resource, it will be displayed on the resource's detail page.
Plural Resource Names
When defining HasMany
relationships, make sure to use the plural form of the relationship so Nova can infer the correct singular resource name:
HasMany::make('Posts'),
#HasOneThrough
The HasOneThrough
field corresponds to a hasOneThrough
Eloquent relationship. For example, let's assume a Mechanic
model has one Car
, and each Car
may have one Owner
. While the Mechanic
and the Owner
have no direct connection, the Mechanic
can access the Owner
through the Car
itself. You can display this relationship by adding it to your Nova resource:
use Laravel\Nova\Fields\HasOneThrough;
HasOneThrough::make('Owner'),
#HasManyThrough
The HasManyThrough
field corresponds to a hasManyThrough
Eloquent relationship. For example, a Country
model might have many Post
models through an intermediate User
model. In this example, you could easily gather all blog posts for a given country. To display this relationship within Nova, you may add it to your Nova resource:
use Laravel\Nova\Fields\HasManyThrough;
HasManyThrough::make('Posts'),
#BelongsTo
The BelongsTo
field corresponds to a belongsTo
Eloquent relationship. For example, let's assume a Post
model belongsTo
a User
model. We may add the relationship to our Post
Nova resource like so:
use Laravel\Nova\Fields\BelongsTo;
BelongsTo::make('User'),
Customizing Resource Classes
You may customize the resource class used by the relation field by providing the second and third arguments of the make
method, which define the name of the relationship and the underlying Nova resource class:
BelongsTo::make('Author', 'author', 'App\Nova\User'),
#Nullable Relationships
If you would like your BelongsTo
relationship to be nullable
, you may simply chain the nullable
method onto the field's definition:
use Laravel\Nova\Fields\BelongsTo;
BelongsTo::make('User')->nullable(),
#Title Attributes
When a BelongsTo
field is shown on a resource creation / update page, a drop-down selection menu or search menu will display the "title" of the resource. For example, a User
resource may use the name
attribute as its title. Then, when the resource is shown in a BelongsTo
selection menu, that attribute will be displayed:
To customize the "title" attribute of a resource, you may define a title
property on the resource class:
public static $title = 'name';
Alternatively, you may override the resource's title
method:
/**
* Get the value that should be displayed to represent the resource.
*
* @return string
*/
public function title()
{
return $this->name;
}
#Disable Ordering By Title
By default, associatable resources will be sorted by their title when listed in a select dropdown. Using the dontReorderAssociatables
method, you can disable this behavior so that the resources as sorted based on the ordering specified by the relatable query:
BelongsTo::make('User')->dontReorderAssociatables(),
#Filter Trashed Items
By default, the BelongsTo
field will allow users to select soft-deleted models; however, this can be disabled using the withoutTrashed
method:
BelongsTo::make('User')->withoutTrashed(),
#BelongsToMany
The BelongsToMany
field corresponds to a belongsToMany
Eloquent relationship. For example, let's assume a User
model belongsToMany
Role
models:
public function roles()
{
return $this->belongsToMany(Role::class);
}
We may add the relationship to our User
Nova resource like so:
use Laravel\Nova\Fields\BelongsToMany;
BelongsToMany::make('Roles'),
You may customize the resource class used by the relationship field by providing the second and third arguments to the make
method:
BelongsToMany::make('Pseudonyms', 'pseudonyms', 'App\Nova\Author'),
Once the field has been added to your resource, it will be displayed on the resource's detail page.
#Pivot Fields
If your belongsToMany
relationship interacts with additional "pivot" fields that are stored on the intermediate table of the many-to-many relationship, you may also attach those to your BelongsToMany
Nova relationship. Once these fields are attached to the relationship field, and the relationship has been defined on both of the related models / resources, they will be displayed on the related resource index.
For example, let's assume our User
model belongsToMany
Role
models. On our role_user
intermediate table, let's imagine we have a notes
field that contains some simple text notes about the relationship. We can attach this pivot field to the BelongsToMany
field using the fields
method:
BelongsToMany::make('Roles')
->fields(function ($request, $relatedModel) {
return [
Text::make('Notes'),
];
}),
Of course, it is likely we would also define this field on the inverse of the relationship. So, if we define the BelongsToMany
field on the User
resource, we would define its inverse on the Role
resource:
BelongsToMany::make('Users')
->fields(function ($request, $relatedModel) {
return [
Text::make('Notes'),
];
}),
Pivot fields must be defined
Don't forget to define the pivot fields inside your Model's relationship definition using the withPivot
method: https://laravel.com/docs/9.x/eloquent-relationships#retrieving-intermediate-table-columns
Since defining the field on both ends of the relationship can cause some code duplication, Nova allows you to pass an invokable object to the fields
method:
BelongsToMany::make('Users')->fields(new RoleUserFields),
In this example, the RoleUserFields
class would be a simple, invokable class that returns the array of pivot fields:
<?php
namespace App\Nova;
use Laravel\Nova\Fields\Text;
class RoleUserFields
{
/**
* Get the pivot fields for the relationship.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $relatedModel
* @return array
*/
public function __invoke($request, $relatedModel)
{
return [
Text::make('Notes'),
];
}
}
#Pivot Computed Fields
Laravel Nova also allows you to define computed fields within the field list of a belongsToMany
relationship field:
BelongsToMany::make('Users')
->fields(function ($request, $relatedModel) {
return [
Text::make('Notes'),
Boolean::make('Has Notes', function ($pivot) {
return ! empty($pivot->notes);
}),
];
}),
#Pivot Actions
Typically, Nova actions operate on a resource. However, you may also attach actions to belongsToMany
fields so that they can operate on pivot / intermediate table records. To accomplish this, you may chain the actions
method onto your field's definition:
BelongsToMany::make('Roles')
->actions(function () {
return [
new Actions\MarkAsActive,
];
}),
Once the action has been attached to the field, you will be able to select the action and execute it from the relationship index on the parent resource's detail page.
Actions
To learn more about Nova actions, check out the complete action documentation.
#Title Attributes
When a BelongsToMany
field is shown on a resource creation / update page, a drop-down selection menu or search menu will display the "title" of the resource. For example, a Role
resource may use the name
attribute as its title. Then, when the resource is shown in a BelongsToMany
selection menu, that attribute will be displayed:
To customize the "title" attribute of a resource, you may define a title
property on the resource class:
public static $title = 'name';
Alternatively, you may override the resource's title
method:
/**
* Get the value that should be displayed to represent the resource.
*
* @return string
*/
public function title()
{
return $this->name;
}
#Allowing Duplicate Relations
By default, Laravel Nova ensures that "belongs to many" relationships are unique. However, if necessary, you may instruct Nova to allow duplicate relationship entries.
To get started, you should ensure that your pivot record's id
column is available by using the withPivot
method when defining the relationship on your Eloquent model. In this example, let's imagine that a User
may purchase a Book
one or more times:
public function books()
{
return $this->belongsToMany(Book::class)
->using(BookPurchase::class)
->withPivot('id', 'notes')
->withTimestamps();
}
Next, we can define the Nova relationship that allows duplicate relations using the allowDuplicateRelations
method:
BelongsToMany::make('Books')
->fields(function () {
return [
Text::make('Notes'),
];
})->allowDuplicateRelations(),
#MorphOne
The MorphOne
field corresponds to a morphOne
Eloquent relationship. For example, let's assume a Post
has a one-to-one polymorphic relationship with the Image
model. We may add the relationship to our Post
Nova resource like so:
use Laravel\Nova\Fields\MorphOne;
MorphOne::make('Image'),
#MorphOneOfMany
The MorphOne
relationship field can be transformed into a "morph one of many" Eloquent relationship using the ofMany
method. For example, let's assume a Post
has a one-to-many polymorphic relationship with the Comment
model. We may add the relationship to our Post
Nova resource like so:
use App\Nova\Comment;
use Laravel\Nova\Fields\MorphOne;
MorphOne::ofMany('Latest Comment', 'latestComment', Comment::class),
#MorphMany
The MorphMany
field corresponds to a morphMany
Eloquent relationship. For example, let's assume a Post
has a one-to-many polymorphic relationship with the Comment
model. We may add the relationship to our Post
Nova resource like so:
use Laravel\Nova\Fields\MorphMany;
MorphMany::make('Comments'),
#MorphTo
The MorphTo
field corresponds to a morphTo
Eloquent relationship. For example, let's assume a Comment
model has a polymorphic relationship with both the Post
and Video
models. We may add the relationship to our Comment
Nova resource like so:
use App\Nova\Post;
use App\Nova\Video;
use Laravel\Nova\Fields\MorphTo;
MorphTo::make('Commentable')->types([
Post::class,
Video::class,
]),
As you can see in the example above, the types
method is used to instruct the MorphTo
field what types of resources it may be associated with. Nova will use this information to populate the MorphTo
field's type selection menu on the creation and update pages:
MorphTo Title Attributes
When a MorphTo
field is shown on a resource creation / update page, the title attributes of the available resources will automatically be displayed.
#Nullable Relationships
If you would like your MorphTo
relationship to be nullable
, chain the nullable
method onto the field's definition:
use App\Nova\Post;
use App\Nova\Video;
use Laravel\Nova\Fields\MorphTo;
MorphTo::make('Commentable')->types([
Post::class,
Video::class,
])->nullable(),
#MorphToMany
The MorphToMany
field corresponds to a morphToMany
Eloquent relationship. For example, let's assume a Post
has a many-to-many polymorphic relationship with the Tag
model. We may add the relationship to our Post
Nova resource like so:
use Laravel\Nova\Fields\MorphToMany;
MorphToMany::make('Tags'),
#Pivot Fields
If your morphToMany
relationship interacts with additional "pivot" fields that are stored on the intermediate table of the many-to-many relationship, you may also attach those to your MorphToMany
Nova relationship. Once these fields are attached to the relationship field, they will be displayed on the related resource index.
For example, on our taggables
intermediate table, let's imagine we have a notes
field that contains some simple text notes about the relationship. We can attach this pivot field to the MorphToMany
field using the fields
method:
MorphToMany::make('Tags')
->fields(function ($request, $relatedModel) {
return [
Text::make('Notes'),
];
}),
Of course, it is likely we would also define this field on the inverse of the relationship. So, if we define the MorphToMany
field on the Post
resource, we would define it's inverse on the Tag
resource:
MorphToMany::make('Posts')
->fields(function ($request, $relatedModel) {
return [
Text::make('Notes'),
];
}),
Pivot fields must be defined
Don't forget to define the pivot fields inside your Model's relationship definition using the withPivot
method: https://laravel.com/docs/9.x/eloquent-relationships#retrieving-intermediate-table-columns
Since defining the field on both ends of the relationship can cause some code duplication, Nova allows you to pass an invokable object to the fields
method:
MorphToMany::make('Users')->fields(new TaggableFields),
In this example, the TaggableFields
class would be a simple, invokable class that returns the array of pivot fields:
<?php
namespace App\Nova;
use Laravel\Nova\Fields\Text;
class TaggableFields
{
/**
* Get the pivot fields for the relationship.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Model $relatedModel
* @return array
*/
public function __invoke($request, $relatedModel)
{
return [
Text::make('Notes'),
];
}
}
#Title Attributes
When a MorphToMany
field is shown on a resource creation / update page, a drop-down selection menu or search menu will display the "title" of the resource. For example, a Tag
resource may use the name
attribute as its title. Then, when the resource is shown in a MorphToMany
selection menu, that attribute will be displayed:
To customize the "title" attribute of a resource, you may define a title
property on the resource class:
public static $title = 'name';
Alternatively, you may override the resource's title
method:
/**
* Get the value that should be displayed to represent the resource.
*
* @return string
*/
public function title()
{
return $this->name;
}
#Searchable Relations
By default, when the BelongsTo
, MorphTo
, and MorphToMany
relationship fields are shown on a resource creation / update page, a simple drop-down selection menu will be displayed. However, this can quickly become cumbersome if a resource model has many records. For example, imagine a drop-down selection menu populated with over 10,000 users!
Instead of displaying a drop-down selection menu, you may mark your relationships as searchable
. When a relationship is marked as searchable
, a beautiful search input control will be displayed instead:
To mark a relationship as searchable
, chain the searchable
method onto the field's definition. If you would like to conditionally determine if a field should be searchable, you may pass a closure to the searchable
method:
BelongsTo::make('User')->searchable(),
BelongsTo::make('User')->searchable(function ($request) {
return true;
}),
You may also instruct the relation field to display the resource's subtitle by invoking the withSubtitles
method when defining the field:
BelongsTo::make('User')->searchable()->withSubtitles(),
#Limiting Relation Results
You can limit the number of results that are returned when searching the field by defining a relatableSearchResults
property on the class of the resource that you are searching for:
/**
* The number of results to display when searching for relatable resources without Scout.
*
* @var int|null
*/
public static $relatableSearchResults = 200;
#Creating Inline Relations
For convenience, When BelongsTo
or MorphTo
relationship fields are shown on a resource create or update page, you may create the related resource inline via a modal window without leaving the creation / update page:
To enable this functionality, invoke the showCreateRelationButton
method when defining the relationship field:
BelongsTo::make('User')->showCreateRelationButton(),
You may also pass a closure to the showCreateRelationButton
method to conditionally determine if inline resource creation should be enabled:
BelongsTo::make('User')->showCreateRelationButton(function ($request) {
//
}),
You may also create related many-to-many relationships from the "attach" and "update attached" pages. To enable this feature, invoke the showCreateRelationButton
when defining a BelongsToMany
or MorphToMany
relationship:
BelongsToMany::make('Roles')->showCreateRelationButton(),
To hide the inline creation button, invoke the hideCreateRelationButton
method when defining the relationship field:
BelongsTo::make('User')->hideCreateRelationButton(),
The inline relation creation process will respect any authorization policies you have defined.
Inline Creation Limitations
Inline relation creation only supports creating relations one level deep. This means you cannot trigger an additional inline creation modal inside an existing inline creation modal. Instead, you must select a resource that already exists.
Unless you like to live dangerously, any Nova fields that are displayed on the Nova creation / update pages will need some validation. Thankfully, it's a cinch to attach all of the Laravel validation rules you're familiar with to your Nova resource fields. Let's get started.
#Rules
#Attaching Rules
When defining a field on a resource, you may use the rules
method to attach validation rules to the field:
Text::make('Name')
->sortable()
->rules('required', 'max:255'),
Of course, if you are leveraging Laravel's support for validation rule objects , you may attach those to resources as well:
use App\Rules\ValidState;
Text::make('State')
->sortable()
->rules('required', new ValidState),
Additionally, you may use custom closure rules to validate your resource fields:
Text::make('State')
->sortable()
->rules('required', function($attribute, $value, $fail) {
if (strtoupper($value) !== $value) {
return $fail('The '.$attribute.' field must be uppercase.');
}
}),
#Creation Rules
If you would like to define rules that only apply when a resource is being created, you may use the creationRules
method:
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:255')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
#Update Rules
Likewise, if you would like to define rules that only apply when a resource is being updated, you may use the updateRules
method. If necessary, you may use resourceId
place-holder within your rule definition. This place-holder will automatically be replaced with the primary key of the resource being updated:
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:255')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
#After Validation Hooks
Nova also provides several methods that allow you to perform tasks after a resource has been validated, providing the opportunity to perform more custom validation before the resource is persisted to the database:
#The afterValidation
Method
The afterValidation
method will always be called after a resource has been validated during its creation or during an update. This method will be called before calling afterCreationValidation
or afterUpdateValidation
:
/**
* Handle any post-validation processing.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
protected static function afterValidation(NovaRequest $request, $validator)
{
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
}
#The afterCreationValidation
Method
The afterCreationValidation
method will be called after a resource that is being created has been validated:
/**
* Handle any post-creation validation processing.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
protected static function afterCreationValidation(NovaRequest $request, $validator)
{
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
}
#The afterUpdateValidation
Method
The afterUpdateValidation
method will be called after a resource that is being updated has been validated:
/**
* Handle any post-update validation processing.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
protected static function afterUpdateValidation(NovaRequest $request, $validator)
{
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
}
When Nova is accessed only by you or your development team, you may not need additional authorization before Nova handles incoming requests. However, if you provide access to Nova to your clients or a large team of developers, you may wish to authorize certain requests. For example, perhaps only administrators may delete records. Thankfully, Nova takes a simple approach to authorization that leverages many of the Laravel features you are already familiar with.
#Policies
To limit which users may view, create, update, or delete resources, Nova leverages Laravel's authorization policies . Policies are simple PHP classes that organize authorization logic for a particular model or resource. For example, if your application is a blog, you may have a Post
model and a corresponding PostPolicy
within your application.
When manipulating a resource within Nova, Nova will automatically attempt to find a corresponding policy for the model. Typically, these policies will be registered in your application's AuthServiceProvider
. If Nova detects a policy has been registered for the model, it will automatically check that policy's relevant authorization methods before performing their respective actions, such as:
viewAny
view
create
update
replicate
delete
restore
forceDelete
No additional configuration is required! So, for example, to determine which users are allowed to update a Post
model, you simply need to define an update
method on the model's corresponding policy class:
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can update the post.
*
* @param \App\Models\User $user
* @param \App\Models\Post $post
* @return mixed
*/
public function update(User $user, Post $post)
{
return $user->type == 'editor';
}
}
#Undefined Policy Methods
If a policy exists but is missing a method for a particular action, Nova will use the following default permission for each actions:
Policy Action | Default Permission |
---|---|
viewAny | Allowed |
view | Forbidden |
create | Forbidden |
update | Forbidden |
replicate | Fallback to create and update |
delete | Forbidden |
forceDelete | Forbidden |
restore | Forbidden |
add{Model} | Allowed |
attach{Model} | Allowed |
detach{Model} | Allowed |
runAction | Fallback to update |
runDestructiveAction | Fallback to delete |
So, if you have defined a policy, don't forget to define all of its relevant authorization methods so that the authorization rules for a given resource are explicit.
#Hiding Entire Resources
If you would like to hide an entire Nova resource from a subset of your dashboard's users, you may define a viewAny
method on the model's policy class. If no viewAny
method is defined for a given policy, Nova will assume that the user can view the resource:
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Post;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any posts.
*
* @param \App\Models\User $user
* @return mixed
*/
public function viewAny(User $user)
{
return in_array('view-posts', $user->permissions);
}
}
#Relationships
We have already learned how to authorize the typical view, create, update, and delete actions, but what about relationship interactions? For example, if you are building a podcasting application, perhaps you would like to specify that only certain Nova users may add comments to podcasts. Again, Nova makes this simple by leveraging Laravel's policies.
When working with relationships, Nova uses a simple policy method naming convention. To illustrate this convention, lets assume your application has Podcast
resources and Comment
resources. If you would like to authorize which users can add comments to a podcast, you should define an addComment
method on your podcast model's policy class:
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Podcast;
use Illuminate\Auth\Access\HandlesAuthorization;
class PodcastPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can add a comment to the podcast.
*
* @param \App\Models\User $user
* @param \App\Models\Podcast $podcast
* @return mixed
*/
public function addComment(User $user, Podcast $podcast)
{
return true;
}
}
As you can see, Nova uses a simple add{Model}
policy method naming convention for authorizing relationship actions.
#Authorizing Attaching / Detaching
For many-to-many relationships, Nova uses a similar naming convention. However, instead of add{Model}
, you should use an attach{Model}
/ detach{Model}
naming convention. For example, imagine a Podcast
model has a many-to-many relationship with the Tag
model. If you would like to authorize which users can attach "tags" to a podcast, you may add an attachTag
method to your podcast policy. In addition, you will likely want to define the inverse attachPodcast
on the tag policy:
<?php
namespace App\Policies;
use App\Models\Tag;
use App\Models\User;
use App\Models\Podcast;
use Illuminate\Auth\Access\HandlesAuthorization;
class PodcastPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can attach a tag to a podcast.
*
* @param \App\Models\User $user
* @param \App\Models\Podcast $podcast
* @param \App\Models\Tag $tag
* @return mixed
*/
public function attachTag(User $user, Podcast $podcast, Tag $tag)
{
return true;
}
/**
* Determine whether the user can detach a tag from a podcast.
*
* @param \App\Models\User $user
* @param \App\Models\Podcast $podcast
* @param \App\Models\Tag $tag
* @return mixed
*/
public function detachTag(User $user, Podcast $podcast, Tag $tag)
{
return true;
}
}
In the previous examples, we are determining if a user is authorized to attach one model to another. If certain types of users are never allowed to attach a given type of model, you may define a attachAny{Model}
method on your policy class. This will prevent the "Attach" button from displaying in the Nova UI entirely:
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Podcast;
use Illuminate\Auth\Access\HandlesAuthorization;
class PodcastPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can attach any tags to the podcast.
*
* @param \App\Models\User $user
* @param \App\Models\Podcast $podcast
* @return mixed
*/
public function attachAnyTag(User $user, Podcast $podcast)
{
return false;
}
}
Many To Many Authorization
When working with many-to-many relationships, make sure you define the proper authorization policy methods on each of the related resource's policy classes.
#Disabling Authorization
If one of your Nova resources' models has a corresponding policy, but you want to disable Nova authorization for that resource (thus allowing all actions), you may override the authorizable
method on the Nova resource:
/**
* Determine if the given resource is authorizable.
*
* @return bool
*/
public static function authorizable()
{
return false;
}
#Fields
Sometimes you may want to hide certain fields from a group of users. You may easily accomplish this by chaining the canSee
method onto your field definition. The canSee
method accepts a closure which should return true
or false
. The closure will receive the incoming HTTP request:
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Text::make('Name')
->sortable()
->canSee(function ($request) {
return $request->user()->can('viewProfile', $this);
}),
];
}
In the example above, we are using Laravel's Authorizable
trait's can
method on our User
model to determine if the authorized user is authorized for the viewProfile
action. However, since proxying to authorization policy methods is a common use-case for canSee
, you may use the canSeeWhen
method to achieve the same behavior. The canSeeWhen
method has the same method signature as the Illuminate\Foundation\Auth\Access\Authorizable
trait's can
method:
Text::make('Name')
->sortable()
->canSeeWhen('viewProfile', $this),
Authorization & The "Can" Method
To learn more about Laravel's authorization helpers and the can
method, check out the full Laravel authorization documentation .
#Index Filtering
You may notice that returning false
from a policy's view
method does not stop a given resource from appearing in the resource index. To filter models from the resource index query, you may override the indexQuery
method on the resource's class.
This method is already defined in your application's App\Nova\Resource
base class; therefore, you may simply copy and paste the method into a specific resource and then modify the query based on how you would like to filter the resource's index results:
/**
* Build an "index" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function indexQuery(NovaRequest $request, $query)
{
return $query->where('user_id', $request->user()->id);
}
#Relatable Filtering
If you would like to filter the queries that are used to populate relationship model selection menus, you may override the relatableQuery
method on your resource.
For example, if your application has a Comment
resource that belongs to a Podcast
resource, Nova will allow you to select the parent Podcast
when creating a Comment
. To limit the podcasts that are available in that selection menu, you should override the relatableQuery
method on your Podcast
resource:
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Laravel\Nova\Fields\Field $field
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableQuery(NovaRequest $request, $query)
{
return $query->where('user_id', $request->user()->id);
}
#Dynamic Relatable Methods
You can customize the "relatable" query for individual relationships by using a dynamic, convention based method name. For example, if your application has a Post
resource, in which posts can be tagged, but the Tag
resource is associated with different types of models, you may define a relatableTags
method to customize the relatable query for this relationship:
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Laravel\Nova\Fields\Field $field
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableTags(NovaRequest $request, $query)
{
return $query->where('type', 'posts');
}
If necessary, you may access the resource
and resourceId
for the request via the NovaRequest
instance that is passed to your method:
public static function relatableTags(NovaRequest $request, $query)
{
$resource = $request->route('resource'); // The resource type...
$resourceId = $request->route('resourceId'); // The resource ID...
return $query->where('type', $resource);
}
When a Nova resource depends on another resource via multiple fields, you will often assign the fields different names. In these situations, you should supply a third argument when defining the relationship to specify which Nova resource the relationship should utilize, since Nova may not be able to determine this via convention:
HasMany::make('Owned Teams', 'ownedTeams', Team::class),
BelongsToMany::make('Teams', 'teams', Team::class),
#Relationship Types
If necessary when customizing the "relatable" query, you may examine the field type to determine how to build the relationship query:
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Laravel\Nova\Fields\Field $field
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableTeams(NovaRequest $request, $query, Field $field)
{
if ($field instanceof BelongsToMany) {
// ...
}
return $query;
}
#Scout Filtering
If your application is leveraging the power of Laravel Scout for search, you may also customize the Laravel\Scout\Builder
query instance before it is sent to your search provider. To accomplish this, override the scoutQuery
method on your resource class:
/**
* Build a Scout search query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Scout\Builder $query
* @return \Laravel\Scout\Builder
*/
public static function scoutQuery(NovaRequest $request, $query)
{
return $query->where('user_id', $request->user()->id);
}
Searchable Columns
To define which resource fields are searchable, you may assign an array of database columns to the search
property of your resource class. This array includes the id
column by default:
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'id', 'title', 'content',
];
Scout Integration
If you are using Nova's Scout integration, the $search
property has no effect on your search results and may be ignored. You should manage the searchable columns within the Algolia or Meilisearch dashboard.
#Full-Text Indexes
Typically, Nova searches your database columns using simple LIKE
clauses. However, if you are using MySQL or Postgres, you may take advantage of any full-text indexes you have defined. To do so, you should define a searchableColumns
method on your Nova resource class instead of defining a $search
property.
The searchableColumns
method should return an array of columns that are searchable. You may include an instance of Laravel\Nova\Query\Search\SearchableText
within this array to instruct Nova to utilize your full-text indexes when querying a given column:
use Laravel\Nova\Query\Search\SearchableText;
/**
* Get the searchable columns for the resource.
*
* @return array
*/
public static function searchableColumns()
{
return ['id', new SearchableText('title')];
}
#Searching Relationships
Laravel Nova also allows you to search against a resource's related models. For example, imagine a Post
model that is related to a User
model via an author
relatonship. You may indicate that this relationship data should be considered when searching for users by returning an instance of Laravel\Nova\Query\Search\SearchableRelation
from your resource's searchableColumns
method.
If the searchableColumns
method does not exist on your resource, you should define it. Once the searchableColumns
method has been defined, you may remove the $search
property from your resource:
use Laravel\Nova\Query\Search\SearchableRelation;
/**
* Get the searchable columns for the resource.
*
* @return array
*/
public static function searchableColumns()
{
return ['id', new SearchableRelation('author', 'name')];
}
For convenience, you may define a relationship that should be searched by adding the field to your resource's $search
property using "dot notation":
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'id', 'author.name'
];
#MorphTo Relationships
"Morph to" relationships can be made searchable by returning an instance of Laravel\Nova\Query\Search\SearchableMorphToRelation
from your resource's searchableColumns
method. The SearchableMorphToRelation
class allows you to specify which types of morphed models should be searched:
use App\Nova\Post;
use Laravel\Nova\Query\Search\SearchableMorphToRelation;
/**
* Get the searchable columns for the resource.
*
* @return array
*/
public static function searchableColumns()
{
return ['id', new SearchableMorphToRelation('commentable', 'title', [Post::class])];
}
#Searching JSON Data
If the database table associated with your resource includes a column that contains a JSON string, you may instruct Nova to search within the JSON string by returning a Laravel\Nova\Query\Search\SearchableJson
instance from your resource's searchableColumns
method.
If the searchableColumns
method does not exist on your resource, you should define it. Once the searchableColumns
method has been defined, you may remove the $search
property from your resource:
use Laravel\Nova\Query\Search\SearchableJson;
/**
* Get the searchable columns for the resource.
*
* @return array
*/
public static function searchableColumns()
{
return ['id', new SearchableJson('meta->address->postcode')];
}
Nova not only allows you to search within specific resources and relationships, you may also globally search across all your resources using the global search input located within the top-navigation bar of the Nova administration panel:
Focusing Global Search
You can focus the global search input by pressing /
(forward slash) on your keyboard. Pressing ESC
(the escape key) will also close the global search input.
#Title / Subtitle Attributes
When a resource is shown within the search results, the results will display the "title" of the resource. For example, a User
resource may specify the name
attribute as its title. Then, when the resource is shown within the global search results, that attribute will be displayed.
To customize the "title" attribute of a resource, you may define a title
property on the resource class:
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'name';
Alternatively, you may override the resource's title
method:
/**
* Get the value that should be displayed to represent the resource.
*
* @return string
*/
public function title()
{
return $this->name;
}
Displaying Avatar in Search Results
You may also display resource's "avatar" next to the title in the search result by adding an Avatar field to the resource.
#Subtitles
You may also display a smaller "subtitle" attribute within the global search results. The subtitle will be placed directly under the title attribute. In this screenshot, you can see that the Post
resource's author is displayed as a subtitle, allowing quick identification of who wrote a given post:
To define a resource's subtitle, you should override the subtitle
method of the resource:
/**
* Get the search result subtitle for the resource.
*
* @return string
*/
public function subtitle()
{
return "Author: {$this->user->name}";
}
Eager Loading
If your subtitle accesses information on a related resource, you should consider adding the related resource to your resource's eager load array.
#Customization
#Limiting Global Search Results
You can limit the number of results that are returned via global search for a given resource by overriding the globalSearchResults
property on the resource:
/**
* The maximum number of results to include when searching globally.
*
* @var int
*/
public static $globalSearchResults = 5;
#Global Search Debounce
You can configure the debounce timing of the global search field using the Nova::globalSearchDebounce
method. Normally, this method should be called from within your application's NovaServiceProvider
:
Nova::globalSearchDebounce(1); // 1 second
#Custom Avatars / Covers
If you are building a custom field that you would like to serve as the "avatar image" / cover art for global search results, your field should implement the Laravel\Nova\Contracts\Cover
interface. This interface requires you to define a resolveThumbnailUrl
method, which should return the URL of your desired "cover art":
/**
* Resolve the thumbnail URL for the field.
*
* @return string|null
*/
public function resolveThumbnailUrl()
{
return 'https://www.example.com/avatar/'.md5(strtolower($this->value)).'?s=300';
}
#Disabling Global Search For A Resource
By default, all Nova resources are globally searchable; however, you may exclude a given resource from the global search by overriding the globallySearchable
property on the resource:
/**
* Indicates if the resource should be globally searchable.
*
* @var bool
*/
public static $globallySearchable = false;
#Disabling Global Search Globally
If you wish to completely disable global search inside of Nova, you can call the withoutGlobalSearch
method from your App/Providers/NovaServiceProvider
:
use Laravel\Nova\Nova;
/**
* Boot any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
Nova::withoutGlobalSearch();
}
By default, Nova searches your resources using the resource's database columns. However, this can become inefficient and lacks support for robust fuzzy matching capabilities provided by dedicated search engines.
For this reason, Nova integrates seamlessly with Laravel Scout . When the Laravel\Scout\Searchable
trait is attached to a model associated with a Nova resource, Nova will automatically begin using Scout when performing searches against that resource. There is no other configuration required.
#Customizing Scout Searches
If you would like to call methods on the Laravel\Scout\Builder
instance before it executes your search query against your search engine, you may override the scoutQuery
method on your resource:
use Laravel\Nova\Http\Requests\NovaRequest;
/**
* Build a Scout search query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Scout\Builder $query
* @return \Laravel\Scout\Builder
*/
public static function scoutQuery(NovaRequest $request, $query)
{
return $query;
}
#Limiting Scout Search Results
You can customize the amount of search results returned from your Scout search engine by defining the scoutSearchResults
property on the resource class that is associated with the Scout searchable model:
/**
* The number of results to display when searching the resource using Scout.
*
* @var int
*/
public static $scoutSearchResults = 200;
#Disabling Scout Search
You may disable Scout search support for a specific resource by defining a usesScout
method on the resource class. When Scout search support is disabled, simple database queries will be used to search against the given resource, even if the associated resource model includes the Scout Searchable
trait:
/**
* Determine if this resource uses Laravel Scout.
*
* @return bool
*/
public static function usesScout()
{
return false;
}
Nova filters are simple classes that allow you to scope your Nova index queries with custom conditions.
Filterable Fields
Before creating your own filters, you may want to check out filterable fields. Filterable fields can solve the filtering needs of most Nova installations without the need to write custom code.
#Select Filters
The most common type of Nova filter is the "select" filter, which allows the user to select a filter option from a drop-down selection menu:
You may generate a select filter using the nova:filter
Artisan command. By default, Nova will place newly generated filters in the app/Nova/Filters
directory:
php artisan nova:filter UserType
Each select filter generated by Nova contains two methods: apply
and options
. The apply
method is responsible for modifying the underlying Eloquent query to achieve the desired filter state, while the options
method defines the "values" the filter may have. Let's take a look at an example UserType
filter:
<?php
namespace App\Nova\Filters;
use Laravel\Nova\Filters\Filter;
use Laravel\Nova\Http\Requests\NovaRequest;
class UserType extends Filter
{
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
return $query->where('type', $value);
}
/**
* Get the filter's available options.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function options(NovaRequest $request)
{
return [
'Administrator' => 'admin',
'Editor' => 'editor',
];
}
}
The options
method should return an array of keys and values. The array's keys will be used as the "human-friendly" text that will be displayed in the Nova UI, while the array's values will be passed into the apply
method as the $value
argument. This filter defines two possible values: admin
and editor
.
As you can see in the example above, you may leverage the incoming $value
to modify your query, and the apply
method should return the modified query instance.
#Boolean Filters
Nova also supports "boolean" filters, which allow the user to select multiple filter options via a list of check-boxes:
You may generate a boolean filter using the nova:filter --boolean
Artisan command. By default, Nova will place newly generated filters in the app/Nova/Filters
directory:
php artisan nova:filter UserType --boolean
Each boolean filter generated by Nova contains two methods: apply
and options
. The apply
method is responsible for modifying the Eloquent query to achieve the desired filter state, while the options method defines the "values" the filter may have.
When building boolean filters, the $value
argument passed to the apply
method is an associative array containing the boolean value of each of your filter's options. Let's take a look at an example UserType
filter:
<?php
namespace App\Nova\Filters;
use Laravel\Nova\Filters\BooleanFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
class UserType extends BooleanFilter
{
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
// $value = ['admin' => true / false, 'editor' => true / false]
return $query->where('is_admin', $value['admin'])
->where('is_editor', $value['editor']);
}
/**
* Get the filter's available options.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function options(NovaRequest $request)
{
return [
'Administrator' => 'admin',
'Editor' => 'editor',
];
}
}
The options
method should return an array of keys and values. The array's keys will be used as the "human-friendly" text that will be displayed in the Nova UI. The array's values will be passed into the apply
method as the $value
argument. This filter defines two possible values: admin
and editor
.
As you can see in the example above, you may leverage the incoming $value
to modify your query. The apply
method should return the modified query instance.
#Date Filters
Nova also supports "date" filters, which allow the user to select the filter's value via a date selection calendar:
You may generate a date filter using the nova:filter --date
Artisan command. By default, Nova will place newly generated filters in the app/Nova/Filters
directory:
php artisan nova:filter BirthdayFilter --date
Each date filter generated by Nova contains one method: apply
. The apply
method is responsible for modifying the query to achieve the desired filter state.
When building date filters, the $value
argument passed to the apply
method is the string representation of the selected date. Let's take a look at an example BirthdayFilter
filter:
<?php
namespace App\Nova\Filters;
use Illuminate\Support\Carbon;
use Laravel\Nova\Filters\DateFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
class BirthdayFilter extends DateFilter
{
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
return $query->where('birthday', '<=', Carbon::parse($value));
}
}
As you can see in the example above, you may leverage the incoming $value
to modify your query. The apply
method should return the modified query instance.
#Filter Titles
If you would like to change the filter title that is displayed in Nova's filter selection menu, you may define a name
property on the filter class:
/**
* The displayable name of the filter.
*
* @var string
*/
public $name = 'Filter Title';
If the name of your filter needs to be dynamic, you should create a name
method on the filter class:
/**
* Get the displayable name of the filter.
*
* @return string
*/
public function name()
{
return 'Filter By '.$this->customProperty;
}
#Filter Default Values
If you would like to set the default value of a filter, you may define a default
method on the filter class:
/**
* The default value of the filter.
*
* @var string
*/
public function default()
{
return true;
}
#Dynamic Filters
There may be times when you want to create a dynamic filter which filters on columns that are determined at runtime, allowing you to reuse a filter class across multiple different resources and fields.
To accomplish this, you could pass the name of the filterable column into the filter's constructor. In addition to passing the column name that we want to filter on in the constructor, we'll also need to override the key
method of the filter so that Nova can uniquely identify this instance of the filter if multiple instances of this filter exist on the page. Let's take a look at an example TimestampFilter
filter:
<?php
namespace App\Nova\Filters;
use Illuminate\Support\Carbon;
use Laravel\Nova\Filters\DateFilter;
use Laravel\Nova\Http\Requests\NovaRequest;
class TimestampFilter extends DateFilter
{
/**
* The column that should be filtered on.
*
* @var string
*/
protected $column;
/**
* Create a new filter instance.
*
* @param string $column
* @return void
*/
public function __construct($column)
{
$this->column = $column;
}
/**
* Apply the filter to the given query.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(NovaRequest $request, $query, $value)
{
return $query->where($this->column, '<=', Carbon::parse($value));
}
/**
* Get the key for the filter.
*
* @return string
*/
public function key()
{
return 'timestamp_'.$this->column;
}
}
Then, as discussed, you should pass the name of the column you wish to filter on:
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [
new Filters\TimestampFilter('created_at'),
new Filters\TimestampFilter('deleted_at'),
];
}
Registering Filters
Once you have defined a filter, you are ready to attach it to a resource. Each resource generated by Nova contains a filters
method. To attach a filter to a resource, you should simply add it to the array of filters returned by this method:
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [
new Filters\UserType,
];
}
While similar to filters, Nova lenses allow you to fully customize the underlying resource Eloquent query. For example, you may want to list of all your application's users sorted by their total lifetime revenue:
Creating such a list may require you to join to additional tables and perform aggregate functions within the query. If it sounds complicated, don't worry - this is exactly the type of situation lenses are designed to solve.
#Overview
To create a lens, you may use the nova:lens
Artisan command. By default, Nova will place newly generated lenses in the app/Nova/Lenses
directory:
php artisan nova:lens MostValuableUsers
Each lens generated by Nova contains several methods. However, the two methods we are currently concerned with are the query
and fields
methods. The query
method is responsible for building the Eloquent query that is needed to retrieve the desired data, while the fields
method returns an array of fields that should be displayed when viewing the lens.
To learn more, let's take a look at a complete lens definition that displays users and their lifetime revenue. As you can see in the example below, the query
method will take advantage of the withFilters
and withOrdering
methods offered by the LensRequest
in order to instruct Nova to also apply any selected filters and ordering constraints to the query:
<?php
namespace App\Nova\Lenses;
use Illuminate\Support\Facades\DB;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\LensRequest;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Lenses\Lens;
class MostValuableUsers extends Lens
{
/**
* Get the query builder / paginator for the lens.
*
* @param \Laravel\Nova\Http\Requests\LensRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return mixed
*/
public static function query(LensRequest $request, $query)
{
return $request->withOrdering($request->withFilters(
$query->select(self::columns())
->join('licenses', 'users.id', '=', 'licenses.user_id')
->orderBy('revenue', 'desc')
->groupBy('users.id', 'users.name')
));
}
/**
* Get the columns that should be selected.
*
* @return array
*/
protected static function columns()
{
return [
'users.id',
'users.name',
DB::raw('sum(licenses.price) as revenue'),
];
}
/**
* Get the fields available to the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make('ID', 'id'),
Text::make('Name', 'name'),
Number::make('Revenue', 'revenue', function ($value) {
return '$'.number_format($value, 2);
}),
];
}
/**
* Get the cards available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [];
}
/**
* Get the URI key for the lens.
*
* @return string
*/
public function uriKey()
{
return 'most-profitable-users';
}
}
As you can see in the example above, the query
method has full control of the Eloquent query used to retrieve the lens data. The fields
method may leverage any of Nova's fields in order to appropriately display the data retrieved by the query.
Columns Method
In this example, the columns
method has been extracted from the query
method for readability. It is not "required" and is not a "feature" of lenses.
Lens Column Selection
When writing your lens query, you should always try to include the resource's ID as a selected column. If the ID is not included, Nova will not be able to display the "Select All Matching" option for the lens. In addition, the resource deletion menu will not be available.
#Lens Polling
Nova can automatically fetch the latest records for a lens at a specified interval via polling. To enable polling, overwrite the polling
property of your lens class:
/**
* Indicates whether the lens should automatically poll for new records.
*
* @var bool
*/
public static $polling = true;
To customize the polling interval, you may override the pollingInterval
property on your lens class. The pollingInterval
defines the number of seconds Nova should wait before fetching new records:
/**
* The interval (in seconds) at which Nova should poll for new lens.
*
* @var int
*/
public static $pollingInterval = 5;
#Toggling Lens Polling
By default, when lens polling is enabled, there is no way to disable polling once the page has loaded. However, you can instruct Nova to display a start / stop polling toggle button by defining a showPollingToggle
property on your lens class as true
:
/**
* Indicates whether to show the polling toggle button inside Nova.
*
* @var bool
*/
public static $showPollingToggle = true;
#Lens Filters
Each Nova lens also contains a filters
method. This method allows you to attach any of your existing filters to the lens:
use App\Nova\Filters\UserType;
/**
* Get the filters available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [new UserType];
}
#Lens Actions
Each Nova lens also contains an actions
method. This method allows you to attach any of your existing actions to the lens:
use App\Nova\Actions\Export;
/**
* Get the actions available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [new Export];
}
Resource Actions
By default, lenses will inherit the actions of their associated resource. However, you may override the actions
method on the lens to define a custom set of actions that should be available to the lens.
#Lens Metrics
Each Nova lens also contains a cards
method. This method allows you to attach any of your existing metrics to the lens:
use App\Nova\Metrics\NewUsers;
/**
* Get the cards available for the lens.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [new NewUsers];
}
Once you have defined a lens, you are ready to attach it to a resource. Each resource generated by Nova contains a lenses
method. To attach a lens to a resource, you should simply add it to the array of lenses returned by this method:
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [
new Lenses\MostValuableUsers,
];
}
Alternatively, you may use the make
method to instantiate your lens. Any arguments passed to the make
method will be passed to the constructor of your lens:
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [
Lenses\MostValuableUsers::make(),
];
}
#Authorization
If you would like to only expose a given lens to certain users, you may invoke the canSee
method when registering your lens. The canSee
method accepts a closure which should return true
or false
. The closure will receive the incoming HTTP request:
use App\Models\User;
use Laravel\Nova\Http\Requests\NovaRequest;
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [
(new Lenses\MostValuableUsers)->canSee(function ($request) {
return $request->user()->can(
'viewValuableUsers', User::class
);
}),
];
}
In the example above, we are using Laravel's Authorizable
trait's can
method on our User
model to determine if the authorized user is authorized for the viewValuableUsers
action. However, since proxying to authorization policy methods is a common use-case for canSee
, you may use the canSeeWhen
method to achieve the same behavior. The canSeeWhen
method has the same method signature as the Illuminate\Foundation\Auth\Access\Authorizable
trait's can
method:
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [
(new Lenses\MostValuableUsers)->canSeeWhen(
'viewValuableUsers', User::class
),
];
}
Comments
Post a Comment