Add form value persistence across cache revalidation re-renders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
253 lines
5.8 KiB
Markdown
Executable File
253 lines
5.8 KiB
Markdown
Executable File
---
|
|
name: event-hooks
|
|
description: RSX PHP event system with filters, gates, and actions using the #[OnEvent] attribute. Use when extending framework behavior, implementing authorization hooks, transforming data through handlers, or triggering custom events.
|
|
---
|
|
|
|
# RSX Event Hooks
|
|
|
|
## Overview
|
|
|
|
RSX provides an attribute-based event system with three event types:
|
|
|
|
| Type | Purpose | Return Handling |
|
|
|------|---------|-----------------|
|
|
| **Filter** | Transform data through chain | Each handler modifies and returns data |
|
|
| **Gate** | Authorization checks | First non-true stops execution |
|
|
| **Action** | Side effects | Return values ignored |
|
|
|
|
Unlike Laravel's EventServiceProvider, RSX uses `#[OnEvent]` attributes with automatic manifest discovery.
|
|
|
|
---
|
|
|
|
## Creating Event Handlers
|
|
|
|
Place handler classes in `/rsx/handlers/`:
|
|
|
|
```php
|
|
namespace Rsx\Handlers;
|
|
|
|
class Upload_Handlers
|
|
{
|
|
#[OnEvent('file.upload.authorize', priority: 10)]
|
|
public static function require_auth($data)
|
|
{
|
|
if (!Session::is_logged_in()) {
|
|
return response()->json(['error' => 'Auth required'], 403);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#[OnEvent('file.upload.params', priority: 20)]
|
|
public static function add_metadata($params)
|
|
{
|
|
$params['uploaded_by'] = Session::get_user_id();
|
|
return $params;
|
|
}
|
|
}
|
|
```
|
|
|
|
Handlers are automatically discovered - no manual registration needed.
|
|
|
|
---
|
|
|
|
## Filter Events
|
|
|
|
Transform data through a chain of handlers. Each handler receives the result of the previous handler.
|
|
|
|
```php
|
|
// Trigger
|
|
$result = Rsx::trigger_filter('event.name', $data);
|
|
|
|
// Handler - MUST return modified data
|
|
#[OnEvent('post.content.filter', priority: 10)]
|
|
public static function sanitize_html($content)
|
|
{
|
|
return strip_tags($content, '<p><a><strong>');
|
|
}
|
|
|
|
#[OnEvent('post.content.filter', priority: 20)]
|
|
public static function add_signature($content)
|
|
{
|
|
return $content . "\n\nPosted via RSX";
|
|
}
|
|
```
|
|
|
|
**Flow**: Handler 1 (priority 10) → Handler 2 (priority 20) → Final result
|
|
|
|
---
|
|
|
|
## Gate Events
|
|
|
|
Authorization checks where first non-true response halts execution.
|
|
|
|
```php
|
|
// Trigger
|
|
$result = Rsx::trigger_gate('api.access.authorize', $data);
|
|
if ($result !== true) {
|
|
return $result; // Access denied
|
|
}
|
|
|
|
// Handler - return true to allow, anything else to deny
|
|
#[OnEvent('api.access.authorize', priority: 10)]
|
|
public static function check_auth($data)
|
|
{
|
|
if (!Session::is_logged_in()) {
|
|
return response()->json(['error' => 'Login required'], 401);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#[OnEvent('api.access.authorize', priority: 20)]
|
|
public static function check_permissions($data)
|
|
{
|
|
if (!$data['user']->can($data['permission'])) {
|
|
return response()->json(['error' => 'Forbidden'], 403);
|
|
}
|
|
return true;
|
|
}
|
|
```
|
|
|
|
**Flow**: Handler 1 returns true → Handler 2 returns false → STOPS, returns false
|
|
|
|
---
|
|
|
|
## Action Events
|
|
|
|
Fire-and-forget side effects. Return values are ignored.
|
|
|
|
```php
|
|
// Trigger
|
|
Rsx::trigger_action('user.created', ['user' => $user]);
|
|
|
|
// Handler - no return value needed
|
|
#[OnEvent('user.created', priority: 10)]
|
|
public static function send_welcome_email($data): void
|
|
{
|
|
Email_Service::send_welcome($data['user']);
|
|
}
|
|
|
|
#[OnEvent('user.created', priority: 20)]
|
|
public static function log_signup($data): void
|
|
{
|
|
Activity_Model::log('signup', $data['user']->id);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Priority
|
|
|
|
Handlers execute in priority order (lowest number first):
|
|
|
|
```php
|
|
#[OnEvent('my.event', priority: 10)] // Runs first
|
|
#[OnEvent('my.event', priority: 20)] // Runs second
|
|
#[OnEvent('my.event', priority: 30)] // Runs third
|
|
```
|
|
|
|
Default priority is 10 if not specified.
|
|
|
|
---
|
|
|
|
## Common Use Cases
|
|
|
|
### File Upload Authorization
|
|
|
|
```php
|
|
#[OnEvent('file.upload.authorize', priority: 10)]
|
|
public static function require_auth($data)
|
|
{
|
|
if (!Session::is_logged_in()) {
|
|
return response()->json(['error' => 'Auth required'], 403);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#[OnEvent('file.download.authorize', priority: 10)]
|
|
public static function check_file_access($data)
|
|
{
|
|
$attachment = $data['attachment'];
|
|
if ($attachment->site_id !== Session::get_site_id()) {
|
|
return response()->json(['error' => 'Access denied'], 403);
|
|
}
|
|
return true;
|
|
}
|
|
```
|
|
|
|
### Transforming Request Data
|
|
|
|
```php
|
|
#[OnEvent('contact.save.params', priority: 10)]
|
|
public static function normalize_phone($params)
|
|
{
|
|
if (!empty($params['phone'])) {
|
|
$params['phone'] = preg_replace('/[^0-9]/', '', $params['phone']);
|
|
}
|
|
return $params;
|
|
}
|
|
```
|
|
|
|
### Logging Actions
|
|
|
|
```php
|
|
#[OnEvent('project.deleted', priority: 10)]
|
|
public static function log_deletion($data): void
|
|
{
|
|
Audit_Log::create([
|
|
'action' => 'project_deleted',
|
|
'entity_id' => $data['project']->id,
|
|
'user_id' => Session::get_user_id(),
|
|
]);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Triggering Custom Events
|
|
|
|
```php
|
|
// In your controller or service
|
|
class Project_Controller
|
|
{
|
|
#[Ajax_Endpoint]
|
|
public static function delete(Request $request, array $params = [])
|
|
{
|
|
$project = Project_Model::find($params['id']);
|
|
|
|
// Gate: Check if deletion allowed
|
|
$auth = Rsx::trigger_gate('project.delete.authorize', [
|
|
'project' => $project,
|
|
'user' => Session::get_user()
|
|
]);
|
|
if ($auth !== true) {
|
|
return $auth;
|
|
}
|
|
|
|
$project->delete();
|
|
|
|
// Action: Notify listeners
|
|
Rsx::trigger_action('project.deleted', ['project' => $project]);
|
|
|
|
return ['success' => true];
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Handler Organization
|
|
|
|
```
|
|
rsx/handlers/
|
|
├── auth_handlers.php # Authentication hooks
|
|
├── file_handlers.php # File upload/download hooks
|
|
├── notification_handlers.php
|
|
└── audit_handlers.php
|
|
```
|
|
|
|
Each file can contain multiple handler methods with different event subscriptions.
|
|
|
|
## More Information
|
|
|
|
Details: `php artisan rsx:man event_hooks`
|