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 a workflow directory:

    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 and Configuration sections for quick integration.
    • Lody facade: Core entry point for files() and classes() methods.
    • ClassnameLazyCollection: Methods like isInstanceOf(), hasTrait(), and hasMethod() for filtering.

Implementation Patterns

Core Workflows

  1. Dynamic Plugin/Extension Registration:

    // Auto-register all classes implementing `PluginContract` in `app/Plugins/`
    Lody::classes('app/Plugins')
        ->isInstanceOf(PluginContract::class)
        ->each(fn (string $class) => PluginManager::register($class));
    
  2. Conditional Loading with Feature Flags:

    // Load classes only if a feature is enabled
    Lody::classes('app/Features/Advanced')
        ->when(fn () => config('features.advanced.enabled'))
        ->each(fn (string $class) => $this->boot($class));
    
  3. Service Provider Optimization:

    // Replace manual `register()` calls with dynamic discovery
    public function register()
    {
        Lody::classes('app/Providers/EventListeners')
            ->isInstanceOf(EventListener::class)
            ->each([$this, 'registerListener']);
    }
    
  4. Test Data Generation:

    // Discover and mock classes with `generateTestData()`
    Lody::classes('app/Models')
        ->hasMethod('generateTestData')
        ->each(fn (string $model) => $this->mock($model));
    
  5. CLI Command Auto-Discovery:

    // Register all commands implementing `QueuedCommand`
    Lody::classes('app/Console/Commands')
        ->hasTrait(QueuedCommand::class)
        ->each(fn (string $command) => $this->commands($command));
    

Integration Tips

  • Combine with Laravel Services: Use Lody in AppServiceProvider or EventServiceProvider to replace hardcoded registrations.

    // app/Providers/AppServiceProvider.php
    public function boot()
    {
        Lody::classes('app/Rules')
            ->isInstanceOf(Rule::class)
            ->each(fn (string $rule) => Rule::extend(class_basename($rule), $rule));
    }
    
  • Custom Finder Logic: Pass a Symfony\Component\Finder\Finder instance for advanced filtering:

    use Symfony\Component\Finder\Finder;
    
    $finder = Finder::create()
        ->files()
        ->in(app_path('Workflow/Nodes'))
        ->name('*.php')
        ->depth(2);
    
    Lody::classesFromFinder($finder)
        ->isInstanceOf(Node::class)
        ->each(fn (string $class) => $this->registerNode($class));
    
  • Leverage Lazy Collections: Chain methods for complex filtering without loading all classes into memory:

    Lody::classes('app/Repositories')
        ->isInstanceOf(Repository::class)
        ->doesNotHaveTrait(Cacheable::class)
        ->each(fn (string $repo) => $this->bind($repo));
    
  • Non-Laravel Environments: Set a custom base path for standalone PHP projects:

    Lody::setBasePath(__DIR__);
    

Gotchas and Tips

Pitfalls

  1. Path Resolution Quirks:

    • Paths without a leading slash are resolved relative to base_path(). Use absolute paths (e.g., '/app/Plugins') for consistency.
    • Hidden files/directories (e.g., .env) are excluded by default. Use hidden: true to include them:
      Lody::files('app/Config', hidden: true);
      
  2. Classname Resolution Edge Cases:

    • PSR-4 Mappings: If your composer.json autoload paths differ from Laravel’s conventions, override resolveClassnameUsing:
      Lody::resolveClassnameUsing(function (SplFileInfo $file) {
          return str_replace(
              ['/', '.php'],
              ['\\', ''],
              $file->getRelativePathname()
          );
      });
      
    • Vendor Classes: Lody respects vendor/composer/autoload_psr4.php by default. To exclude vendor classes, filter manually:
      Lody::classes('vendor')
          ->reject(fn (string $class) => str_starts_with($class, 'Vendor\\'));
      
  3. Performance with Large Directories:

    • Lazy Loading: Methods like each() process items one by one. For memory-intensive operations, use ->toArray() sparingly.
    • Recursive Scans: Disable recursion for shallow directories to avoid unnecessary I/O:
      Lody::classes('app/Commands', recursive: false);
      
  4. Reflection Limitations:

    • Dynamic Classes: Classes loaded via eval() or dynamic bytecode may not be detected. Stick to filesystem-based discovery.
    • Traits/Methods: Reflection-based checks (e.g., hasTrait()) may fail for abstract methods or private/protected members. Use isPublic() or isProtected() filters if needed.
  5. Laravel-Specific Assumptions:

    • app() Helper: The default resolveClassnameUsing relies on Laravel’s app() helper. For non-Laravel use, provide a custom resolver or set setBasePath().

Debugging Tips

  1. Inspect Raw Files/Classes:

    // Log all discovered files (for debugging)
    Lody::files('app/Plugins')->each(fn (SplFileInfo $file) => Log::debug($file->getPathname()));
    
    // Log classnames before filtering
    Lody::classes('app/Nodes')->getClassnames()->each(fn (string $class) => Log::debug($class));
    
  2. Validate Paths: Ensure paths are correct by checking base_path():

    dd(base_path('app/Workflow/Nodes')); // Verify the path exists
    
  3. Override Resolvers Temporarily: Test custom logic in a service provider:

    Lody::resolveClassnameUsing(function (SplFileInfo $file) {
        return 'Custom\\Namespace\\' . str_replace(['/', '.php'], ['\\', ''], $file->getRelativePathname());
    });
    
  4. Handle Missing Classes Gracefully: Use classExists() to filter invalid classes:

    Lody::files('app/Models')->getClassnames()->classExists()->each(...);
    

Extension Points

  1. Custom Filters: Extend ClassnameLazyCollection by adding methods to app/Providers/LodyServiceProvider.php:

    use Lorisleiva\Lody\ClassnameLazyCollection;
    
    ClassnameLazyCollection::macro('hasAnnotation', function (string $annotation) {
        return $this->filter(fn (string $class) => class_has_attribute($class, $annotation));
    });
    
  2. Event-Based Discovery: Trigger events when classes are discovered:

    Lody::classes('app/Listeners')
        ->each(fn (string $listener) => event(new ClassDiscovered($listener)));
    
  3. Caching: Cache results for performance-critical paths (e.g., plugin discovery):

    $plugins = Cache::remember('lody.plugins', now()->addHours(1), function () {
        return Lody::classes('app/Plugins')->isInstanceOf(PluginContract::class)->toArray();
    });
    
  4. Windows Path Handling: Normalize paths for cross-platform compatibility:

    Lody::resolvePathUsing(function (string $path) {
        return Str::replaceFirst(['\\', ':'], '/', $path);
    });
    
  5. Integration with Laravel Packages: Use Lody in package development to auto-register components:

    // In your package's service provider
    Lody::classes('vendor/package/src/Commands')
        ->each(fn (string $command) => $this->commands($command));
    
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope