Back to Main Site

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

Last updated on Jun 24, 2026 02:01

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.php và module ServiceProvider

  • 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