Back to Main Site

Tham khảo về Móc & Bộ lọc lõi: Hướng dẫn hoàn chỉnh dành cho nhà phát triển

Last updated on Jun 24, 2026 00:33

Tham khảo móc lõi & 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: Tài liệu tham khảo kỹ thuật — Tài liệu kỹ thuật dành cho lập trình viên tích hợp.

Bạn sẽ học được gì

  • Tìm hiểu cơ chế Action vs Filter trong PolyCMS
  • [] Tra cứu tên hook, thông số, vị trí gửi
  • Biết đă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 trường hợp sử dụng thực tế cho từng nhóm hook

##1. Giới thiệu hệ thống Hook

PolyCMS sử dụng hệ thống Hook/Filter tương tự như WordPress, được xây dựng nguyên bản trên Laravel:

  • Action (Hook::doAction): Chạy tác dụng phụ (ghi nhật ký, gửi email, đồng bộ hóa dữ liệu). Không cần phải quay lại.
  • Filter (Hook::applyFilters): Nhận một giá trị, chỉnh sửa và buộc trả về giá trị đã điều chỉnh.

Cú pháp cơ bản

use App\Facades\Hook;

// Sign up for Action
Hook::addAction('order_completed', function ($order) {
    Mail::to($order->user)->send(new OrderCompletedMail($order));
}, priority: 10);

// Sign up for 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 đại diện
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 trước khi trả về API
post.query.builder $query, $request Chỉnh sửa 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 danh mục của bạn
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 Sau khi thẻ được tạo/cập nhật
tag.deleted $tag, $context Sau khi thẻ bị xóa
category.saved $category, $context Sau khi danh mục được tạo/cập nhật
category.deleted $category, $context Sau khi danh mục bị xóa

Các trường hợp sử dụng thực tế

UC1 — Ảnh đại diện theo danh mục: Trang tin muốn mỗi danh mục có ảnh mặc định riêng khi bài viết chưa đặt ảnh đại diện:

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 admin setting
});

UC2 — URL Wiki: Chủ đề tài liệu muốn di chuyển URL bài đăng từ /posts/slug sang /docs/slug:

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: Module quảng cáo muốn chèn banner sau đoạn thứ 3 trong nội dung bài viết:

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 Chỉnh sửa tập tin trước khi xử lý
media.upload.data $data, $file Chỉnh sửa siêu dữ liệu tải lên
media.create.data $mediaData, $file Điều chỉnh dữ liệu bản ghi trước khi lưu DB
media.delete.should $shouldDelete, $media Cổng: trả về false để tránh bị xóa
media.url $url, $media Viết lại URL (e.g. CDN)

Hành động

Móc Thông số Mô tả
media.uploaded $media, $file, $data Sau khi tải lên thành công
media.deleting $media Trước khi xóa
media.deleted $media Sau khi xóa

Các trường hợp sử dụng thực tế

UC1 — CDN rewrite: Tích hợp Cloudflare R2/S3, tự động chuyển đổi URL phương tiện sang miền CDN:

Hook::addFilter('media.url', function (string $url, $media) {
    return str_replace('/storage/', 'https://cdn.mysite.com/', $url);
});

UC2 — Bảo vệ các tệp quan trọng: Ngăn chặn việc xóa hình ảnh đang được sử dụng làm biểu tượng 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 khi tải lên: Tự động tạo WebP từ hình ảnh đã tải lê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 Khi trạng thái đơn hàng thay đổi
order_completed $order Khi đơn hàng hoàn thành
order.refund.processing $order, $validated Trước khi hoàn tiền
order.refund.completed $order, $result 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 Điều chỉnh 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 khi có đơn 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 — Kiếm điểm khách hàng thân thiết khi hoàn thành đơn đăng ký:

Hook::addAction('order_completed', function ($order) {
    if ($order->user_id) {
        $points = (int) floor($order->total / 10000); // 1 point per 10k
        LoyaltyService::addPoints($order->user_id, $points, "Order #{$order->code}");
    }
});

UC3 — Ghi lại nhật ký kiểm tra khi hoàn tiền 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 Điều chỉnh tổng số giỏ hàng
shipping.calculate_cost $cost, $method, $cart Ghi đè phí vận chuyển
shipping.available_methods $methods, $address, $cart Lọc phương thức vận chuyển
tax.calculated $result, $subtotal, $address Ghi đè kết quả tính thuế
inventory.is_stockable_product $default, $product, $context Xác định sản phẩm bằng quản lý hàng tồn kho
review.can_submit $allowed, $user, $product Cổng: nó có cho phép đánh giá không?

Hành động

Móc Thông số Mô tả
cart.item.added $item, $cart Sau khi thêm mặt hàng vào giỏ hàng
cart.item.updated $item, $oldQty, $newQty Sau khi cập nhật số lượng
cart.item.removed $item, $cart Sau khi xóa các mặt hàng khỏi giỏ hàng
cart.cleared $cart Sau khi xóa toàn bộ giỏ hàng
cart.merged $userCart, $guestCart Khi gộp giỏ hàng của khách vào giỏ hàng của người dùng
review.submitted $review Sau khi gửi đánh giá
review.approved $review Sau khi xem xét đánh giá
review.rejected $review Sau khi từ chối đánh giá

Các trường hợp sử dụng thực tế

UC1 — Miễn phí vận chuyển cho đơn hàng trên 500k:

