Chuyển tới nội dung chính
Phiên bản: 8.0.0

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ạiKhi nào dùngTrả về
ActionThực hiện một hành động tại thời điểm nhất địnhKhông (void)
FilterBiến đổi/lọc một giá trị trước khi dùngGiá 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
$tagTên event/hookbắt buộc
$callbackHàm sẽ được gọibắt buộc
$priorityThứ tự thực thi (càng nhỏ càng chạy trước)10
$accepted_argsSố tham số mà callback nhận1
// 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​

HookKhi nào phát raTham số
cms_loadedCMS Loader đã load xong các thành phần cơ bản.—
after_setup_adminKhu vực Admin (views/admin) đã load xong (chỉ phát khi đang ở trang admin).—
plugins_loadedTất cả Plugin đang active đã load xong ServiceProvider.—
after_setup_themeTheme (parent → child) đã load xong bootstrap/providers.—
initCMS Controller khởi tạo xong (lúc bắt đầu vòng đời request).—
admin_initHệ thống khởi tạo riêng cho khu vực Admin backend.—
client_init / theme_initHệ thống khởi tạo riêng cho Theme frontend (client_init phát trước theme_init).—
readyCMS Controller đã sẵn sàng thực thi logic trang hiện tại.—
shutdownCuối vòng đời request, trước khi gửi response.$response, $request
template_redirectTrước khi Template render trang frontend.$request
admin_navigationThời điểm đăng ký menu sidebar Admin (AdminMenu::add).—
admin_header / admin_footerRender trong layout Admin (in thêm assets/HTML vào head/footer admin).—
cle_header / cle_footerRender 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_loginUser đăng nhập thành công.$username, $user
skd_login_failedĐăng nhập thất bại.$username
user_logoutUser đăng xuất.—
user_registerKhi một User mới đăng ký/tạo thành công.$userId
profile_updateKhi thông tin User được cập nhật.$userId
deleted_userKhi User bị xóa khỏi hệ thống.$user
set_user_roleKhi User được gán một role mới.$userId, $role, $userRole
remove_user_roleKhi một role bị gỡ khỏi User.$userId, $role, $userRole
plugin_active / plugin_deactivate / plugin_update / plugin_deleteVòng đời Plugin trong trang quản lý Plugin.$name (plugin id)
add_{$name}_option / update_{$name}_option / delete_{$name}_optionThê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}_removeTrước khi Eloquent Builder update/delete trên bảng $table.$builder
delete_{$table}_successSau 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​

HookMô tảTham số
the_contentLọ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_urlLọc URL (dùng cho đa ngôn ngữ hoặc rewrite rule).$slug
get_imgLọc HTML thẻ <img> của helper SkillDo\Cms\Support\Image.$html, $params
get_img_linkLọc URL ảnh của helper Image.$link, $params
cms_logoLọc/đổi logo của CMS Admin.$logoPath
authenticateCan thiệp quá trình xác thực trong Auth::login (trả về SKD_Error để chặn login).$user, $username, $password
user_has_capLọc danh sách capability của user trước khi check quyền.$capabilities, $args ($args = [$cap, $userId])
role_has_capLọc danh sách capability của một role trước khi check quyền.$capabilities, $cap, $roleKey
illegal_usernameLọc danh sách username bị cấm khi tạo User (mặc định ['root']).$usernames
login_redirect_toLọc URL chuyển hướng sau đăng nhập (middleware RedirectIfAuthenticated).$redirectTo
get_data_menuLọc danh sách menu item khi render Theme Menu.$menuItems
admin_navigation_dataLọc toàn bộ cấu trúc navigation Admin trước khi render.$adminNav
pre_insert_{$table}_checkChặ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_viewLọ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_searchThêm/Sửa các input trên thanh filter/search của bảng Admin.$form
admin_table_object_form_filterThê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');
});
}
}