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

Hookable Laravel Package

sofa/hookable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require sofa/hookable:~5.2
    

    Ensure your Laravel version is 5.2 (or use a compatible fork for newer versions).

  2. Register Hooks: Use the hook() method on your Eloquent model to attach closures to events. Example:

    use Sofa\Hookable\HookableTrait;
    
    class User extends Model
    {
        use HookableTrait;
    
        protected static function bootHookable()
        {
            static::hook('saving', function ($model) {
                // Runs before saving
            });
        }
    }
    
  3. First Use Case: Hook into saving or saved to modify behavior before/after persistence:

    static::hook('saving', function ($model) {
        if ($model->password) {
            $model->password = bcrypt($model->password);
        }
    });
    

Where to Look First

  • Documentation: README for supported hooks (e.g., getAttribute, setAttribute, save).
  • Source: HookableTrait for implementation details.
  • Examples: Check the eloquence repo for real-world usage.

Implementation Patterns

Core Workflows

  1. Attribute Hooks: Modify attribute access/setters dynamically:

    static::hook('getAttribute:email', function ($model, $key) {
        return strtolower($model->{$key});
    });
    
    static::hook('setAttribute:email', function ($model, $key, $value) {
        $model->{$key} = strtoupper($value);
    });
    
  2. Builder Hooks: Intercept queries via Builder hooks:

    static::hook('Builder::where', function ($builder, $query, $callback) {
        // Modify query logic
    });
    
  3. Lifecycle Hooks: Use saving, saved, deleting, etc., for pre/post operations:

    static::hook('saving', function ($model) {
        $model->updated_at = now();
    });
    

Integration Tips

  • Dependency Injection: Bind closures to model instances using a wrapper class (as noted in the README) to avoid static context issues:

    class HookWrapper {
        public function __invoke($model) {
            return function () use ($model) { /* ... */ };
        }
    }
    
  • Conditional Hooks: Enable/disable hooks dynamically:

    if ($model->isAdmin()) {
        static::hook('saving', $adminHook);
    }
    
  • Priority Hooks: Use named hooks to control execution order:

    static::hook('saving:before', $hook1);
    static::hook('saving:after', $hook2);
    
  • Reusable Hooks: Extract hooks into services for modularity:

    class UserHooks {
        public static function register() {
            User::hook('saving', function ($model) { /* ... */ });
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Static Context Limitation:

    • Issue: Closures defined in boot() (static context) lose $this binding.
    • Fix: Use a wrapper class or define hooks in a non-static method (e.g., constructor or instance method).
  2. Hook Overwriting:

    • Issue: Subsequent hook() calls overwrite previous ones for the same event.
    • Fix: Use unique hook names or accumulate hooks:
      static::hook('saving:hook1', $closure1);
      static::hook('saving:hook2', $closure2);
      
  3. Performance:

    • Issue: Excessive hooks may slow down queries/operations.
    • Fix: Profile and remove unused hooks. Use hookOnce() for one-time actions.
  4. Builder Hook Scope:

    • Issue: Builder hooks apply globally to all queries.
    • Fix: Scope hooks to specific models or use middleware for query filtering.
  5. Magic Methods:

    • Issue: Hooks like __isset/__unset may conflict with Laravel’s magic methods.
    • Fix: Test thoroughly and avoid overriding core behavior unintentionally.

Debugging

  • Log Hooks: Add debug logs to verify hook execution:

    static::hook('saving', function ($model) {
        \Log::debug('Saving model', ['id' => $model->id]);
    });
    
  • Disable Hooks: Temporarily remove hooks during debugging:

    static::unhook('saving', $hook);
    
  • Check Hook Order: Use named hooks or timestamps to debug execution order:

    static::hook('saving:1', $hook1);
    static::hook('saving:2', $hook2);
    

Configuration Quirks

  • Laravel Version:

  • Hook Naming:

    • Use consistent naming (e.g., saving:hookName) to avoid collisions.

Extension Points

  1. Custom Hooks: Extend the trait to add domain-specific hooks:

    class CustomHookableTrait {
        public static function hookCustom($event, $closure) {
            // Extend HookableTrait logic
        }
    }
    
  2. Hook Storage: Store hooks in a database for dynamic management (e.g., via a Hook model).

  3. Event Dispatching: Combine with Laravel’s events for cross-cutting concerns:

    static::hook('saving', function ($model) {
        event(new ModelSaving($model));
    });
    
  4. Testing: Mock hooks in tests using partial mocks or interfaces:

    $this->partialMock(User::class, ['hook']);
    
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