Botble provides a powerful hook system similar to WordPress, allowing developers to modify and extend functionality without changing core files. This system consists of two main components:
- Filters - Used to modify data before it's used or displayed
- Actions - Used to execute functions at specific points in code execution
This documentation explains how to use filters and actions in Botble with practical examples.
Filters allow you to modify data before it's used by the application. They are used to intercept and potentially modify values.
add_filter(string|array $hook, callable $callback, int $priority = 20, int $arguments = 1): voidParameters:
$hook: The name of the filter hook$callback: The function to run when the filter is applied$priority: Priority of execution (lower numbers run first)$arguments: Number of arguments the callback accepts
apply_filters(string $hook, mixed $value, mixed ...$args)Parameters:
$hook: The name of the filter hook$value: The value to filter$args: Additional arguments to pass to the filter callback
remove_filter(string $hook): void// Add a filter to modify table data
add_filter(BASE_FILTER_GET_LIST_DATA, function ($data, $model, $table) {
// Modify $data here
return $data;
}, 10, 3);// Add a filter to modify page content
add_filter(PAGE_FILTER_FRONT_PAGE_CONTENT, function ($content, $page) {
// Add additional content or modify existing content
$content .= '<div class="additional-content">Custom content added via filter</div>';
return $content;
}, 20, 2);// Add a filter to add custom dashboard widgets
add_filter(DASHBOARD_FILTER_ADMIN_LIST, function ($widgets, $widgetSettings) {
// Add a custom widget
$widgets[] = [
'id' => 'custom_widget',
'name' => 'Custom Widget',
'content' => view('plugins/your-plugin::widgets.custom-widget')->render(),
];
return $widgets;
}, 21, 2);Actions allow you to execute functions at specific points in the code execution. Unlike filters, actions don't modify data but perform operations.
add_action(string|array $hook, callable $callback, int $priority = 20, int $arguments = 1): voidParameters:
$hook: The name of the action hook$callback: The function to run when the action is triggered$priority: Priority of execution (lower numbers run first)$arguments: Number of arguments the callback accepts
do_action(string $hook, mixed ...$args): voidParameters:
$hook: The name of the action hook$args: Arguments to pass to the action callback
// Add an action to execute after content creation
add_action(BASE_ACTION_AFTER_CREATE_CONTENT, function ($screen, $request, $object) {
// Perform operations after content is created
// For example, send a notification or log the activity
Log::info("New content created: {$object->name}");
}, 10, 3);// Add an action to add custom form buttons
add_action(BASE_ACTION_FORM_ACTIONS, function ($form) {
// Add custom buttons to the form
echo '<button type="button" class="btn btn-info">Custom Button</button>';
}, 10, 1);// Add an action to execute after user logout
add_action(AUTH_ACTION_AFTER_LOGOUT_SYSTEM, function ($screen, $user) {
// Perform operations after user logout
// For example, log the logout activity
activity()
->performedOn($user)
->causedBy($user)
->log('Logged out');
}, 10, 2);Modifies data before retrieving front page items.
add_filter(BASE_FILTER_BEFORE_GET_FRONT_PAGE_ITEM, function ($query, $model) {
// Example: Add a condition to only show items with a specific tag
if ($model instanceof \Botble\Blog\Models\Post) {
$query->whereHas('tags', function ($query) {
$query->where('name', 'featured');
});
}
return $query;
}, 10, 2);Modifies data before retrieving a single item.
add_filter(BASE_FILTER_BEFORE_GET_SINGLE, function ($query, $model) {
// Example: Add eager loading relationships for better performance
if ($model instanceof \Botble\Blog\Models\Post) {
$query->with(['categories', 'tags', 'author']);
}
return $query;
}, 10, 2);Modifies single item data for public display.
add_filter(BASE_FILTER_PUBLIC_SINGLE_DATA, function ($data, $model) {
// Example: Add custom data to a blog post
if (get_class($model) === \Botble\Blog\Models\Post::class) {
// Add related posts
$data['related_posts'] = \Botble\Blog\Models\Post::query()
->wherePublished()
->where('id', '!=', $data['id'])
->whereHas('categories', function ($query) use ($model) {
$categoryIds = $model->categories->pluck('id')->all();
$query->whereIn('category_id', $categoryIds);
})
->limit(5)
->get();
}
return $data;
}, 10, 2);Modifies table queries in the admin panel.
add_filter(BASE_FILTER_TABLE_QUERY, function ($query, $table) {
// Example: Add a condition to a specific table
if ($table->getModel() instanceof \Botble\Blog\Models\Post) {
// Only show posts created by the current user for non-admin users
if (!auth()->user()->hasPermission('posts.edit')) {
$query->where('author_id', auth()->id());
}
}
return $query;
}, 10, 2);Modifies list data before display.
add_filter(BASE_FILTER_GET_LIST_DATA, function ($data, $model, $table) {
// Example: Add custom data to each row in a table
if ($model instanceof \Botble\Blog\Models\Post) {
foreach ($data as $key => $item) {
// Add view count from a custom analytics service
$data[$key]['views'] = YourAnalyticsService::getViews($item->id);
}
}
return $data;
}, 10, 3);Modifies dashboard widgets.
add_filter(DASHBOARD_FILTER_ADMIN_LIST, function ($widgets, $widgetSettings) {
// Example: Add a custom dashboard widget
$widgets[] = [
'id' => 'custom_stats_widget',
'name' => 'Custom Statistics',
'content' => view('plugins/your-plugin::widgets.stats', [
'stats' => YourStatsService::getStats(),
])->render(),
];
// Example: Remove a widget
$widgets = collect($widgets)->filter(function ($item) {
return $item['id'] !== 'widget_to_remove';
})->toArray();
return $widgets;
}, 10, 2);Modifies page content.
add_filter(PAGE_FILTER_FRONT_PAGE_CONTENT, function ($content, $page) {
// Example: Add a call-to-action section at the end of the homepage
if ($page->id == get_page_id_from_shortcode('[homepage]')) {
$content .= view('theme.sections.cta', [
'title' => 'Ready to get started?',
'button_text' => 'Contact Us',
'button_url' => url('contact'),
])->render();
}
return $content;
}, 10, 2);Modifies page names in admin list.
add_filter(PAGE_FILTER_PAGE_NAME_IN_ADMIN_LIST, function ($name, $page) {
// Example: Add an indicator for pages that are used in the menu
$usedInMenu = Menu::query()
->whereHas('menuNodes', function ($query) use ($page) {
$query->where('reference_id', $page->id)
->where('reference_type', Page::class);
})
->exists();
if ($usedInMenu) {
$name .= ' <span class="badge badge-info">In Menu</span>';
}
return $name;
}, 10, 2);Executes when the application is initialized.
add_action(BASE_ACTION_INIT, function () {
// Example: Register a custom post type or extend functionality
if (is_plugin_active('your-plugin')) {
// Initialize your plugin's components
YourPlugin::initialize();
}
// Example: Add custom routes
Route::group(['prefix' => 'custom', 'middleware' => ['web', 'core']], function () {
Route::get('page', 'YourController@index');
});
}, 10);Registers meta boxes for admin forms.
add_action(BASE_ACTION_META_BOXES, function ($context, $object) {
if ($context == 'advanced' && $object instanceof \Botble\Blog\Models\Post) {
MetaBox::addMetaBox(
'custom_seo_box',
'Custom SEO Options',
function () use ($object) {
return view('plugins/your-plugin::meta-box.seo', compact('object'))->render();
},
get_class($object),
$context
);
}
}, 10, 2);Adds custom form actions.
add_action(BASE_ACTION_FORM_ACTIONS, function ($form) {
// Example: Add a custom button to forms
echo '<a href="#" class="btn btn-warning preview-button" target="_blank">
<i class="fa fa-eye"></i> Preview
</a>';
// Example: Add a custom dropdown to forms
echo '<div class="btn-group">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown">
More Actions <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">Export as PDF</a></li>
<li><a href="#">Send to Email</a></li>
</ul>
</div>';
}, 10, 1);Executes after content creation.
add_action(BASE_ACTION_AFTER_CREATE_CONTENT, function ($screen, $request, $object) {
// Example: Send notification when a new post is created
if ($screen == POST_MODULE_SCREEN_NAME && $object) {
// Send email to admin
Mail::to(setting('admin_email'))
->send(new \App\Mail\NewPostCreated($object));
// Create an activity log
activity()
->performedOn($object)
->causedBy(auth()->user())
->log('Created new post: ' . $object->name);
// Ping search engines
if ($object->status == \Botble\Base\Enums\BaseStatusEnum::PUBLISHED) {
ping_search_engines(route('public.single', $object->slug));
}
}
}, 10, 3);Executes after content update.
add_action(BASE_ACTION_AFTER_UPDATE_CONTENT, function ($screen, $request, $object) {
// Example: Clear cache when a page is updated
if ($screen == PAGE_MODULE_SCREEN_NAME) {
Cache::forget('page_' . $object->id);
// Regenerate sitemap
if ($object->status == \Botble\Base\Enums\BaseStatusEnum::PUBLISHED) {
\Botble\Sitemap\Sitemap::generate();
}
}
}, 10, 3);Executes after content deletion.
add_action(BASE_ACTION_AFTER_DELETE_CONTENT, function ($screen, $request, $object) {
// Example: Clean up related data when a post is deleted
if ($screen == POST_MODULE_SCREEN_NAME) {
// Delete post metadata
MetaBox::deleteMetaData($object, 'custom_fields');
// Delete associated media
\Botble\Media\Models\MediaFile::query()
->where('reference_id', $object->id)
->where('reference_type', get_class($object))
->delete();
// Log the deletion
info('Post deleted: ' . $object->name);
}
}, 10, 3);Executes before content editing.
add_action(BASE_ACTION_BEFORE_EDIT_CONTENT, function ($request, $object) {
// Example: Check permissions or prepare data before editing
if ($object instanceof \Botble\Blog\Models\Post) {
// Check if user can edit this post
if ($object->author_id != auth()->id() && !auth()->user()->hasPermission('posts.edit')) {
abort(403, 'You do not have permission to edit this post.');
}
// Prepare additional data
$object->loadMissing(['categories', 'tags']);
}
}, 10, 2);Executes after user profile update.
add_action(USER_ACTION_AFTER_UPDATE_PROFILE, function ($screen, $request, $user) {
// Example: Sync user data with a CRM or external service
if ($user) {
// Sync with CRM
YourCrmService::updateContact([
'email' => $user->email,
'name' => $user->name,
'phone' => $request->input('phone'),
]);
// Update user preferences
UserPreference::updateOrCreate(
['user_id' => $user->id],
['preferences' => json_encode($request->input('preferences', []))]
);
}
}, 10, 3);Executes after user password update.
add_action(USER_ACTION_AFTER_UPDATE_PASSWORD, function ($screen, $request, $user) {
// Example: Send notification email when password is changed
if ($user) {
Mail::to($user->email)
->send(new \App\Mail\PasswordChanged([
'user' => $user,
'time' => now()->format('Y-m-d H:i:s'),
'ip' => $request->ip(),
]));
// Log the password change
activity()
->performedOn($user)
->causedBy($user)
->log('Changed password');
}
}, 10, 3);Executes after user logout.
add_action(AUTH_ACTION_AFTER_LOGOUT_SYSTEM, function ($screen, $user) {
// Example: Clear user-specific cache and log the logout
if ($user) {
// Clear user cache
Cache::forget('user_permissions_' . $user->id);
// Log the logout
activity()
->performedOn($user)
->causedBy($user)
->log('Logged out');
// Track with analytics
YourAnalyticsService::trackEvent('user_logout', [
'user_id' => $user->id,
'time' => now()->timestamp,
]);
}
}, 10, 2);- Use Appropriate Priority: Lower numbers run first. Use this to control execution order.
- Document Your Hooks: When creating custom hooks, document them for other developers.
- Check Existing Hooks: Before creating new hooks, check if existing ones can be used.
- Be Mindful of Performance: Hooks can impact performance if overused or poorly implemented.
- Use Namespaced Hook Names: For plugins, prefix hook names to avoid conflicts.
You can create your own hooks in your plugins or themes:
// Define a custom filter hook
$filteredValue = apply_filters('your_plugin_custom_filter', $originalValue, $additionalArg);
// Define a custom action hook
do_action('your_plugin_custom_action', $arg1, $arg2);Filters and actions provide a powerful way to extend and customize Botble without modifying core files. By understanding and using these hooks effectively, you can create flexible and maintainable code that integrates seamlessly with the Botble ecosystem.