PHPnews.io

★ How to add a spotlight-like search field to your Laravel app

Written by murze.be / Original link on May. 19, 2022

As developers, we tend to like shortcuts to speed up our workflow. One of the tools I'm using to speed up doing stuff on my Mac is Raycast. It offers a lovely command palette that allows opening apps and URLs, working with clipboard history, and much more.

raycast.png

Wouldn't it be nice to also add such a command palette to a Laravel app? This way, power users of your app can get around quickly and perform small tasks without having to click around.

The good news is that there's already a package to add such a thing: Spotlight by Philo Hermans. In this blog post, I'd like to show how we use this fantastic package at Oh Dear.

How users can use the command palette

Oh Dear is an all-in solution for monitoring your website. It is simple to use, but because we offer a lot of checks, our UI is quite extensive: there are many screens.

To make navigating the service a little bit easier, we added the command palette provided by the Spotlight package.

When you log in Oh Dear (you can create a trial account if you don't already have one), you'll notice a new little search field in the header.

search.png

When you click that, it opens up a nice command palette. You can use this to navigate to anywhere in our service quickly.

palette.png

If you want to go to the performance graph of your site, just type "performance", ...

command.png

... and type the site's name you want to see the performance results of.

site.png

site2.png

When selecting the site, you'll get taken to the performance results.

performance.jpg

In many cases, this is much faster than clicking on the site and then going to the right check results yourself.

The command palette has commands to navigate to almost anywhere in our UI, and tasks like logging out or switching teams.

other.png

Using the Spotlight package

Using the Spotlight package is simple. You can install it with Composer. Just keep in mind that it requires Livewire.

composerrequirewire-elements/spotlight

Next, you should include the provided Livewire component in your view.

<html>
<body>
<!--content-->

@livewire('livewire-ui-spotlight')
</body>
</html>

With that out of the way, you can start writing Spotlight command classes. Here is what our command class for logging out the user looks like.

namespaceApp\Support\SpotlightCommands;

useApp\Domain\Site\Models\Site;
useLivewireUI\Spotlight\Spotlight;
useLivewireUI\Spotlight\SpotlightCommand;

classLogoutCommandextendsSpotlightCommand
{
publicstring $name ='Logout';

publicstring $description ='Log out from your account';

publicfunctionexecute(Spotlight $spotlight, Site $site):void
    {
        $spotlight->redirect(route('logout'));
    }
}

The name property contains the string a user needs to type to invoke the command. The Execute function will get executed when the user picks that function. As mentioned in the docs, Spotlight commands should be registered in the config file or a service provider.

And that is all you need to do to offer a command. Pretty cool, right?

Let me show you one more command. In the previous section, you saw that when a user wants to see a site's performance results, we ask the user which site they want to see it. Such a question is called "a dependency" in Spotlight parlance.

Here's what that ViewPerformanceCheckResultsCommand looks like.

namespaceApp\Support\SpotlightCommands;

useApp\Domain\Site\Models\Site;
useIlluminate\Support\Collection;
useLivewireUI\Spotlight\Spotlight;
useLivewireUI\Spotlight\SpotlightCommand;
useLivewireUI\Spotlight\SpotlightCommandDependencies;
useLivewireUI\Spotlight\SpotlightCommandDependency;
useLivewireUI\Spotlight\SpotlightSearchResult;

classViewPerformanceCheckResultsCommandextendsSpotlightCommand
{
protectedstring $name ='Performance check results';

publicfunctiondependencies():?SpotlightCommandDependencies
    {
returnSpotlightCommandDependencies::collection()
->add(
SpotlightCommandDependency::make('site')
->setPlaceholder('For which site?')
->setType(SpotlightCommandDependency::SEARCH)
            );
    }

publicfunctionsearchSite(string $query):Collection
    {
returncurrentTeam()
->sites()
->search($query)
->limit(15)
->get()
->map(function (Site $site) {
returnnewSpotlightSearchResult(
                    $site->id,
                    $site->name,
''
                );
            });
    }

publicfunctionexecute(Spotlight $spotlight, Site $site):void
    {
        $url =route('check.performance', $site);

        $spotlight->redirect($url);
    }
}

So in dependencies you can define which follow up questions should be asked when a user picks this command. If it is a question that involves searching something, you should add a function called search<name of the dependency>, in our case, searchSite.

This will let Spotlight display that "For which site?" question. Users can type the name of the site. When a site gets picked, the chosen site will be passed to execute, and we can redirect the user to the correct route.

Because we have a lot of spotlight commands that will ask for a site and then redirect to a page, I extracted most of the logic away in an abstract SiteSpotlightCommand.

namespaceApp\Support\SpotlightCommands;

useApp\Domain\Site\Models\Site;
useApp\Support\SpotlightCommands\Concerns\HasSiteDependency;
useIlluminate\Support\Collection;
useLivewireUI\Spotlight\Spotlight;
useLivewireUI\Spotlight\SpotlightCommand;
useLivewireUI\Spotlight\SpotlightCommandDependencies;
useLivewireUI\Spotlight\SpotlightCommandDependency;
useLivewireUI\Spotlight\SpotlightSearchResult;

abstractclassSiteSpotlightCommandextendsSpotlightCommand
{
publicfunctiondependencies():?SpotlightCommandDependencies
    {
returnSpotlightCommandDependencies::collection()
->add(
SpotlightCommandDependency::make('site')
->setPlaceholder('For which site?')
->setType(SpotlightCommandDependency::SEARCH)
            );
    }

publicfunctionsearchSite(string $query):Collection
    {
returncurrentTeam()
->sites()
->search($query)
->limit(15)
->get()
->map(function (Site $site) {
returnnewSpotlightSearchResult(
                    $site->id,
                    $site->label,
'',
                );
            });
    }

publicfunctionexecute(Spotlight $spotlight, Site $site):void
    {
        $url =$this->getUrl($site);

        $spotlight->redirect($url);
    }

abstractprotectedfunctiongetUrl(Site $site):string;
}

This is what is left over in the ViewPerformanceCheckResultsCommand.

namespaceApp\Support\SpotlightCommands;

useApp\Domain\Site\Models\Site;

classViewPerformanceCheckResultsCommandextendsSiteSpotlightCommand
{
protectedstring $name ='Performance check results';

protectedfunctiongetUrl(Site $site):string
    {
returnroute('check.performance', $site);
    }
}

With this set up, adding other spotlight commands to navigate to a different part of the UI becomes a breeze.

In closing

I hope you like this little tour of how we are using the Spotlight package at Oh Dear.

Philo has done an excellent job on the package. If you want to see more, check out his talk he gave at the Laravel Worldwide Meetup.

murze

« Creating your own color system with Tailwind - What's New and Changed in phpMyAdmin 5.2 »