Hook::addFilter('shipping.calculate_cost', function ($cost, $method, $cart) {
    $subtotal = collect($cart->items)->sum(fn($i) => $i->price * $i->quantity);
    return ($subtotal >= 500000) ? 0 : $cost;
});

UC2 — Chặn đánh giá nếu bạn chưa mua:

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 khi thêm giỏ hàng:

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 lược đồ cấu hình cổng

Use-case: Thêm trường "Mã chi nhánh" cho 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 Đưa dữ liệu vào bất kỳ chế độ xem nào
theme.template.resolve $viewName, $templateTheme, $entityType, $entity Chế độ xem Blade ghi đè được hiển thị
theme.template.registry $templates, $viewType Đăng ký mẫu trang mới
theme.options.values $options Lọc các tùy chọn chủ đề
theme.options.css_vars $cssVars, $themeOptionValues Điều chỉnh thuộc tính tùy chỉnh CSS
theme.breadcrumbs.post $breadcrumbs, $post Chỉnh sửa mẩu tin bài viết
theme.breadcrumbs.product $breadcrumbs, $product Chỉnh sửa đường dẫn sản phẩm
theme.show_page_header $show, $page Ẩn/hiện tiêu đề trang
frontend.topbar.banners $banners Tiêm biểu ngữ quảng cáo
themes.list $themes Lọc danh sách chủ đề quản trị viên

Hành động

Móc Thông số Mô tả
theme.activated $theme Khi chủ đề chính được tải
theme.main.changed $theme, $oldMainTheme Khi thay đổi chủ đề chính
theme.installing $file Trước khi xử lý chủ đề ZIP
theme.activating $slug, $type, $mode Trước khi kích hoạt chủ đề
theme.deactivating $slug Trước khi tắt chủ đề
theme.deleting $theme Trước khi xóa chủ đề
cms_head (không có) Móc đầu ra trong <head>

Các trường hợp sử dụng thực tế

UC1 — Biểu ngữ quảng cáo trên toàn trang web 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 — Đưa Google Analytics vào <head>:

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 — Chèn dữ liệu thanh bên cho trang blog:

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/ghi đè định nghĩa cài đặt
settings.media.drivers $drivers Đăng ký driver lưu trữ mới
settings.permalinks.structure $structure, $settingsService Điều chỉnh cấu trúc permalink

Hành động

Móc Thông số Mô tả
setting.updating $key, $value, $group, $type Trước khi lưu cài đặt
settings.saved $payload Sau khi lưu cài đặt

Các trường hợp sử dụng thực tế

UC1 — Mô-đun đăng ký các cài đặt riêng lẻ trên trang Cài đặt:

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ộ nhớ đệm khi cập nhật liên kết cố định:

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 phản hồi API của người dùng
auth.login.pre_token $response, $user, $request Chặn đăng nhập (đối với 2FA)

Hành động

Móc Thông số Mô tả
user.creating / user.updating / user.deleting $user|$data Vòng đời CRUD
role.creating / role.updating / role.deleting / role.cloning $role|$data Vòng đời CRUD

Các trường hợp sử dụng thực tế

UC1 — Yêu cầu 2FA đối với quản trị viên:

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 creating tokens
});

UC2 — Gửi email chào mừng khi tạo người dùng:

Hook::addAction('user.creating', function ($data) {
    // Queued email will be sent after the user is saved
    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 đè URL biểu tượng yêu thích

Ca sử dụng - URL chuẩn cho đ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 Điều chỉnh phiên bản widget trước khi hiển thị
widget.render.{widget_type} $widget Chỉnh sửa dữ liệu widget cụ thể
widget.area.render.instances $instances, $area Lọc các trường hợp trong khu vực
widget.render.output $html, $instance Lọc tiện ích đầu ra HTML
widget.area.render.output $html, $area Lọc vùng đầu ra HTML
widgets.types $widgets Lọc các loại tiện ích quản trị viên

Hành động

Móc Thông số Mô tả
widgets.register_types $widgetManager Đăng ký loại widget mới
widgets.register_areas $widgetManager Đăng ký khu vực widget mới

Ca sử dụng — Đăng ký loại widget "Store Map":

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 mục thanh trên cùng của giao diện người dùng
topbar.menu.should_show $show, $user Chuyển đổi hiển thị thanh trên cùng
admin.editor.panels $panels, $type, $user Thêm bảng tùy chỉnh vào trình chỉnh sửa khối

Hành động

Móc Thông số Mô tả
admin.menu.build (không có) Khi menu quản trị thanh bên được xây dựng
topbar.menu.context $request, $user Khi bối cảnh thanh trên cùng được khởi tạo

Ca sử dụng - Mô-đun thêm mục menu "Điểm SEO" 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 mô-đun siêu dữ liệu
modules.list $modulesArray Lọc danh sách mô-đun quản trị
product.query.builder $query, $request Điều chỉnh truy vấn Eloquent cho sản phẩm

Hành động

Móc Thông số Mô tả
module.activating $moduleKey, $module Trước khi kích hoạt mô-đun
module.deactivating $moduleKey, $module Trước khi tắt mô-đun
module.deleting $moduleKey, $module Trước khi xóa mô-đun
module.installing $uploadedFile Khi cài đặt ZIP

Use-case — Chạy Migration khi kích hoạt module:

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 tùy chỉnh
register_email_templates $emailTemplateManager Đăng ký nhận mẫu email
layout.register_assets $layoutAssetManager Đăng ký tài sản bố cục
routes.frontend.register (không có) Đăng ký các tuyến giao diện bổ sung

Use-case — Module đăng ký quyền riêng tư:

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