Hook System
Hệ thống Hook của SkillDo CMS v8 được thiết kế theo mô hình WordPress Hook (Event-driven). Đây là cơ chế cốt lõi cho phép Plugin và Theme mở rộng hoặc thay đổi hành vi của CMS mà không cần sửa code gốc.
1. Hai Loại Hook
| Loại | Khi nào dùng | Trả về |
|---|---|---|
| Action | Thực hiện một hành động tại thời điểm nhất định | Không (void) |
| Filter | Biến đổi/lọc một giá trị trước khi dùng | Giá trị đã được lọc |
Quy tắc đơn giản:
- Cần làm gì đó (gửi mail, thêm class, xóa cache...) → dùng Action
- Cần thay đổi dữ liệu (sửa nội dung, thêm HTML, lọc mảng...) → dùng Filter
2. Action Hooks
add_action() — Đăng Ký Callback Cho Action
add_action(string $tag, callable $callback, int $priority = 10, int $accepted_args = 1): bool
| Tham số | Mô tả | Mặc định |
|---|---|---|
$tag | Tên event/hook | bắt buộc |
$callback | Hàm sẽ được gọi | bắt buộc |
$priority | Thứ tự thực thi (càng nhỏ càng chạy trước) | 10 |
$accepted_args | Số tham số mà callback nhận | 1 |
// Dạng function đơn giản
add_action('init', function () {
// Chạy khi CMS khởi tạo
});
// Dạng static method
add_action('init', [MyPlugin\Services\TaxonomyService::class, 'register']);
// Dạng instance method
$service = new MyService();
add_action('save_post', [$service, 'onPostSaved']);
// Với priority tùy chỉnh (chạy sau các hook priority 10)
add_action('init', function () {
// Chạy sau các handler có priority 10
}, 20);
// Nhận nhiều tham số từ do_action
add_action('user_created', function ($userId, $userData) {
// xử lý
}, 10, 2); // accepted_args = 2
do_action() — Phát Ra Action Event
Dùng trong code core/plugin để thông báo một sự kiện đã xảy ra:
do_action(string $tag, mixed ...$args): void
// Phát event đơn giản
do_action('my_plugin_loaded');
// Phát event kèm dữ liệu
do_action('order_completed', $orderId);
// Phát event với nhiều dữ liệu
do_action('user_created', $userId, $userData);
has_action() & did_action() — Kiểm Tra Action
// Kiểm tra xem có callback nào đăng ký cho hook chưa
if (has_action('my_plugin_loaded')) {
// có ít nhất 1 callback đăng ký
}
// Kiểm tra action đã được chạy bao nhiêu lần
$count = did_action('init');
if ($count > 0) {
// 'init' đã chạy rồi
}
remove_action() — Hủy Đăng Ký Callback
// Hủy một callback cụ thể (phải đúng priority ban đầu)
remove_action('init', [TaxonomyService::class, 'register'], 10);
// Hủy tất cả callbacks của một action
remove_all_actions('init');
3. Filter Hooks
add_filter() — Đăng Ký Callback Lọc Giá Trị
add_filter(string $tag, callable $callback, int $priority = 10, int $accepted_args = 1): true
// Filter đơn giản — nhận giá trị, trả về giá trị đã sửa
add_filter('the_content', function ($content) {
return $content . '<p>Bản quyền thuộc về tôi</p>';
});
// Filter với nhiều tham số (hook 'get_img' có sẵn trong core, truyền 2 tham số)
add_filter('get_img', function ($imgHtml, $params) {
// $params gồm: url, img, alt, params (attributes), type
return str_replace('<img', '<img class="post-thumb"', $imgHtml);
}, 10, 2); // accepted_args = 2
// Filter trong Plugin
add_filter('products_list_query', function ($query) {
$query->where('status', 'active');
return $query;
});
apply_filters() — Áp Dụng Filter Lên Giá Trị
Dùng trong code core/plugin để cho phép bên ngoài lọc một giá trị:
apply_filters(string $tag, mixed $value, mixed ...$args): mixed
// Cơ bản
$content = apply_filters('the_content', $content);
// Truyền thêm tham số để filter có thể dùng
$imgHtml = apply_filters('get_img', $imgHtml, $params);
// Trong Model hoặc Service
$items = apply_filters('my_plugin_product_list', $products, $categoryId);
has_filter() — Kiểm Tra Filter
has_filter(string $tag, callable|string|false $function_to_check = false)
if (has_filter('the_content')) {
// Có filter nào đó đang lọc the_content
}
remove_filter() — Hủy Đăng Ký Filter
remove_filter('the_content', 'my_custom_content_function', 10);
remove_all_filters('the_content');
current_filter() / doing_filter() — Kiểm Tra Hook Đang Chạy
// Tên hook (action hoặc filter) đang được thực thi
$tag = current_filter(); // alias: current_action()
// Có đang trong quá trình chạy một hook cụ thể không
if (doing_filter('the_content')) { ... } // alias: doing_action(...)
4. Tạo Hook Riêng Trong Plugin
Nếu Plugin của bạn muốn cho phép các Plugin/Theme khác mở rộng, bạn cần tự tạo hook ở những điểm thích hợp:
<?php
namespace MyPlugin\Services;
class ProductService
{
public function getProducts(array $filters = []): array
{
// Cho phép các plugin khác s ửa đổi $filters trước khi query
$filters = apply_filters('my_plugin_product_filters', $filters);
$products = Product::where($filters)->get();
// Cho phép các plugin khác sửa đổi kết quả sau khi query
$products = apply_filters('my_plugin_products_result', $products, $filters);
return $products;
}
public function createProduct(array $data): int
{
// Action hook trước khi tạo
do_action('my_plugin_before_create_product', $data);
$id = Product::create($data);
// Action hook sau khi tạo (truyền id luôn)
do_action('my_plugin_after_create_product', $id, $data);
return $id;
}
}
Plugin khác có thể hook vào:
// Thêm điều kiện lọc sản phẩm
add_filter('my_plugin_product_filters', function ($filters) {
$filters['featured'] = 1;
return $filters;
});
// Gửi notification sau khi tạo sản phẩm
add_action('my_plugin_after_create_product', function ($productId, $data) {
// Gửi email notification
\Mail::send('admin@site.com', 'Sản phẩm mới: ' . $data['title']);
}, 10, 2);
5. Priority — Thứ Tự Thực Hiện
- Priority mặc định là 10
- Số nhỏ hơn → chạy trước
- Số lớn hơn → chạy sau
- Nhiều callback cùng priority → chạy theo thứ tự đăng ký
add_action('init', function () {
echo 'Chạy thứ 2'; // priority 10 (mặc định)
});
add_action('init', function () {
echo 'Chạy đầu tiên'; // priority 1
}, 1);
add_action('init', function () {
echo 'Chạy cuối'; // priority 99
}, 99);
6. Các Hook Quan Trọng CMS Cung Cấp Sẵn
Action Hooks Hệ Thống
| Hook | Khi nào phát ra | Tham số |
|---|---|---|
cms_loaded | CMS Loader đã load xong các thành phần cơ bản. | — |
after_setup_admin | Khu vực Admin (views/admin) đã load xong (chỉ phát khi đang ở trang admin). | — |
plugins_loaded | Tất cả Plugin đang active đã load xong ServiceProvider. | — |
after_setup_theme | Theme (parent → child) đã load xong bootstrap/providers. | — |
init | CMS Controller khởi tạo xong (lúc bắt đầu vòng đời request). | — |
admin_init | Hệ thống khởi tạo riêng cho khu vực Admin backend. | — |
client_init / theme_init | Hệ thống khởi tạo riêng cho Theme frontend (client_init phát trước theme_init). | — |
ready | CMS Controller đã sẵn sàng thực thi logic trang hiện tại. | — |
shutdown | Cuối vòng đời request, trước khi gửi response. | $response, $request |
template_redirect | Trước khi Template render trang frontend. | $request |
admin_navigation | Thời điểm đăng ký menu sidebar Admin (AdminMenu::add). | — |
admin_header / admin_footer | Render trong layout Admin (in thêm assets/HTML vào head/footer admin). | — |
cle_header / cle_footer | Render trong layout frontend của theme (theme-store phát từ head.blade.php/footer — nơi theme đăng ký in CSS/JS, meta tags). | — |
skd_login | User đăng nhập thành công. | $username, $user |
skd_login_failed | Đăng nhập thất bại. | $username |
user_logout | User đăng xuất. | — |
user_register | Khi một User mới đăng ký/tạo thành công. | $userId |
profile_update | Khi thông tin User được cập nhật. | $userId |
deleted_user | Khi User bị xóa khỏi hệ thống. | $user |
set_user_role | Khi User được gán một role mới. | $userId, $role, $userRole |
remove_user_role | Khi một role bị gỡ khỏi User. | $userId, $role, $userRole |
plugin_active / plugin_deactivate / plugin_update / plugin_delete | Vòng đời Plugin trong trang quản lý Plugin. | $name (plugin id) |
add_{$name}_option / update_{$name}_option / delete_{$name}_option | Thêm/Cập nhật/Xóa một Option trong bảng system ($name là key option). | update: $option, $name, $value |
model_before_{$table}_update / model_before_{$table}_remove | Trước khi Eloquent Builder update/delete trên bảng $table. | $builder |
delete_{$table}_success | Sau khi xóa thành công bản ghi trên bảng $table. | $objects (danh sách id) |
admin_{$module}_table_column_{$name} | D ùng trong quản trị admin để in thêm HTML ra 1 cột trong bảng. | $item |
Filter Hooks Hệ Thống
| Hook | Mô tả | Tham số |
|---|---|---|
the_content | Lọc nội dung bài viết trước khi xuất ra view (VD: replace shortcode). Áp dụng trong helper the_content() (packages/skilldo/cms/src/Support/helpers.php). | $content |
get_url | Lọc URL (dùng cho đa ngôn ngữ hoặc rewrite rule). | $slug |
get_img | Lọc HTML thẻ <img> của helper SkillDo\Cms\Support\Image. | $html, $params |
get_img_link | Lọc URL ảnh của helper Image. | $link, $params |
cms_logo | Lọc/đổi logo của CMS Admin. | $logoPath |
authenticate | Can thiệp quá trình xác thực trong Auth::login (trả về SKD_Error để chặn login). | $user, $username, $password |
user_has_cap | Lọc danh sách capability của user trước khi check quyền. | $capabilities, $args ($args = [$cap, $userId]) |
role_has_cap | Lọc danh sách capability của một role trước khi check quyền. | $capabilities, $cap, $roleKey |
illegal_username | Lọc danh sách username bị cấm khi t ạo User (mặc định ['root']). | $usernames |
login_redirect_to | Lọc URL chuyển hướng sau đăng nhập (middleware RedirectIfAuthenticated). | $redirectTo |
get_data_menu | Lọc danh sách menu item khi render Theme Menu. | $menuItems |
admin_navigation_data | Lọc toàn bộ cấu trúc navigation Admin trước khi render. | $adminNav |
pre_insert_{$table}_check | Chặn/validate trước khi insert vào bảng $table (trả về SKD_Error để hủy). | $error, $builder |
post_detail_view / post_index_view / page_detail_view | Lọc/đổi template view render cho trang chi tiết/danh sách bài viết, chi tiết trang. | $view, $object |
admin_table_object_form_search | Thêm/Sửa các input trên thanh filter/search của bảng Admin. | $form |
admin_table_object_form_filter | Thêm/Sửa các input trên form filter của bảng Admin. | $form, $request |
7. Hook Trong Plugin — Ví Dụ Khởi Động
<?php
namespace MyPlugin\Providers;
use SkillDo\ServiceProvider;
use MyPlugin\Services\MyTaxonomyService;
class MyPluginServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Đăng ký Taxonomy ở hook 'init'
add_action('init', [MyTaxonomyService::class, 'register']);
// Ghi log sau khi CMS tải xong plugin
add_action('plugins_loaded', function () {
\Log::info('MyPlugin đã sẵn sàng chạy');
});
// Lọc nội dung bài viết để thêm text
add_filter('the_content', function ($content) {
$text = '<p class="my-plugin-copyright">Nội dung đã được đăng ký bản quyền</p>';
return $content . $text;
});
// Đổi logo ở Admin backend
add_filter('cms_logo', function ($logo) {
return asset('my-plugin::images/my-custom-logo.png');
});
}
}