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

Modularize Laravel Package

internachi/modularize

Add InterNACHI Modular support to your Laravel package commands with simple traits. Provides a --module option and helpers to resolve module config. Includes a GeneratorCommand trait so generated files land in the module directory with correct namespaces.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the package:
    composer require internachi/modularize
    
  2. Add the trait to a command: For standard commands:
    use Illuminate\Console\Command;
    use InterNACHI\Modularize\Support\Modularize;
    
    class MyCommand extends Command
    {
        use Modularize;
    
        public function handle()
        {
            if ($module = $this->module()) {
                // Module is active; use $module->name, $module->path, etc.
            }
        }
    }
    
  3. Test the --module flag:
    php artisan my:command --module=Blog
    

First Use Case

Convert a file generator to module-aware:

use Illuminate\Console\GeneratorCommand;
use InterNACHI\Modularize\Support\ModularizeGeneratorCommand;

class MakeAdminController extends GeneratorCommand
{
    use ModularizeGeneratorCommand;

    protected function getStub()
    {
        return $this->module()
            ? __DIR__.'/stubs/admin.controller.stub'
            : parent::getStub();
    }
}

Run:

php artisan make:admin-controller User --module=AdminPanel

Implementation Patterns

Core Workflows

  1. Module-Aware Commands:

    • Use Modularize trait for commands that need to detect a module (e.g., logging, validation).
    • Access module via $this->module() (returns null if --module not provided).
    • Example: Dynamic path resolution:
      $path = $this->module()
          ? $this->module()->path('database/migrations')
          : database_path('migrations');
      
  2. File Generation:

    • Use ModularizeGeneratorCommand for GeneratorCommand-based tools.
    • Override getDefaultNamespace() to inject module namespaces:
      protected function getDefaultNamespace()
      {
          return $this->module()
              ? $this->module()->getNamespace('App/Http/Controllers')
              : 'App\\Http\\Controllers';
      }
      
    • Files auto-place in modules/{module}/... with correct namespacing.
  3. Conditional Logic:

    • Chain module checks with existing command logic:
      if ($this->module()) {
          $this->info("Generating for module: {$this->module()->name}");
      } else {
          $this->warn("No module specified; using global namespace.");
      }
      

Integration Tips

  • Leverage ModuleConfig helpers:
    $module->name;          // Module name (e.g., "Blog")
    $module->path($subdir); // Resolve path (e.g., "modules/Blog/database")
    $module->getNamespace($subpath); // Namespace (e.g., "Modules\\Blog\\Http\\Controllers")
    
  • Combine with internachi/modular: Ensure your package’s composer.json requires internachi/modular:
    "require": {
        "internachi/modular": "^1.0"
    }
    
  • Customize stubs: Use $this->module() in stub files to inject module-specific placeholders (e.g., {{ moduleNamespace }}).

Gotchas and Tips

Pitfalls

  1. No Module, No Graceful Fallback:

    • $this->module() returns null if --module is omitted. Always check:
      if (!$module = $this->module()) {
          return $this->error("Module required for this operation.");
      }
      
    • Fix: Provide a --module hint in getOptions():
      protected function getOptions()
      {
          return [
              ['module', 'm', InputOption::VALUE_OPTIONAL, 'Target module (e.g., Blog)', null],
          ];
      }
      
  2. Namespace Collisions:

    • If a module’s namespace conflicts with global namespaces (e.g., App vs. Modules\App), override getDefaultNamespace() explicitly:
      protected function getDefaultNamespace()
      {
          return $this->module()
              ? 'Modules\\' . str_replace('/', '\\', $this->module()->name) . '\\' . $this->rootNamespace()
              : $this->rootNamespace();
      }
      
  3. Laravel 13+ Console Changes:

    • The package supports Laravel 13’s Symfony 7.x console, but custom argument parsing may break. Use getArguments()/getOptions() instead of direct $input access.
  4. Dynamic Module Discovery:

    • The package does not auto-detect modules. Ensure internachi/modular is configured to load modules before running commands.

Debugging

  • Verify ModuleConfig: Dump the module object to debug:

    dd($this->module());
    

    Expected structure:

    ModuleConfig {
        name: "Blog",
        basePath: "/path/to/modules/Blog",
        +getNamespace(string $subpath): string,
        +path(string $subdir): string,
    }
    
  • Check File Permissions: If generated files fail silently, ensure the module directory is writable:

    chmod -R 755 modules/{module-name}
    

Extension Points

  1. Custom Module Paths: Extend ModuleConfig by binding a custom resolver:

    $this->app->bind('internachi/modular.config', function () {
        return new CustomModuleConfig($this->module());
    });
    
  2. Pre-Generation Hooks: Add logic before file generation in ModularizeGeneratorCommand:

    protected function getModulePath()
    {
        if (!$module = $this->module()) {
            return null;
        }
        return $module->path('src/' . $this->argument('type'));
    }
    
  3. Post-Command Actions: Use the handle() return value to trigger module-specific tasks:

    public function handle()
    {
        if ($module = $this->module()) {
            $this->call('module:publish', [
                '--module' => $module->name,
                '--tag' => 'config',
            ]);
        }
        return 0;
    }
    

Pro Tips

  • Batch Module Commands: Create a wrapper command to apply operations to multiple modules:

    class ModuleBatchCommand extends Command
    {
        use Modularize;
    
        protected function handle()
        {
            foreach ($this->argument('modules') as $moduleName) {
                $this->call('my:command', [
                    '--module' => $moduleName,
                ]);
            }
        }
    }
    

    Usage:

    php artisan module:batch Blog Admin --command=my:command
    
  • Module-Aware Testing: Mock ModuleConfig in tests:

    $module = Mockery::mock('internachi/modular.ModuleConfig');
    $module->shouldReceive('name')->andReturn('TestModule');
    $this->app->instance('internachi/modular.config', $module);
    
  • Performance: Cache module lookups in long-running commands:

    private $moduleCache;
    
    private function module()
    {
        if (is_null($this->moduleCache)) {
            $this->moduleCache = parent::module();
        }
        return $this->moduleCache;
    }
    
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/entity-migrator
codraw/doctrine-extra
codraw/aws-tool-kit
codraw/validator
codraw/workflow
codraw/open-api
codraw/cron-job
codraw/process
codraw/log
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