Xây dựng bất cứ thứ gì. Không cần fork core.
AcelleMail là nền tảng email marketing chạy trên Laravel 11+, với mặt phẳng mở rộng đi xa hơn callback rất nhiều. Sending driver, payment gateway, AI agent, trang admin tuỳ biến, các slot UI ở cấp page, REST endpoint, lifecycle listener — tất cả đều ship dưới dạng một plugin tự đóng gói, nằm trong namespace của chính bạn. Đây là tham khảo đầy đủ: mười một bài đào sâu, dẫn nguồn trực tiếp tới các code path chính tắc trong storage/app/plugins/, App\Library\HookManager, và app/Model/Plugin.php.
- Mặt phẳng mở rộng
- 14
- Mẫu hook
- 4
- Trang đào sâu
- 11
- Thời gian đọc
- ~3h
- Dẫn nguồn từ code
- 100%
Cách dùng bộ tài liệu này
Đọc tuần tự nếu bạn mới bắt đầu — mỗi trang đều dựa trên trang trước đó. Có thể vào thẳng một trang từ kết quả tìm kiếm nếu bạn đã biết mình cần gì: mỗi bài đào sâu đều đứng độc lập, có breadcrumb riêng, mục lục sticky và điều hướng prev/next.
Mọi khẳng định trên mọi trang đều được truy ngược về một file nguồn trong cây docs plugin của nền tảng (docs/plugin/, docs/sending-server-polymorphism/, docs/payment-order-plan-subscription-saas/) hoặc về phần triển khai live trong storage/app/plugins/Aurius/. Nếu một khẳng định không trích dẫn được code, nó bị bỏ — không diễn giải lại.
Thời gian đọc, từ đầu tới cuối: ~3 giờ. Để ship một plugin thật, hãy dành nguyên một buổi chiều — mẫu Hello World mất năm phút để scaffold, còn ví dụ dài nhất (một sending driver với Postal MTA) tốn khoảng một giờ từ plugin:init tới một integration test xanh.
Hiểu mental model trước đã.
Ba trang đưa bạn từ "Composer, ServiceProvider, là cái gì?" tới chỗ viết được dòng Hook::add(...) đầu tiên một cách tự tin. Đọc theo đúng thứ tự này. Phần còn lại của tài liệu mặc định bạn đã nắm load flow và phân loại bốn mẫu hook.
Bắt đầu
Điều kiện tiên quyết, php artisan plugin:init, tám file mà scaffolder sinh ra, hợp đồng composer.json, vòng đời activate, và bảy lỗi thường gặp nhất trong ngày đầu — naming validator, namespace lệch, thiếu autoload.psr-4, cái bẫy register() so với boot().
Kiến trúc plugin
Load flow tại thời điểm boot: AppServiceProvider::boot → Plugin::autoloadWithoutDbQuery → storage/app/plugins/index.json. Thời điểm chạy register() so với boot(). Vì sao plugin inactive vẫn được autoload, bốn lifecycle state, và hai lớp injection.
Hook system — bốn mẫu
REGISTRY (add/collect), EVENT (on/fire), BEHAVIOR (set/perform), FILTER (modify/filter) — cơ chế, callsite thật trong core, semantics khi xung đột, quy ước args-bag, sáu anti-pattern, và một cây quyết định bốn câu hỏi.
Ship một feature thật.
Ví dụ làm việc cho bốn mẫu được hỏi nhiều nhất: một sending driver mới (Postal MTA), một payment gateway theo vùng (Paddle), một surface UI admin tuỳ biến (chatbox Aurius), và các bảng database tách biệt theo plugin.
Sending driver — mẫu plugin đầy đủ
Ship một MTA backend mới mà không cần fork core. Hợp đồng register_sending_server_driver, class driver + 9 capability marker, pipeline validation, blade connection, controller webhook, identity/warmup. Ví dụ làm việc end-to-end dựa trên static review của plugin Postal MTA.
Payment gateway — hợp đồng Cashier
Mô hình pull, không webhook. Registry BillingManager, bốn capability interface, Paddle làm ví dụ làm việc, state machine PaymentIntent, và năm nguyên tắc giữ biên với vendor rút ra từ bài học default currency của TBANK.
UI injection — layout, sidebar, page slot
Ba hook REGISTRY ở tầng layout (layout.head.assets, layout.body.before_close, admin.sidebar.groups) cộng với hợp đồng page.{controller}.{action}.{slot}. Mẫu chatbox-bubble của Aurius dựng end-to-end, biến thể FILTER cho redirect, và sáu anti-pattern.
Database & model — tách biệt theo plugin
Migration nằm trong <plugin>/database/migrations/, tên bảng prefix theo vendor, vòng đời activate-chạy / delete-rollback, foreign key trỏ về bảng core, cờ $keepData, và showcase mười bốn migration của Aurius.
Đưa lên mức production.
Translation theo cách gián tiếp (master file → runtime dump). Lifecycle theo từng state cho register, activate, disable, delete. Test Pest đăng ký qua phpunit.xml của core với chu kỳ activate → test → delete tươi mới mỗi lần CI chạy.
Translation
Master file → runtime dump trong storage/app/data/plugins/... → admin sửa được. Hook REGISTRY add_translation_file, cái bẫy double-load, quy ước mười tám locale, và merge không phá huỷ dữ liệu của translation:upgrade.
Vòng đời plugin
Đi qua từng state: register (5 bước), activate (4 bước), disable (chỉ lật status — autoload + hook vẫn sống), delete (rollback + dọn master file). State diagram, phục hồi khi state hỏng, và bộ console plugin:*.
Testing
Đăng ký testsuite plugin vào phpunit.xml của host, class base PluginTestCase với seed active-row + reset gate-cache theo từng request, các mẫu hook-under-test cho REGISTRY / EVENT / BEHAVIOR / FILTER, công thức tích hợp lifecycle activate-test-delete, và ma trận CI theo từng plugin.
Đọc code production thật.
Học sâu nhất là khi đối diện với một plugin thật, không phải một Hello World. Aurius là plugin phức tạp chính tắc — tám model, mười hai migration, mười tám locale. Dùng showcase này để ánh xạ mọi khái niệm phía trên lên một codebase đang ship thật.
Plugin showcase — Aurius
Plugin phức tạp chính tắc, đi từ đầu tới cuối — tám model, mười bốn migration, mười tám locale, đủ mọi hook surface (sáu đóng góp REGISTRY + hai listener EVENT + một override BEHAVIOR), UI chatbox, hạ tầng test, và công thức học bốn bước.
Tham khảo REST API
Xác thực bằng token, tám resource (Lists, Subscribers, Campaigns, Automations, Sending servers, Customers, Subscriptions, Plans), sáu event webhook lifecycle, ba tracking ping theo từng người nhận, và các shape lỗi — chính API mà plugin xài cũng là API mà client bên ngoài xài.
Knowledge Base
Tham khảo phía vận hành: yêu cầu server, cấu hình supervisor, queue worker, tinh chỉnh deliverability, thiết lập DKIM/SPF/DMARC, playbook migration. Đọc cái này khi câu hỏi là "làm sao để vận hành AcelleMail", chứ không phải "làm sao để mở rộng nó".
Triết lý thiết kế
Core khai báo các extension point. Plugin phản ứng lại.
Core không bao giờ biết plugin của bạn tồn tại.
Đảo ngược phụ thuộc
Code core không bao giờ được plugin import, và plugin không bao giờ được core import. Hook system ngồi giữa hai bên như hợp đồng duy nhất. Nâng cấp core, plugin của bạn vẫn chạy — miễn là tên hook và signature giữ nguyên, mà chúng tôi gắn version-stamp đàng hoàng.
Bốn mẫu, không hơn
REGISTRY, EVENT, BEHAVIOR, FILTER — những động từ duy nhất Hook system biết. Semantics khi xung đột được nêu rõ theo từng mẫu: REGISTRY merge, EVENT bắn cho tất cả, FILTER nối chuỗi, BEHAVIOR độc quyền (hai bên gọi thì throw ngay, không có override im lặng).
Namespace của bạn, mãi mãi
Plugin sống dưới MyVendor\MyPlugin\, tách biệt hoàn toàn khỏi namespace của nền tảng core. Compose, bán, white-label, fork — plugin của bạn là của bạn. Extended License cho phép phân phối lại có tính thương mại.
Nguồn sự thật: chính code
Mọi trang trong cây docs này đều dẫn link tới path file chính tắc mà nó được truy ngược về. Nếu doc nói khác code, code thắng — mở issue và chúng tôi sửa doc, không phải ngược lại. Không copy-paste lỗi thời, không API mang tính ước vọng.
Mở source. Mở rộng nền tảng.
Toàn bộ mã PHP không mã hoá. Cập nhật trọn đời. Hook system. Plugin production thật để học theo. License trả một lần — không subscription, không phí theo subscriber, không giành namespace.