Installation:
composer require sofa/hookable:~5.2
Ensure your Laravel version is 5.2 (or use a compatible fork for newer versions).
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
});
}
}
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);
}
});
getAttribute, setAttribute, save).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);
});
Builder Hooks:
Intercept queries via Builder hooks:
static::hook('Builder::where', function ($builder, $query, $callback) {
// Modify query logic
});
Lifecycle Hooks:
Use saving, saved, deleting, etc., for pre/post operations:
static::hook('saving', function ($model) {
$model->updated_at = now();
});
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) { /* ... */ });
}
}
Static Context Limitation:
boot() (static context) lose $this binding.Hook Overwriting:
hook() calls overwrite previous ones for the same event.static::hook('saving:hook1', $closure1);
static::hook('saving:hook2', $closure2);
Performance:
hookOnce() for one-time actions.Builder Hook Scope:
Magic Methods:
__isset/__unset may conflict with Laravel’s magic methods.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);
Laravel Version:
spatie/laravel-activitylog or custom solutions.Hook Naming:
saving:hookName) to avoid collisions.Custom Hooks: Extend the trait to add domain-specific hooks:
class CustomHookableTrait {
public static function hookCustom($event, $closure) {
// Extend HookableTrait logic
}
}
Hook Storage:
Store hooks in a database for dynamic management (e.g., via a Hook model).
Event Dispatching: Combine with Laravel’s events for cross-cutting concerns:
static::hook('saving', function ($model) {
event(new ModelSaving($model));
});
Testing: Mock hooks in tests using partial mocks or interfaces:
$this->partialMock(User::class, ['hook']);
How can I help you explore Laravel packages today?