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

Macroable Laravel Package

hyperf/macroable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package:

    composer require hyperf/macroable
    

    No additional configuration is required unless using container macros (see Implementation Patterns).

  2. First Use Case: Extending a Collection Hyperf’s Collection class (or a custom collection) can be extended with domain-specific macros. Start by creating a macro-enabled collection class:

    use Hyperf\Collection\Collection;
    use Hyperf\Macroable\Macroable;
    
    class DomainCollection extends Collection
    {
        use Macroable;
    }
    
  3. Define a Macro: Add a reusable method to your collection:

    DomainCollection::macro('groupByDate', function () {
        return $this->groupBy(fn ($item) => $item['created_at']->format('Y-m-d'));
    });
    
  4. Use the Macro:

    $collection = new DomainCollection([/* items */]);
    $grouped = $collection->groupByDate();
    
  5. Where to Look Next:

    • Hyperf Collections: Explore vendor/hyperf/collection for built-in methods to extend.
    • Macroable Trait: Review src/Macroable.php for advanced usage (e.g., static macros, container macros).
    • Laravel Docs: Cross-reference Laravel’s macro documentation for patterns (e.g., conditional macros, macro events).

Implementation Patterns

Core Workflows

  1. Extending Built-in Classes:

    • Collections: Add domain logic (e.g., calculateRevenue() for e-commerce).
      use Hyperf\Collection\Collection;
      
      Collection::macro('calculateRevenue', function () {
          return $this->sum('price');
      });
      
    • Strings: Custom string manipulations (e.g., slugify()).
      use Hyperf\Support\Str;
      
      Str::macro('slugify', function ($value) {
          return Str::of($value)->slug();
      });
      
  2. Domain-Specific Macros:

    • Query Builders: Reusable scopes for Hyperf DB.
      use Hyperf\Database\Model\Builder;
      
      Builder::macro('activeOnly', function (Builder $query) {
          return $query->where('is_active', true);
      });
      
    • Request/Response: Transform payloads.
      use Hyperf\HttpMessage\Stream\SwooleStream;
      
      SwooleStream::macro('sanitize', function (SwooleStream $stream) {
          return Str::of($stream)->replace(['<script>', '</script>'], '');
      });
      
  3. Container Macros: Register macros globally via Hyperf’s container (for framework-wide reuse):

    use Hyperf\Di\Container;
    use Hyperf\Macroable\Macroable;
    
    Container::get(Macroable::class)->extend('collection', function () {
        return new class extends Collection {
            use Macroable;
        };
    });
    

Integration Tips

  • Service Providers: Centralize macro registrations in a Hyperf service provider:

    use Hyperf\Framework\Contract\ConfigInterface;
    use Hyperf\Di\Annotation\Inject;
    
    class MacroServiceProvider extends ServiceProvider
    {
        public function register()
        {
            Collection::macro('customMethod', function () {
                return 'Registered via provider';
            });
        }
    }
    
  • Testing Macros: Use Hyperf’s testing tools to mock macros:

    $this->mock(Collection::class)
         ->shouldReceive('customMethod')
         ->andReturn('Mocked result');
    
  • Macros in Middleware: Extend middleware with fluent macros for request/response transformations:

    use Hyperf\HttpServer\Contract\RequestInterface;
    
    RequestInterface::macro('getJsonApiPayload', function () {
        return $this->json()->get('data', []);
    });
    
  • Performance Considerations:

    • Avoid registering thousands of macros in a single class (use namespacing or separate classes).
    • For high-frequency macros, consider caching the macro lookup (e.g., via Hyperf\Cache).

Gotchas and Tips

Pitfalls

  1. Container Macros and Singleton Scope:

    • Macros registered via Container::get(Macroable::class) may behave unexpectedly in multi-process Hyperf applications (e.g., workers). Ensure macros are stateless or use shared storage (e.g., Redis).
  2. Macro Overrides:

    • Macros do not override existing methods. If you define a macro with the same name as an existing method, the macro will be called instead:
      Collection::macro('pluck', function () { ... }); // Overrides Collection::pluck()
      
    • Fix: Use unique macro names or check for method existence:
      if (!method_exists(Collection::class, 'customMethod')) {
          Collection::macro('customMethod', function () { ... });
      }
      
  3. Static Macros and Autoloading:

    • Static macros (e.g., Str::macro()) may trigger autoloading delays if used in performance-critical paths. Pre-register macros in a service provider:
      Str::macro('custom', function () { ... });
      
  4. Hyperf’s DI Container Quirks:

    • Macros registered via Container do not persist across process restarts (e.g., in Hyperf’s worker model). Re-register macros in onWorkerStart:
      $container->get(Macroable::class)->extend('collection', function () { ... });
      
  5. Macros in Coroutines:

    • Macros called within go() coroutines share the same context. Avoid mutable state:
      // ❌ Risky: Shared state across coroutines
      Collection::macro('incrementCounter', function () {
          static $counter = 0;
          return ++$counter;
      });
      

Debugging

  1. Macro Not Found:

    • Verify the macro is registered before use (macros are not lazy-loaded).
    • Check for typos or case sensitivity (e.g., GroupByDate vs. groupByDate).
  2. Method Not Callable:

    • Ensure the class using the macro has the Macroable trait:
      class MyClass {
          use Macroable; // Required!
      }
      
  3. Container Macro Issues:

    • Use Container::get(Macroable::class)->hasMacro('method') to check registration:
      if (!$container->get(Macroable::class)->hasMacro('customMethod')) {
          throw new \RuntimeException('Macro not registered!');
      }
      
  4. Performance Bottlenecks:

    • Profile macro-heavy code with hyperf\di\profiler to identify slow lookups.

Tips

  1. Namespacing Macros: Use namespaced macros to avoid collisions:

    Collection::macro('ecommerce:calculateRevenue', function () { ... });
    
  2. Conditional Macros: Dynamically register macros based on config:

    if (config('app.feature_flags.enable_custom_macros')) {
        Collection::macro('customMethod', function () { ... });
    }
    
  3. Macro Events (Laravel-Style): Simulate Laravel’s Macroable::macro() event using Hyperf’s events:

    event(new MacroRegistered('collection', 'customMethod'));
    
  4. Extending Hyperf’s Core: Override core classes (e.g., Hyperf\Collection\Collection) in a decorator or extension:

    use Hyperf\Collection\Collection as BaseCollection;
    
    class ExtendedCollection extends BaseCollection
    {
        use Macroable;
    
        public static function boot()
        {
            static::macro('newMethod', function () { ... });
        }
    }
    
  5. Macros in Tests: Reset macros between tests to avoid pollution:

    use Hyperf\Macroable\Macroable;
    
    beforeEach(function () {
        Macroable::forgetAll();
    });
    
  6. Hyperf-Specific Extensions:

    • Process Macros: Register macros in onWorkerStart for worker processes.
    • Coroutine Macros: Use Hyperf\AsyncQueue\Job to queue macro-heavy operations.
  7. Documentation: Add PHPDoc blocks to macros for IDE autocompletion:

    /**
     * Calculate revenue for a collection of orders.
     *
     * @return float
     */
    Collection::macro('calculateRevenue', function () { ... });
    
  8. Fallback for Laravel Macros: If migrating from Laravel, create a compatibility layer:

    if (!class_exists('Str')) {
        class_alias(\Hyperf\Support\Str::class, 'Str');
    }
    
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky