Tham khảo về Móc & Bộ lọc: Hướng dẫn hoàn chỉnh dành cho nhà phát triển
Tham khảo móc & bộ lọc
Đối tượng: Nhà phát triển — Yêu cầu kiến thức về PHP / Laravel. Danh mục: Tham khảo kỹ thuật — Tài liệu kỹ thuật dành cho nhà phát triển tích hợp.
Bạn sẽ học được gì
-
Hiểu cơ chế Action vs Filter trong PolyCMS
-
Tra cứu tên hook, thông số, vị trí điều phối
-
Đăng ký hook từ theme
functions.phpvà moduleServiceProvider -
Sử dụng mức độ ưu tiên để kiểm soát thứ tự thực hiện
-
Áp dụng các use-case thực tế cho từng nhóm hook
1. Giới thiệu về Hook System
PolyCMS sử dụng hệ thống Hook/Filter giống như WordPress, được xây dựng nguyên bản trên Laravel:
-
Action (
Hook::doAction): Thực thi các tác dụng phụ (ghi nhật ký, gửi email, đồng bộ hóa dữ liệu). Không cần giá trị trả về. -
Filter (
Hook::applyFilters): Nhận một giá trị, sửa đổi nó và phải trả về giá trị đã sửa đổi.
Cú pháp cơ bản
use App\Facades\Hook;
// Register an Action
Hook::addAction('order_completed', function ($order) {
Mail::to($order->user)->send(new OrderCompletedMail($order));
}, priority: 10);
// Register a Filter
Hook::addFilter('post.default_image', function (?string $imageUrl, $post) {
if ($post?->categories->contains('slug', 'news')) {
return '/images/news-default.jpg';
}
return $imageUrl;
}, priority: 10);
2. Nội dung & Bài viết
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
post.default_image |
$imageUrl, $context |
Hình ảnh mặc định khi bài viết không có ảnh nổi bật |
post.frontend_url |
$url, $post |
Tùy chỉnh URL công khai của bài đăng |
post.content.render |
$html, $post |
Lọc nội dung HTML của bài đăng trước khi quay lại API |
post.query.builder |
$query, $request |
Sửa đổi truy vấn Eloquent cho danh sách bài viết |
category.frontend_url |
$url, $category |
Tùy chỉnh URL công khai của một danh mục |
content.render.blocks |
$blocks |
Lọc mảng khối trước khi kết xuất |
content.render.html |
$html, $blocks |
Lọc HTML đầu ra cuối cùng |
content.render.block.{type} |
$html, $block |
Hiển thị hoặc ghi đè một loại khối cụ thể |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
tag.saved |
$tag, $context |
Được kích hoạt sau khi thẻ được tạo/cập nhật |
tag.deleted |
$tag, $context |
Được kích hoạt sau khi thẻ bị xóa |
category.saved |
$category, $context |
Được kích hoạt sau khi một danh mục được tạo/cập nhật |
category.deleted |
$category, $context |
Được kích hoạt sau khi một danh mục bị xóa |
Các trường hợp sử dụng thực tế
UC1 — Hình thu nhỏ mặc định dành riêng cho danh mục: Giữ hình thu nhỏ mặc định cụ thể cho từng danh mục khi bài đăng không có hình ảnh nổi bật:
Hook::addFilter('post.default_image', function (?string $url, $post) {
if ($post?->categories->contains('slug', 'tech')) return '/images/defaults/tech.jpg';
if ($post?->categories->contains('slug', 'lifestyle')) return '/images/defaults/lifestyle.jpg';
return $url; // fallback to admin setting
});
UC2 — Cấu trúc URL Wiki: Giảm hạng/thay đổi cấu trúc URL bài đăng từ /posts/slug thành /docs/slug để có bố cục kiểu wiki:
Hook::addFilter('post.frontend_url', function ($url, $post) {
return ($post->type === 'wiki') ? '/docs/' . $post->slug : $url;
});
UC3 — Tự động chèn quảng cáo nội tuyến: Tự động chèn các khối biểu ngữ quảng cáo sau đoạn thứ 3 của bài đăng:
Hook::addFilter('content.render.html', function (string $html) {
$adBanner = '<div class="ad-inline">Advertisement</div>';
$parts = explode('</p>', $html, 4);
if (count($parts) > 3) {
$parts[2] .= '</p>' . $adBanner;
return implode('</p>', $parts);
}
return $html;
});
3. Thư viện phương tiện
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
media.upload.file |
$file, $data |
Sửa đổi tệp đã tải lên trước khi xử lý |
media.upload.data |
$data, $file |
Sửa đổi siêu dữ liệu tải lên |
media.create.data |
$mediaData, $file |
Sửa đổi thuộc tính bản ghi phương tiện trước khi lưu vào DB |
media.delete.should |
$shouldDelete, $media |
Cổng bảo mật: trả về false để chặn xóa phương tiện |
media.url |
$url, $media |
Viết lại URL (ví dụ: để chuyển hướng đến CDN) |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
media.uploaded |
$media, $file, $data |
Được kích hoạt sau khi tải lên thành công |
media.deleting |
$media |
Kích hoạt trước khi xóa |
media.deleted |
$media |
Kích hoạt sau khi xóa |
Các trường hợp sử dụng thực tế
UC1 — Viết lại URL CDN: Tích hợp với Cloudflare R2 hoặc Amazon S3, tự động chuyển đổi đường dẫn lưu trữ cục bộ sang URL CDN:
Hook::addFilter('media.url', function (string $url, $media) {
return str_replace('/storage/', 'https://cdn.mysite.com/', $url);
});
UC2 — Bảo vệ tài sản hệ thống quan trọng: Ngăn chặn việc vô tình xóa bất kỳ hình ảnh nào hiện đang được sử dụng làm biểu trưng của trang web:
Hook::addFilter('media.delete.should', function (bool $allow, $media) {
$logoUrl = get_option('site_logo', null, 'general');
return ($media->url === $logoUrl) ? false : $allow;
});
UC3 — Tự động tối ưu hóa hình ảnh khi tải lên: Gửi hàng đợi nền để tạo hình ảnh WebP nhẹ từ các video tải lên tiêu chuẩn:
Hook::addAction('media.uploaded', function ($media, $file) {
if (str_starts_with($media->mime_type, 'image/')) {
dispatch(new ConvertToWebpJob($media));
}
});
4. Thương mại điện tử - Đơn đặt hàng & Hoàn tiền
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
order_status_updated |
$order, $oldStatus, $newStatus |
Được kích hoạt khi trạng thái đơn hàng thay đổi |
order_completed |
$order |
Được kích hoạt khi đơn hàng được hoàn thành thành công |
order.refund.processing |
$order, $validated |
Được kích hoạt trước khi xử lý khoản tiền hoàn lại |
order.refund.completed |
$order, $result |
Kích hoạt sau khi hoàn tiền thành công |
order.refund.succeeded |
$order, $result, $validated, $userId |
Hoàn tiền API thành công |
order.refund.failed |
$order, $validated, $exception, $userId |
Hoàn tiền API không thành công |
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
order.refund.preview.result |
$preview, $order, $input |
Sửa đổi dữ liệu xem trước hoàn tiền |
Các trường hợp sử dụng thực tế
UC1 — Thông báo Telegram cho đơn đặt hàng mới:
Hook::addAction('order_status_updated', function ($order, $old, $new) {
if ($old === 'pending' && $new === 'processing') {
TelegramBot::send(" Order #{$order->code} has been confirmed!");
}
});
UC2 — Điểm thưởng trung thành khi hoàn thành đơn hàng:
Hook::addAction('order_completed', function ($order) {
if ($order->user_id) {
$points = (int) floor($order->total / 10); // 1 point per $10 spent
LoyaltyService::addPoints($order->user_id, $points, "Order #{$order->code}");
}
});
UC3 — Ghi nhật ký kiểm tra các khoản tiền hoàn lại không thành công:
Hook::addAction('order.refund.failed', function ($order, $data, $exception, $userId) {
AuditLog::create([
'action' => 'refund_failed',
'order_id' => $order->id,
'user_id' => $userId,
'reason' => $exception->getMessage(),
]);
});
5. Thương mại điện tử - Giỏ hàng, Vận chuyển, Thuế, Hàng tồn kho
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
cart.totals |
$totals, $cart |
Sửa đổi tổng số giỏ hàng |
shipping.calculate_cost |
$cost, $method, $cart |
Ghi đè chi phí vận chuyển |
shipping.available_methods |
$methods, $address, $cart |
Lọc phương thức giao hàng dựa trên địa chỉ và nội dung giỏ hàng |
tax.calculated |
$result, $subtotal, $address |
Ghi đè kết quả tính thuế |
inventory.is_stockable_product |
$default, $product, $context |
Kiểm tra xem sản phẩm có theo dõi mức tồn kho hay không |
review.can_submit |
$allowed, $user, $product |
Cổng bảo mật: cho phép người dùng gửi đánh giá? |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
cart.item.added |
$item, $cart |
Được kích hoạt sau khi một mặt hàng được thêm vào giỏ hàng |
cart.item.updated |
$item, $oldQty, $newQty |
Được kích hoạt sau khi số lượng mặt hàng thay đổi |
cart.item.removed |
$item, $cart |
Được kích hoạt sau khi một mục bị xóa |
cart.cleared |
$cart |
Được kích hoạt sau khi xóa giỏ hàng |
cart.merged |
$userCart, $guestCart |
Được kích hoạt khi giỏ hàng của khách hợp nhất vào giỏ hàng của người dùng đã được xác thực |
review.submitted |
$review |
Được kích hoạt sau khi gửi đánh giá |
review.approved |
$review |
Kích hoạt sau khi phê duyệt đánh giá |
review.rejected |
$review |
Được kích hoạt sau khi từ chối đánh giá |
Các trường hợp sử dụng thực tế
UC1 — Giao hàng miễn phí cho đơn hàng trên $100:
Hook::addFilter('shipping.calculate_cost', function ($cost, $method, $cart) {
$subtotal = collect($cart->items)->sum(fn($i) => $i->price * $i->quantity);
return ($subtotal >= 100) ? 0 : $cost;
});
UC2 — Chặn đánh giá nếu người dùng chưa mua sản phẩm:
Hook::addFilter('review.can_submit', function (bool $allowed, $user, $product) {
$hasPurchased = Order::where('user_id', $user->id)
->where('status', 'completed')
->whereHas('items', fn($q) => $q->where('product_id', $product->id))
->exists();
return $hasPurchased;
});
UC3 — Theo dõi pixel của Facebook cho sự kiện AddToCart:
Hook::addAction('cart.item.added', function ($item, $cart) {
session()->push('fb_pixel_events', [
'event' => 'AddToCart',
'product_id' => $item->product_id,
'value' => $item->price,
]);
});
6. Thương mại điện tử - Cổng thanh toán
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
payment.gateway.config_schema |
$schema, $gateway |
Mở rộng các trường cấu hình lược đồ cho một cổng |
Ca sử dụng thực tế: Thêm trường tùy chỉnh vào cổng Chuyển khoản ngân hàng:
Hook::addFilter('payment.gateway.config_schema', function ($schema, $gateway) {
if ($gateway->code === 'bank_transfer') {
$schema['branch_code'] = [
'type' => 'text', 'label' => 'Branch Code', 'required' => false,
];
}
return $schema;
});
7. Chủ đề & Hình thức
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
theme.view.data |
$data, $viewName |
Chèn/sửa đổi các biến dạng xem cho bất kỳ mẫu Blade nào |
theme.template.resolve |
$viewName, $templateTheme, $entityType, $entity |
Ghi đè các chế độ xem Blade phù hợp |
theme.template.registry |
$templates, $viewType |
Đăng ký mẫu trang mới |
theme.options.values |
$options |
Lọc các giá trị tùy chọn chủ đề đã tải |
theme.options.css_vars |
$cssVars, $themeOptionValues |
Sửa đổi thuộc tính CSS tùy chỉnh |
theme.breadcrumbs.post |
$breadcrumbs, $post |
Lọc bài viết vụn bánh mì |
theme.breadcrumbs.product |
$breadcrumbs, $product |
Lọc mẩu bánh mì sản phẩm |
theme.show_page_header |
$show, $page |
Chuyển đổi khả năng hiển thị của tiêu đề trang |
frontend.topbar.banners |
$banners |
Tiêm biểu ngữ quảng cáo thanh trên cùng |
themes.list |
$themes |
Lọc danh sách các chủ đề có sẵn trong quản trị viên |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
theme.activated |
$theme |
Được kích hoạt khi một chủ đề được kích hoạt |
theme.main.changed |
$theme, $oldMainTheme |
Kích hoạt khi thay đổi chủ đề chính |
theme.installing |
$file |
Được kích hoạt trước khi giải nén ZIP chủ đề |
theme.activating |
$slug, $type, $mode |
Được kích hoạt trước khi kích hoạt một chủ đề |
theme.deactivating |
$slug |
Được kích hoạt trước khi tắt chủ đề |
theme.deleting |
$theme |
Được kích hoạt trước khi xóa chủ đề |
cms_head |
(không có) | Móc đầu ra bên trong thẻ <head> của bố cục |
Các trường hợp sử dụng thực tế
UC1 — Chèn biểu ngữ quảng cáo từ mô-đun:
Hook::addFilter('frontend.topbar.banners', function (array $banners) {
$banners[] = [
'text' => ' Flash Sale — 30% Off Today!',
'url' => '/sale',
'bg_color' => '#ff4444',
];
return $banners;
});
UC2 — Phần chèn đầu của Google Analytics:
Hook::addAction('cms_head', function () {
$ga = get_option('google_analytics_id', null, 'seo');
if ($ga) {
echo "<script async src='https://www.googletagmanager.com/gtag/js?id={$ga}'></script>";
}
});
UC3 — Tải biến thanh bên các bài đăng phổ biến:
Hook::addFilter('theme.view.data', function ($data, $viewName) {
if ($viewName === 'posts.index') {
$data['popular_posts'] = Post::orderBy('views', 'desc')->limit(5)->get();
}
return $data;
});
8. Cài đặt & Cấu hình
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
settings.defaults |
$defaults, $settingsService |
Mở rộng hoặc ghi đè các định nghĩa cài đặt cơ sở |
settings.media.drivers |
$drivers |
Đăng ký trình điều khiển lưu trữ phương tiện mới |
settings.permalinks.structure |
$structure, $settingsService |
Hệ thống lọc permalinks |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
setting.updating |
$key, $value, $group, $type |
Được kích hoạt trước khi lưu cài đặt |
settings.saved |
$payload |
Được kích hoạt sau khi cài đặt được lưu |
Các trường hợp sử dụng thực tế
UC1 — Đăng ký phần cài đặt tùy chỉnh qua Mô-đun:
Hook::addFilter('settings.defaults', function ($defaults) {
$defaults['mymodule'] = [
'mymodule_api_key' => [
'key' => 'mymodule_api_key', 'value' => '', 'type' => 'text',
'label' => 'API Key', 'description' => 'Enter your API key',
],
];
return $defaults;
});
UC2 — Xóa bộ đệm định tuyến trên bản cập nhật Permalinks:
Hook::addAction('settings.saved', function ($payload) {
if (($payload['group'] ?? '') === 'permalinks') {
Artisan::call('route:clear');
Cache::tags('routes')->flush();
}
});
9. Người dùng, Vai trò & Xác thực
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
user.resource.to_array |
$data, $user, $request |
Mở rộng đầu ra tài nguyên API người dùng |
auth.login.pre_token |
$response, $user, $request |
Chặn vòng đời đăng nhập (ví dụ: đối với Xác thực đa yếu tố) |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
user.creating / user.updating / user.deleting |
`$user | $data` |
role.creating / role.updating / role.deleting / role.cloning |
`$role | $data` |
Các trường hợp sử dụng thực tế
UC1 — Bộ chặn xác thực đa yếu tố:
Hook::addFilter('auth.login.pre_token', function ($response, $user, $request) {
if ($user->hasRole('admin') && !$request->filled('otp_code')) {
return response()->json(['requires_2fa' => true, 'user_id' => $user->id], 403);
}
return $response; // null = continue token generation
});
UC2 — Email chào mừng khi đăng ký người dùng:
Hook::addAction('user.creating', function ($data) {
dispatch(new SendWelcomeEmail($data['email'], $data['name']));
});
##10. SEO
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
seo.canonical_url |
$url |
Ghi đè URL chuẩn |
seo.site_favicon |
$iconUrl |
Ghi đè đường dẫn favicon |
Trường hợp sử dụng thực tế - URL chuẩn cho thiết lập đa ngôn ngữ:
Hook::addFilter('seo.canonical_url', function (string $url) {
$locale = app()->getLocale();
if ($locale !== 'en') {
return url("/{$locale}" . parse_url($url, PHP_URL_PATH));
}
return $url;
});
11. Tiện ích
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
widget.render.instance |
$instance |
Sửa đổi cài đặt phiên bản tiện ích trước khi hiển thị |
widget.render.{widget_type} |
$widget |
Sửa đổi dữ liệu widget cụ thể |
widget.area.render.instances |
$instances, $area |
Lọc các phiên bản tiện ích được tải trong một khu vực |
widget.render.output |
$html, $instance |
Lọc đầu ra HTML của một tiện ích |
widget.area.render.output |
$html, $area |
Lọc kết xuất đầu ra của một khu vực widget |
widgets.types |
$widgets |
Danh sách lọc các loại widget đã đăng ký |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
widgets.register_types |
$widgetManager |
Đăng ký các loại widget mới |
widgets.register_areas |
$widgetManager |
Đăng ký khu vực widget mới |
Ca sử dụng thực tế — Đăng ký loại tiện ích "Bản đồ cửa hàng":
Hook::addAction('widgets.register_types', function ($manager) {
$manager->registerType('store_map', [
'label' => 'Store Map',
'description' => 'Google Maps showing store locations',
'fields' => [
'api_key' => ['type' => 'text', 'label' => 'Google Maps API Key'],
'lat' => ['type' => 'text', 'label' => 'Latitude'],
'lng' => ['type' => 'text', 'label' => 'Longitude'],
],
'view' => 'widgets.store-map',
]);
});
12. Điều hướng & Biên tập của Quản trị viên
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
topbar.menu.items |
$items, $request, $user |
Thêm/xóa các mục khỏi thanh quản trị giao diện người dùng |
topbar.menu.should_show |
$show, $user |
Chuyển đổi khả năng hiển thị của thanh quản trị giao diện người dùng hàng đầu |
admin.editor.panels |
$panels, $type, $user |
Thêm bảng tùy chỉnh vào thanh bên của trình chỉnh sửa khối |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
admin.menu.build |
(không có) | Được kích hoạt khi lắp ráp menu điều hướng thanh bên |
topbar.menu.context |
$request, $user |
Được kích hoạt khi thanh quản trị giao diện người dùng được khởi tạo |
Trường hợp sử dụng thực tế - Thêm mục menu trạng thái SEO tùy chỉnh vào thanh trên cùng:
Hook::addFilter('topbar.menu.items', function (array $items, $request, $user) {
$items[] = [
'key' => 'seo_score',
'label' => 'SEO Score',
'icon' => 'chart-bar',
'url' => '/admin/seo/score',
'position' => 50,
];
return $items;
});
13. Mô-đun
Bộ lọc
| Móc | Thông số | Mô tả |
|---|---|---|
module.resource.meta |
$meta, $moduleData |
Mở rộng chi tiết siêu dữ liệu mô-đun |
modules.list |
$modulesArray |
Lọc các mô-đun được liệt kê trong quản trị viên |
product.query.builder |
$query, $request |
Sửa đổi sản phẩm Truy vấn danh sách Eloquent |
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
module.activating |
$moduleKey, $module |
Được kích hoạt trước khi kích hoạt mô-đun |
module.deactivating |
$moduleKey, $module |
Được kích hoạt trước khi tắt mô-đun |
module.deleting |
$moduleKey, $module |
Được kích hoạt trước khi xóa mô-đun |
module.installing |
$uploadedFile |
Được kích hoạt khi tải lên các ZIP mô-đun mới |
Ca sử dụng thực tế — Tự động chạy di chuyển khi kích hoạt mô-đun:
Hook::addAction('module.activating', function ($moduleKey, $module) {
Artisan::call('migrate', [
'--path' => "modules/Polyx/{$moduleKey}/database/migrations",
'--force' => true,
]);
});
14. Khởi động hệ thống
Hành động
| Móc | Thông số | Mô tả |
|---|---|---|
roles.register_permissions |
$permissionRegistry |
Đăng ký quyền hệ thống mới |
register_email_templates |
$emailTemplateManager |
Đăng ký mẫu email cốt lõi |
layout.register_assets |
$layoutAssetManager |
Đăng ký nội dung bố cục quản trị viên/giao diện người dùng tùy chỉnh |
routes.frontend.register |
(không có) | Tiêm các tuyến giao diện người dùng tùy chỉnh |
Trường hợp sử dụng thực tế - Mô-đun đăng ký các khả năng/quyền tùy chỉnh:
Hook::addAction('roles.register_permissions', function ($registry) {
$registry->register('manage analytics', 'Analytics', 'View and manage analytics dashboard');
$registry->register('export reports', 'Analytics', 'Export analytics reports to CSV');
});
##Bài viết liên quan
-
Tổng quan về Hooks & Filters — Tổng quan chung về hệ thống sự kiện
-
Phát triển chủ đề — Bắt đầu với các chủ đề tùy chỉnh
-
Phát triển mô-đun — Bắt đầu với các mô-đun tùy chỉnh
-
Xác thực API REST — Xác thực ứng dụng khách API