Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Scoutify Laravel Package

matheusmarnt/scoutify

Scoutify adds a production-ready ⌘K/Ctrl+K global search modal to Laravel. Powered by Scout + Livewire, it searches across multiple Eloquent models, groups results by type, auto-discovers Searchable models, and stores recent searches in session.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require matheusmarnt/scoutify
    php artisan scoutify:install
    

    Follow prompts to select Scout driver (Meilisearch, Algolia, Typesense, or Database).

  2. Register Models:

    php artisan scoutify:searchable
    

    Select models to make globally searchable (or use --all flag). The command auto-edits model files.

  3. Import Data:

    php artisan scoutify:import
    
  4. Add UI Components: Place these in your layout (after $slot):

    <x-scoutify::gs.trigger class="hidden lg:inline-flex" />
    <x-scoutify::gs.trigger-mobile />
    <livewire:scoutify::modal />
    

First Use Case

Trigger the search modal with ⌘K (Mac) or Ctrl+K (Windows/Linux). Type a query to see grouped results by model type (e.g., "Articles," "Users"). Click a result to navigate to its route.


Implementation Patterns

Workflows

  1. Model Registration:

    • Use scoutify:searchable to auto-register models under app/Models/.
    • Override trait methods (e.g., globalSearchTitle(), globalSearchUrl()) for custom behavior.
    • Example:
      public function globalSearchTitle(): string { return $this->name; }
      public function globalSearchUrl(): string { return route('users.show', $this); }
      
  2. Query Customization:

    • Extend globalSearchBuilder() for model-specific filters:
      public function globalSearchBuilder(Builder $builder, string $query): Builder
      {
          return $builder->where('status', 'published');
      }
      
  3. Authorization:

    • Implement HasGlobalSearchVisibility for fine-grained access control:
      public function globalSearchVisibility(): VisibilityRule
      {
          return VisibilityRule::make()
              ->visibleToGuests()
              ->orWhenAuthenticated()
                  ->policy('view')
                  ->orPermission('edit-content');
      }
      
  4. File Previews:

    • Add HasGlobalSearchPreview to models with downloadable files:
      public function globalSearchPreview(): ?PreviewDto
      {
          return PreviewDto::fromDisk('documents', $this->file_path);
      }
      
    • Listen for scoutify:download events in JavaScript to handle downloads.
  5. Programmatic Triggers:

    • Open the modal via Alpine.js:
      <button x-data @click="$dispatch('scoutify:open')">Search</button>
      
    • Or in Livewire:
      <button wire:click="$dispatchTo('scoutify::modal', 'scoutify:open')">Search</button>
      

Integration Tips

  • Scout Drivers: Configure config/scout.php for your chosen driver (e.g., Meilisearch, Algolia).
  • Icon Packs: Install Blade Icons packs (e.g., ri-*, tabler-*) for custom icons:
    composer require andreiio/blade-remix-icon
    
    Use in models:
    public static function globalSearchIcon(): string { return 'ri-customer-service-2-fill'; }
    
  • Dark Mode: Scoutify supports dark mode out-of-the-box. Ensure your layout includes Tailwind’s dark mode classes.
  • Translations: Override default translations in resources/lang/{locale}/scoutify.php.

Gotchas and Tips

Pitfalls

  1. Modal Placement:

    • Error: Modal fails to mount if placed inside a collapsed container (e.g., sidebar, drawer).
    • Fix: Place <livewire:scoutify::modal /> at the root layout level, outside conditionally rendered sections.
  2. Meilisearch Substring Search:

    • Issue: Meilisearch uses prefix search by default. Queries like "ano" won’t match "Mariano".
    • Fix: Override globalSearchBuilder() to configure attributesToSearchOn or switch to the database driver for LIKE-based search.
  3. Missing Scout Driver Packages:

    • Error: scoutify:install may fail if driver packages aren’t installed.
    • Fix: Manually install required packages (e.g., meilisearch/meilisearch-php) or let the installer handle it.
  4. Authorization Conflicts:

    • Issue: Custom VisibilityRule logic may override global settings (secure/permissive).
    • Fix: Test with php artisan scoutify:test-visibility to debug visibility rules.
  5. File Preview Permissions:

    • Issue: Preview pane shows "Unauthorized" for files belonging to other users.
    • Fix: Ensure the model implements HasGlobalSearchVisibility and the user passes the visibility check.

Debugging

  • Check Scout Index:

    php artisan scout:flush
    php artisan scout:import
    

    Verify records are indexed with:

    php artisan scout:search "test query"
    
  • Livewire Logs: Enable Livewire logging in config/livewire.php:

    'log' => env('LIVEWIRE_LOG', true),
    

    Check storage/logs/livewire.log for modal initialization errors.

  • Visibility Testing: Use the scoutify:test-visibility command to simulate user roles/permissions:

    php artisan scoutify:test-visibility User --role=admin
    

Extension Points

  1. Custom Theming: Override Tailwind classes via the fluent theme API:

    Scoutify::theme()
        ->modalBackground('bg-gray-800')
        ->resultItemPadding('py-2 px-4');
    
  2. Event Listeners: Extend functionality by listening to Scoutify events:

    • scoutify.searching: Triggered when a query is executed.
    • scoutify.result.click: Fired when a user clicks a result.
    • scoutify.download: Handle file downloads (see above).
  3. Dynamic Model Registration: Register models programmatically at runtime:

    Scoutify::types()->register(Article::class);
    Scoutify::types()->register(User::class);
    
  4. Query Hooks: Modify search behavior globally via service providers:

    Scoutify::query()->hook(function (Builder $builder, string $query) {
        return $builder->where('deleted_at', null);
    });
    
  5. Icon Auto-Detection: Override the default icon prefix in a service provider:

    Scoutify::types()->iconPrefix('tabler-');
    

Configuration Quirks

  • Recent Searches: Persisted to session by default. Clear history via:

    Scoutify::recent()->clear();
    

    Or configure max items in config/scoutify.php:

    'recent' => [
        'max_items' => 10,
    ],
    
  • Dark Mode: Ensure your layout includes:

    <html class="dark">
    

    And Tailwind’s dark mode classes (e.g., dark:bg-gray-800).

  • Spatie Permissions: Required for ->permission() and ->role() rules. Scoutify auto-detects the package but fails closed if missing. Install with:

    composer require spatie/laravel-permission
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony