Publishing Your First Laravel Nova Tool

Laravel Nova gives us an easy way to manage the data in our Laravel applications. With a few lines of code, you can define "resources" corresponding to your Eloquent models, and Nova will generate beautiful interfaces for common CRUD tasks. Its real power, however, lies in the customization options.

There are four types of custom Nova elements you can create:

  • Tools. These will add a new entry to your Nova sidebar and give you complete control over the Vue component it renders.
  • Resource Tools. Similar to Tools, but will be displayed on a particular resource's page.
  • Cards. Miniature tools that are shown at the top of your dashboard. Nova ships with some cards to view metrics out-of-the-box.
  • Fields. These tell Nova how to display and edit the properties on your application's resources. Nova ships with a lot of these too, but you can also create your own.

I've always wanted to sort and filter the output of php artisan route:list. It seemed like a Nova tool would be perfect for this job—so I created a tool that adds an entry to your Nova sidebar and displays a nicely-formatted table of your Laravel routes. In this post, I'll walk through the steps I took to create Nova Route Viewer, my first Nova package; you can view the full source code on GitHub.

These steps will differ slightly if you're creating a resource tool, card, or field Nova package, but the process is more or less the same.

Step 0: Pull Laravel Nova into your application

If you already have Nova installed, you can skip this step.

The very first step is to add Nova to your existing Laravel application. Following the official installation instructions:

  1. Download and unzip the latest Nova release into a nova folder in the root directory of your Laravel application.
  2. Tell Composer where to find Nova using a local 'path' repository:
"repositories": [
    {
        "type": "path",
        "url": "./nova"
    }
],
  1. Add laravel/nova to the require section of your composer.json file:
"require": {
    "php": "^7.1.3",
    "fideloper/proxy": "^4.0",
    "laravel/framework": "5.6.*",
    "laravel/nova": "*"
},
  1. Update your dependencies:
composer update

Note: If you get an error about Composer being unable to resolve laravel/nova, you may need to add "minimum-stability": "dev" and "prefer-stable": true to your composer.json file.

  1. Install Nova using Artisan:
php artisan nova:install
php artisan migrate

Make sure App\Providers\NovaServiceProvider was added to the providers array in config/app.php. If it wasn't, add it manually.

You should now see Nova installed in your application if you browse to http://<your-app>/nova.

Step 1: Initialize your tool

php artisan nova:tool sbine/route-viewer

This command will create your tool in nova-components/<YourTool>. It will then prompt you to install its NPM dependencies, compile them, and update your parent application's composer.json with the new tool dependency. I recommend using the default answers for all prompts—but don't worry, you can always run this command again later.

It will also symlink your tool's directory to your vendor directory, as though you had installed it via Composer.

Step 2: Register your tool

Once your tool scaffolding has been created, you need to tell your parent application to activate it. Open app/Providers/NovaServiceProvider and add your tool to the tools() method. It will be in the src directory under the name you used in the nova:tool command.

use Sbine\RouteViewer\RouteViewer;

public function tools()
{
    return [
        new RouteViewer,
    ];
}

Note that you need to new up your tool in this array, not use RouteViewer::class.

Your tool should now be available in Nova, pre-populated with a fancy landing page.

Laravel Nova tool default landing page

Step 3: Write some code

You'll find all the code for your tool in the folder nova-components/<YourTool>.

Every tool ships with a routes/api.php file for you to register your own routes. Everything registered here will be prefixed with /nova-vendor/<your-tool-name>/.

I need a route to return all registered routes in the Laravel application:

Route::get('/routes', function () {
    $routes = collect(Route::getRoutes())->map(function ($route) {
        return [
            'uri' => $route->uri,
            'as' => $route->action['as'] ?? '',
            'methods' => $route->methods,
            'action' => $route->action['uses'] ?? '',
            'middleware' => $route->action['middleware'] ?? [],
        ];
    });

    return response()->json($routes);
});

Now the endpoint /nova-vendor/route-viewer/routes will return a list of all routes. For even better code organization you can create controllers in src/Http/Controllers, just like in a regular Laravel application.

Your tool also ships with a Vue component, resources/js/components/Tool.vue. We'll edit that next, so this is a good time to start compiling your frontend assets as you save them. Run this command from your tool's directory:

npm run watch

I want to display a table of all registered routes using the endpoint I just created, so I'll update Tool.vue accordingly:

<template>
    <div>
        <heading class="mb-6">Route Viewer</heading>

        <card>
            <div class="overflow-hidden overflow-x-auto relative">
              <table>
                <thead>
                  <tr>
                    <th>Route</th>
                    <th>Name</th>
                    <th>Methods</th>
                    <th>Action</th>
                    <th>Middleware</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="route in routes">
                    <td>{{ route.uri }}</td>
                    <td>{{ route.as }}</td>
                    <td>{{ route.methods }}</td>
                    <td>{{ route.action }}</td>
                    <td>{{ route.middleware }}</td>
                  </tr>
                </tbody>
              </table>
            </div>
        </card>
    </div>
</template>
<script>
export default {
    data() {
        return {
            routes: [],
        }
    },
    mounted() {
        this.getRoutes();
    },
    methods: {
        getRoutes() {
            Nova.request().get('/nova-vendor/route-viewer/routes').then(response => {
                this.routes = response.data;
            });
        },
    },
}
</script>

While Axios is available globally in Nova, the Nova.request() decorator provides the same functions and handles errors and redirects for you automatically, so I recommend using it.

Next we'll replace the generic sidebar icon Nova generated for our tool. You can find the current SVG icon in resources/views/navigation.php and replace it with an icon of your choosing.

Our tool now looks like this: Laravel Nova tool with custom sidebar icon

The last step to prepare for release is to compile our frontend assets for production:

npm run prod

By default, the /dist folder is excluded from your repository. You'll want to make sure it's included so you can push your compiled assets along with your tool. To do this, remove /dist from your .gitignore file.

Step 4: Upload your tool to GitHub

Now that the code is all done we can publish it. Create a new repository on GitHub, and push your code to it.

Since I created my tool locally before creating it on GitHub, I'll need to manually initialize a git repository and tell it what remote to use before I can commit anything:

git init
git remote add origin git@github.com:<your-name>/<your-repository>.git
git add .
git commit -am "Initial commit."
git push origin master

Step 5: Make a release

It's a good idea to tag versions of your package so your users can rely on a specific version, rather than whatever's on master. To tag a version corresponding to your latest commit, use git tag <version>:

git tag 0.0.1 && git push --tags

Alternatively, you can do this through the GitHub UI in the 'Releases' section of your repository.

Step 6: Submit it to Packagist.org

In order to make your tool installable with Composer you'll need to register it on Packagist.org. Log in, go to 'Submit', and enter your repository URL.

Submitting a package to Packagist.org

Your Packagist.org entry won't automatically update when you push new code by default. To do that, you'll need to create a GitHub Service Hook. You can also manually update it using the 'Update' button, but I recommend automating this process.

Step 7: Submit it to NovaPackages.com (optional)

NovaPackages.com is a collection of community-built Laravel Nova packages, brought to you by Tighten. I recommend submitting your package there so Nova users can find it easily.

Log in with GitHub, go to 'Packages' -> 'Create Package', and fill out the form to list your package.

Step 8: Install it in a new Nova app

We're all done! Now you can install your package in any Nova app.

composer require sbine/route-viewer

Just like in step 2, you'll need to register the tool in your app/Providers/NovaServiceProvider in order to use it.

For more information on Nova tools and other types of customization, take a look at the official documentation. To discover more Nova tools you can install, check out our NovaPackages.com project.