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

Lody Laravel Package

lorisleiva/lody

Lody loads files or PHP classes from one or more paths as a Laravel LazyCollection. Discover classes via PSR-4 resolution, then filter (e.g., non-abstract, instance of) and iterate to register or process them. Configurable path and classname resolving.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require lorisleiva/lody
    

    No additional configuration is required for basic usage in Laravel.

  2. First Use Case: Discover and register all non-abstract Node classes in app/Workflow/Nodes:

    use Lorisleiva\Lody\Lody;
    
    Lody::classes('app/Workflow/Nodes')
        ->isNotAbstract()
        ->isInstanceOf(Node::class)
        ->each(fn (string $classname) => $this->register($classname));
    
  3. Where to Look First:

    • README.md: Focus on the Usage section for core methods (files(), classes()).
    • ClassnameLazyCollection: Explore filtering methods like isInstanceOf(), hasTrait(), and hasMethod().
    • Lody Facade: Check resolvePathUsing() and resolveClassnameUsing() for customization.

Implementation Patterns

Core Workflows

1. Dynamic Plugin/Extension Registration

  • Pattern: Scan a plugins/ directory for classes implementing PluginInterface and auto-register them.
  • Example:
    Lody::classes('plugins')
        ->isInstanceOf(PluginInterface::class)
        ->each(fn (string $class) => PluginManager::register($class));
    

2. Conditional Class Loading (Feature Flags)

  • Pattern: Load classes only if a feature flag is enabled.
  • Example:
    if (config('features.workflows.enabled')) {
        Lody::classes('app/Workflow/Nodes')
            ->isNotAbstract()
            ->each(fn (string $class) => $this->register($class));
    }
    

3. Service Provider Boilerplate Reduction

  • Pattern: Replace manual register() calls in AppServiceProvider with dynamic discovery.
  • Example:
    // Instead of:
    // $this->app->bind('App\Contracts\PaymentGateway', App\StripeGateway::class);
    
    // Use:
    Lody::classes('app/Gateways')
        ->isInstanceOf(PaymentGateway::class)
        ->each(fn (string $class) => $this->app->bind(PaymentGateway::class, $class));
    

4. Test Data Generation

  • Pattern: Discover classes with a generateTestData() method to populate test databases.
  • Example:
    Lody::classes('tests/DataFactories')
        ->hasMethod('generateTestData')
        ->each(fn (string $class) => $class::generateTestData());
    

5. CLI Command Auto-Discovery

  • Pattern: Register all Artisan commands implementing QueuedCommand.
  • Example:
    Lody::classes('app/Console/Commands')
        ->hasTrait(QueuedCommand::class)
        ->each(fn (string $class) => $this->commands($class));
    

6. Legacy Code Integration

  • Pattern: Wrap legacy classes with modern interfaces by scanning for specific methods.
  • Example:
    Lody::classes('legacy')
        ->hasMethod('process')
        ->each(fn (string $class) => $this->wrapLegacyClass($class));
    

Integration Tips

  • Combine with Laravel Facades: Use Lody alongside app(), config(), or cache() for environment-aware loading:

    if (app()->environment('local')) {
        Lody::classes('app/DevTools')
            ->each(fn (string $class) => $this->registerDevTool($class));
    }
    
  • Custom Finder Instances: Pass a Symfony\Component\Finder\Finder for advanced filtering (e.g., exclude tests/):

    $finder = Finder::create()
        ->files()
        ->in(app_path('Services'))
        ->exclude('tests');
    
    Lody::classesFromFinder($finder)
        ->isInstanceOf(ServiceInterface::class)
        ->each(...);
    
  • Caching Results: Cache the lazy collection to avoid repeated filesystem scans:

    $classes = cache()->remember('discovered_classes', now()->addHour(), function () {
        return Lody::classes('app/Modules')
            ->isInstanceOf(ModuleInterface::class);
    });
    
  • Event-Driven Discovery: Trigger discovery on booted or registered events in service providers:

    public function boot(): void
    {
        Lody::classes('app/EventListeners')
            ->hasTrait(ShouldQueue::class)
            ->each(fn (string $class) => event(new ClassDiscovered($class)));
    }
    

Gotchas and Tips

Pitfalls

  1. Path Resolution Quirks:

    • Issue: Paths starting with / are treated as absolute. Relative paths are resolved from base_path().
    • Fix: Use Lody::resolvePathUsing() to customize logic:
      Lody::resolvePathUsing(fn (string $path) => $path);
      
  2. Classname Resolution Failures:

    • Issue: Custom PSR-4 mappings or non-standard autoload paths may break resolveClassnameUsing.
    • Fix: Override the resolver or set a custom autoload path:
      Lody::setAutoloadPath('custom/autoload_psr4.php');
      
  3. Performance with Large Directories:

    • Issue: Deeply nested directories or many files can slow down lazy collections.
    • Fix: Use recursive: false or limit depth with a custom Finder:
      Lody::classes('app/Modules', recursive: false);
      
  4. False Positives in classExists:

    • Issue: getClassnames() may return invalid classes (e.g., from vendor/).
    • Fix: Chain classExists() to filter:
      Lody::files('vendor')->getClassnames()->classExists();
      
  5. Trait/Method Reflection Overhead:

    • Issue: Methods like hasTrait() or hasMethod() use reflection, which can be slow for many classes.
    • Fix: Cache results or limit scope:
      $classes = Lody::classes('app/Services')->remember('cached_classes', ...);
      
  6. Windows Path Handling:

    • Issue: Path separators (/ vs \) may cause issues on Windows.
    • Fix: Normalize paths:
      Lody::resolvePathUsing(fn (string $path) => str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path));
      

Debugging Tips

  • Inspect Raw Files: Use Lody::files()->each(fn (SplFileInfo $file) => dump($file->getPathname())) to debug paths.

  • Validate Classnames: Check resolved classnames with:

    Lody::classes('app')->each(fn (string $class) => dump(class_exists($class)));
    
  • Log Filtered Results: Debug filtering chains:

    $collection = Lody::classes('app');
    $collection->isInstanceOf(SomeClass::class)->each(fn (string $class) => logger()->debug($class));
    

Extension Points

  1. Custom Resolvers:

    • Override resolvePathUsing or resolveClassnameUsing for project-specific logic (e.g., Docker volumes, symlinked paths).
  2. Additional Filters:

    • Extend ClassnameLazyCollection by adding methods like hasAnnotation() or implementsInterface():
      // In a service provider:
      ClassnameLazyCollection::macro('hasAnnotation', function (string $annotation) {
          return $this->filter(fn (string $class) => /* check annotation */);
      });
      
  3. Lazy Collection Macros:

    • Add reusable macros for common patterns:
      Lody::classes('app')->queued()->each(...); // Macro for `hasTrait(ShouldQueue::class)`
      
  4. Integration with Laravel Packages:

    • Use Lody in your packages to enable auto-discovery of user-provided classes (e.g., app/Extensions/MyPackage/).
  5. Performance Optimizations:

    • Implement LazyCollection caching or pre-load paths for frequently accessed directories.

Configuration Quirks

  • Autoload Path: Ensure vendor/composer/autoload_psr4.php is up-to-date after adding new PSR-4 mappings:

    composer dump-autoload
    
  • Base Path: For non-Laravel use, explicitly set the base path:

    Lody::setBasePath(__DIR__);
    
  • Hidden Files: By default, hidden files (e.g., .env) are excluded. Use hidden: true to include them:

    Lody::files('app', hidden: true);
    
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle