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

Laravel Auto Discoverer Laravel Package

spatie/laravel-auto-discoverer

Fast, cached discovery of PHP classes, interfaces, traits, and enums based on conditions. Scan directories to find structures implementing an interface, using attributes, extending classes, and more—ideal for auto-registration and production-ready performance.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/php-structure-discoverer
    

    For Laravel, use the dedicated package:

    composer require spatie/laravel-auto-discoverer
    
  2. First Use Case: Discover all classes implementing Illuminate\Contracts\Support\Arrayable in your app:

    use Spatie\StructureDiscoverer\Discover;
    
    $arrayableClasses = Discover::in(app_path())
        ->classes()
        ->implementing(\Illuminate\Contracts\Support\Arrayable::class)
        ->get();
    
  3. Where to Look First:


Implementation Patterns

Core Workflows

  1. Discovering Classes by Traits/Interfaces:

    // Find all classes using a custom trait
    Discover::in(base_path('app'))
        ->classes()
        ->using(\App\Traits\HasSoftDeletes::class)
        ->get();
    
    // Find all classes implementing a custom interface
    Discover::in(base_path('app'))
        ->classes()
        ->implementing(\App\Contracts\Serializable::class)
        ->get();
    
  2. Filtering by Namespace:

    // Discover only in the 'App\Models' namespace
    Discover::in(base_path('app/Models'))
        ->classes()
        ->get();
    
  3. Caching Results (Laravel):

    // Cache results for 1 hour
    Discover::in(app_path())
        ->classes()
        ->implementing(\Arrayable::class)
        ->cacheFor(now()->addHour())
        ->get();
    
  4. Integration with Laravel Service Providers:

    // Bootstrapping discovered classes in a provider
    public function boot()
    {
        $listeners = Discover::in(app_path())
            ->classes()
            ->using(\Illuminate\Contracts\Event\Dispatcher::class)
            ->get();
    
        foreach ($listeners as $listener) {
            event(new \App\Events\DiscoveredClass($listener));
        }
    }
    
  5. Dynamic Discovery in Controllers/Middleware:

    // Middleware to inject discovered services
    public function handle($request, Closure $next)
    {
        $services = Discover::in(app_path('Services'))
            ->classes()
            ->implementing(\App\Contracts\ServiceContract::class)
            ->get();
    
        app()->singleton(\App\Contracts\ServiceContract::class, $services[0]);
    
        return $next($request);
    }
    
  6. Discovering Enums/Interfaces:

    // Find all enums in the app
    Discover::in(base_path('app'))
        ->enums()
        ->get();
    
    // Find all interfaces with a specific method
    Discover::in(base_path('app'))
        ->interfaces()
        ->withMethod('handle')
        ->get();
    

Gotchas and Tips

Pitfalls

  1. Cache Invalidation:

    • Cached results won’t auto-update when new classes are added. Clear cache manually:
      php artisan cache:clear
      
    • Use cacheFor() sparingly in development to avoid stale data.
  2. Namespace Scope:

    • Discover::in() is relative to the current working directory by default. Always use absolute paths (e.g., base_path('app')) for consistency.
    • Example of silent failure:
      // ❌ Fails silently if 'app' directory doesn't exist
      Discover::in('app')->classes()->get();
      
      Fix: Use base_path('app') or add error handling:
      try {
          Discover::in('nonexistent')->classes()->get();
      } catch (\Spatie\StructureDiscoverer\Exceptions\DirectoryNotFound $e) {
          Log::error($e->getMessage());
      }
      
  3. Performance:

    • Avoid deep recursion in large codebases. Limit discovery to specific directories:
      // ❌ Slow for monorepos
      Discover::in(base_path())->classes()->get();
      
      // ✅ Faster
      Discover::in(base_path('app'))->classes()->get();
      
    • Use --optimize in Laravel for faster autoloading to reduce discovery time.
  4. False Positives in Interface/Trait Detection:

    • The package uses PHP’s reflection, which can misidentify abstract classes or dynamically generated classes.
    • Workaround: Combine with instanceof checks at runtime:
      $classes = Discover::in(app_path())
          ->classes()
          ->implementing(\Arrayable::class)
          ->get();
      
      $validClasses = array_filter($classes, fn($class) => class_implements($class, \Arrayable::class));
      
  5. Laravel Package Auto-Discovery:

    • If using spatie/laravel-auto-discoverer, ensure your composer.json includes:
      "extra": {
          "laravel": {
              "discover": ["spatie/laravel-auto-discoverer"]
          }
      }
      
    • Run composer dump-autoload after adding new discoverable classes.

Tips

  1. Leverage for Dynamic Configuration:

    // Auto-register all event listeners
    $listeners = Discover::in(app_path('Listeners'))
        ->classes()
        ->implementing(\Illuminate\Contracts\Queue\ShouldQueue::class)
        ->get();
    
    foreach ($listeners as $listener) {
        event(new \App\Events\QueueListenerRegistered($listener));
    }
    
  2. Combine with Laravel’s ClassLoader:

    // Discover and instantiate classes dynamically
    $services = Discover::in(app_path('Services'))
        ->classes()
        ->implementing(\App\Contracts\ServiceContract::class)
        ->get();
    
    foreach ($services as $service) {
        app()->bind(\App\Contracts\ServiceContract::class, $service);
    }
    
  3. Debugging:

    • Use ->toArray() to inspect discovery results:
      $result = Discover::in(app_path())
          ->classes()
          ->implementing(\Arrayable::class)
          ->toArray();
      
    • Enable verbose output for troubleshooting:
      Discover::setVerbose(true);
      
  4. Extending Functionality:

    • Create custom discovery filters by extending the Spatie\StructureDiscoverer\Filters\Filter class:
      class HasCustomMethodFilter extends Filter
      {
          public function __invoke($class): bool
          {
              return in_array('customMethod', get_class_methods($class));
          }
      }
      
      Usage:
      Discover::in(app_path())
          ->classes()
          ->filter(new HasCustomMethodFilter())
          ->get();
      
  5. CI/CD Optimization:

    • Cache discovery results in CI pipelines to avoid redundant scans:
      if (app()->environment('ci')) {
          Discover::in(app_path())
              ->classes()
              ->cacheFor(now()->addDays(7))
              ->get();
      }
      
  6. Avoid Over-Discovery:

    • Exclude directories like vendor/, node_modules/, or tests/:
      Discover::in(base_path('app'))
          ->ignoreDirectories(['tests', 'Models/Concerns'])
          ->classes()
          ->get();
      
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport