Cho lập trình viên

Build plugin. Không workaround.

AcelleMail là nền tảng email marketing trên Laravel 11+ ship kèm full PHP source không mã hoá — cộng với Hook system có kiểu rõ ràng để plugin của bạn thêm sending driver, payment gateway, AI, UI tuỳ chỉnh, kể cả REST API. Không cần fork core.

Plugin thực tế đang chạy hôm nay

acelle/ai athena/evs rencontru/postal acelle/console
myvendor/loyalty/ PLUGIN SOURCE composer.json 240 B routes.php 1.2 KB src/ ServiceProvider.php entry Controllers/ Models/ database/migrations/ resources/ views/ lang/ index.json auto $ php artisan plugin:init … HOOK SYSTEM no core touches ACELLEMAIL Laravel 11+ · PHP 8.3+ layout.head.assets layout.body.close admin.sidebar.groups register_sending_* page.{ctrl}.{slot} customer_added sidebar-menu-items dispatch_*_job + many more…

Vì sao developer chọn AcelleMail

Xây trên framework bạn đã biết.
Mở rộng qua pattern bạn đã dùng.

PHP / Laravel open source

Toàn bộ PHP source không mã hoá đi kèm mọi license. Sửa bất kỳ class, override bất kỳ service, fork vào namespace của riêng bạn — code của bạn, luật của bạn. Build trên Laravel 11+ nên framework bạn đã biết sẽ gánh phần nặng.

$ grep -r "protected function" vendor/acelle/

Kiến trúc plugin

Thả một folder hình Composer vào storage/app/plugins/<vendor>/<name>/ và app tự autoload. ServiceProvider inject route, view, migration, hook — activate, deactivate, delete sạch sẽ.

$ php artisan plugin:init myvendor/loyalty

REST API auth bằng token

Endpoint CRUD cho campaign, list, subscriber, template, automation. Webhook event cho mọi sự kiện domain-level. Drop-in cho SaaS frontend, mobile app, hoặc service Laravel khác. API mà plugin dùng cũng chính là API mà client ngoài dùng — không có endpoint hạng hai.

GET /api/v1/campaigns · POST /api/v1/lists

Self-hosted trên hạ tầng của bạn

Server của bạn, DB của bạn, dữ liệu subscriber của bạn. Không vendor lock-in. Trả tiền license một lần; chỉ trả chi phí SMTP nền (ví dụ Amazon SES $0.10/1K). Deploy ở bất cứ nơi nào PHP chạy — bare metal, Kubernetes, Forge, Laravel Vapor. Scale theo cách stack của bạn scale.

· PHP 8.3+ · MySQL / MariaDB · Redis (optional)

Plugin có thể build được gì

14 extension surface.
Mỗi cái đều có ví dụ production thực tế hôm nay.

Plugin là một package Laravel tự chứa nằm trên đĩa, có namespace riêng, table DB riêng, route, view, controller, model — và tích hợp với core thông qua Hook system có kiểu thay vì hack include.

Driver & tích hợp · 4

Sending server / driver REGISTRY
register_sending_server_driver
rencontru/postal — driver Postal MTA chỉ trong một plugin drop-in
Email verification / deliverability REGISTRY
REGISTRY + FILTER
athena/evs — hệ thống con verification kèm admin UI
Payment gateway REGISTRY
REGISTRY
Stripe / PayPal / Braintree / Paystack / Razorpay / Coinbase có sẵn cho thấy pattern
AI / chatbot / assistant REGISTRY
REGISTRY + EVENT
acelle/ai — chatbox + sparkle + observability

UI & trang · 5

Inject UI tuỳ chỉnh REGISTRY
layout.head.assets / layout.body.before_close / admin.sidebar.groups
acelle/ai chatbox + sparkle popover
Slot nội dung theo trang REGISTRY
page.{ctrl}.{action}.{slot}
page.maillist.show.body, page.campaign.index.sidebar
Trang admin ROUTE
Laravel routes
/rui/admin/ai-usage, /rui/admin/ai-audit, /rui/admin/ai-conversations
Trang dành cho customer ROUTE
Laravel routes
/plugins/acelle/ai/dashboard
REST API ROUTE
Routes + api.access_token
/api/v1/ai/* trong acelle/ai

Dữ liệu & vòng đời · 2

Model Eloquent tuỳ chỉnh + table DB BEHAVIOR
Plugin-isolated migrations
acelle/ai ship 8 model / 12 migration, tên table có vendor-prefix
Translation (18 locale) REGISTRY
add_translation_file
acelle/ai ship 162 lang file (18 × 9)

Hành vi & luồng · 3

Webhook listener / domain event EVENT
Hook::on(…)
customer_added, plan_changed, subscription_terminated
Override hành vi BEHAVIOR
Hook::set / setIfEmpty / perform
dispatch_list_import_job — thay toàn bộ logic import
Chuỗi filter FILTER
Hook::modify / filter
sidebar-menu-items, mutate nội dung pre-send, URL redirect

Một plugin duy nhất có thể mix bất kỳ surface nào trong số này — acelle/ai dùng 7/14 trong một package.

Hook system

Bốn pattern extension có kiểu. Không override bất ngờ. Không xung đột im lặng.

Core không bao giờ import code plugin — chỉ khai báo điểm extension. Plugin lắng nghe và phản ứng. Bốn pattern, mỗi cái có vai trò rõ ràng. Xung đột throw ngay lập tức.

1

REGISTRY add() + collect()

Plugin đóng góp item vào một danh sách. Core hỏi "ai có sending driver?" và plugin của bạn nói "tôi có."


Hook::add('register_sending_server_driver', fn () => [
    'type'   => 'myvendor-foo',
    'driver' => \MyVendor\Foo\Driver::class,
]);


$drivers = Hook::collect('register_sending_server_driver');
2

EVENT on() + fire()

Plugin phản ứng với một sự kiện xảy ra. Giá trị trả về bị bỏ.


Hook::on('customer_added', function ($customer) {
    LoyaltyPoints::award($customer, 100, 'welcome');
});


Hook::fire('customer_added', [$customer]);
3

BEHAVIOR set() + perform()

Plugin thay thế hoàn toàn một mảnh logic của core. Chỉ một plugin được claim một behavior cho trước — xung đột throw ngay lập tức.


Hook::set('dispatch_list_import_job',
    fn ($list, $f) => new MyFasterImportJob($list, $f));


Hook::setIfEmpty('dispatch_list_import_job',
    fn ($list, $f) => new DefaultImportJob($list, $f));
$job = Hook::perform('dispatch_list_import_job', [$list, $f]);
dispatch($job);
4

FILTER modify() + filter()

Plugin biến đổi một giá trị qua chuỗi callback. Mỗi callback nhận giá trị hiện tại, trả về giá trị đã sửa.


Hook::modify('sidebar-menu-items', function (array $items) {
    $items[] = [
        'label' => 'Loyalty',
        'url'   => route('lp.dashboard'),
    ];
    return $items;
});


$menu = Hook::filter('sidebar-menu-items', $defaultMenu);

Hello World trong 5 phút

Một plugin chạy được từ con số không trong tám lệnh.

Scaffold tạo mọi thứ — metadata Composer, ServiceProvider, model mẫu, migration mẫu, route mẫu, view mẫu. Từ đó là phần của bạn để phát triển.

  1. 1

    Scaffold plugin

    Tạo layout folder chuẩn + một row DB inactive.

    bash
    $ php artisan plugin:init myvendor/loyalty
      ✓ Created storage/app/plugins/myvendor/loyalty/
      ✓ DB row inserted  (status: inactive)
      ✓ index.json + ServiceProvider autoloaded
  2. 2

    Sửa composer.json

    Khai báo namespace và ServiceProvider cho Composer.

    json
    {
      "name": "myvendor/loyalty",
      "description": "Award points on signup",
      "version": "1.0.0",
      "autoload":  { "psr-4": { "MyVendor\\Loyalty\\": "src/" } },
      "extra":     { "laravel": { "providers": [
        "MyVendor\\Loyalty\\ServiceProvider"
      ] } }
    }
  3. 3

    Viết migration

    Table riêng cho plugin, tên có vendor-prefix. Chạy khi activate, rollback khi delete.

    php
    Schema::create('myvendor_loyalty_accounts', function (Blueprint $t) {
        $t->id();
        $t->foreignId('customer_id')->constrained()->cascadeOnDelete();
        $t->integer('points')->default(0);
        $t->timestamps();
    });
  4. 4

    Định nghĩa model

    Model Eloquent chuẩn trong namespace của riêng bạn.

    php
    namespace MyVendor\Loyalty\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Account extends Model
    {
        protected $table    = 'myvendor_loyalty_accounts';
        protected $fillable = ['customer_id', 'points'];
    }
  5. 5

    Thêm controller và view

    Controller Laravel thuần — namespace view chính là tên plugin của bạn.

    php
    namespace MyVendor\Loyalty\Controllers;
    
    use Acelle\Http\Controllers\Controller;
    
    class DashboardController extends Controller
    {
        public function index()
        {
            return view('loyalty::dashboard', [
                'accounts' => \MyVendor\Loyalty\Models\Account::orderByDesc('points')->take(10)->get(),
            ]);
        }
    }
  6. 6

    Khai báo route

    Cùng Route::group bạn đã quen dùng. Load bởi ServiceProvider::boot().

    php
    Route::group([
        'middleware' => ['web', 'auth'],
        'namespace'  => '\MyVendor\Loyalty\Controllers',
        'prefix'     => 'plugins/myvendor/loyalty',
    ], function () {
        Route::get('/',          'DashboardController@index')->name('lp.dashboard');
        Route::get('/{account}', 'DashboardController@show');
    });
  7. 7

    Ráp tất cả trong ServiceProvider::boot()

    Load view + route; hook vào activate_plugin_… để migrate khi activate.

    php
    public function boot(): void
    {
        $this->loadViewsFrom (__DIR__.'/../resources/views', 'loyalty');
        $this->loadRoutesFrom(__DIR__.'/../routes.php');
    
        Hook::on('activate_plugin_myvendor/loyalty', fn () =>
            \Artisan::call('migrate', [
                '--path'  => 'storage/app/plugins/myvendor/loyalty/database/migrations',
                '--force' => true,
            ])
        );
    
        Hook::on('customer_added', fn ($customer) =>
            Models\Account::create(['customer_id' => $customer->id, 'points' => 100])
        );
    }
  8. 8

    Activate từ admin

    Vào /rui/admin/plugins. Migration tự chạy. Plugin sống tại prefix của nó.

    bash
    ✓ activated  myvendor/loyalty  v1.0.0
      → migrations: 1 ran (myvendor_loyalty_accounts)
      → routes:     2 registered under /plugins/myvendor/loyalty
      → hooks:      2 listening (activate, customer_added)
      → status:     active
    
    → visit /plugins/myvendor/loyalty/  ← live

Giờ bạn đã có một plugin chạy được. Thêm bao nhiêu model, controller, hook tuỳ feature yêu cầu. Test sống trong tests/; chạy chúng với php artisan test --testsuite='Plugin: myvendor/loyalty'. Tham khảo contract chính thức trong knowledge base.

Vòng đời plugin

Bốn trạng thái. Chuyển tiếp dự đoán được. Không có chuyện "nó có chạy không?".

Mỗi plugin sống ở đúng một trong bốn trạng thái. Trạng thái bạn đang ở quyết định hook nào fire, migration nào đã chạy, và điều gì xảy ra ở chuyển tiếp kế tiếp. Tham chiếu nguồn tại docs/plugin/SOURCE_OF_TRUTH.md.

STATE 01 register (inactive) Folder plugin được khám phá. index.json + row DB tồn tại. Hook CHƯA fire. STATE 02 active Mọi hook đều live. Route đã mount. Migration đã chạy. activate_plugin_… fire một lần. STATE 03 inactive (paused) Folder vẫn ở trên đĩa. Hook tạm dừng. Route unmount. Table DB còn nguyên. STATE 04 deleted (gone) Folder đã xoá. Migration rollback nếu $keepData = false. Row DB bị xoá. activate migration chạy deactivate data giữ lại remove $keepData? re-activate hook activate KHÔNG chạy lại
Register · inactive

Đã khám phá, chưa chạy

Folder plugin tồn tại, index.json đã index, row DB ghi inactive. register() của ServiceProvider đã chạy, nhưng các hook trong boot() bị gate cho tới khi bạn nhấn Activate.

Active

Live, hook đang fire

Lần activation đầu tiên fire activate_plugin_<name> một lần — đây là chỗ migration chạy. Sau đó mọi hook REGISTRY / EVENT / FILTER / BEHAVIOR đều live cho tới khi deactivate.

Inactive · paused

Tạm dừng, data còn nguyên

Route unmount, hook gate off, nhưng folder + table DB còn. Re-activate không chạy lại migration hoặc hook activate — an toàn để toggle trong production.

Deleted

Đã gỡ sạch sẽ

Folder đã xoá, row DB không còn. Nếu $keepData = false, migration của plugin rollback — table có vendor-prefix bị drop. Table core không bao giờ bị động đến.

Plugin showcase thực tế

acelle/ai — toàn bộ AI subsystem trong một folder plugin.

Plugin acelle/ai ship một layer AI assistant hoàn chỉnh: chatbox agent trên mọi trang, ✨ sparkle viết lại text trên rich-text field, persona coach grounded theo KB (Deliverability, Segments, Campaigns, Forms), và một admin observability surface trải 5 route. Mount qua 3 layout hook. Zero thay đổi core.

Plugin acelle/ai đang chạy — bubble chatbox AI AcelleMail nổi với sparkle popover ✨ để rewrite một click, cộng với group sidebar admin của plugin render trong UI AcelleMail

Nó ship gì

  • Chatbox nổi — chế độ support thuần + chế độ agent opt-in gọi được tool
  • Sparkle popover — rewrite / expand / simplify một click trên mọi rich-text field
  • Coach persona — LLM helper nhận biết domain, scope theo từng màn hình
  • Admin observability — usage, audit, conversation, feedback, self-improve
  • Engine linh hoạt — BYO OpenAI, Anthropic, hoặc Ollama local
  • Trang landing plugin tại /plugins/acelle/ai/dashboard

Bằng con số

8
Model Eloquent
12
Migration
60+
Template Blade
100+
Test Pest
18 × 9
Locale × file lang
~35
File CSS + JS

Mount thế nào — 3 hook

acelle/ai plugin → CSS / JS assets Hook::add( 'layout.head.assets', fn()=>...); → Chatbox bubble Hook::add( 'layout.body.before_close' ,fn()=>...); → AI sidebar group Hook::add( 'admin.sidebar.groups', fn()=>...); Self-contained. AcelleMail core <head> + acelle-ai-chatbox.css </body> (just before) + <div class="ai-chatbox"> Admin sidebar AI · Settings AI · Usage AI · Conversations

3 hook REGISTRY layout — vậy thôi. Cộng inject theo từng trang, listener event vòng đời, và một REST API riêng namespace tại /api/v1/ai/*. Zero thay đổi core; folder plugin là drop-in deploy được.

REST API

Endpoint auth bằng token. Cùng API mà plugin dùng.

Dùng REST API để tích hợp AcelleMail vào SaaS frontend, mobile app, hoặc service Laravel của bạn. API mà plugin dùng cũng chính là API mà client ngoài dùng — không có endpoint hạng hai.

Resource

  • ListGET/POST/PATCH/DELETE /api/v1/lists[/:uid]
  • Subscriber/api/v1/subscribers · tag · sub/unsub
  • Campaign/api/v1/campaigns[/:uid] · run/pause/resume · CSV log
  • Automation/api/v1/automations[/:uid] · execute · api/call
  • Sending server/api/v1/sending_servers[/:uid]
  • Customer (admin)/api/v1/customers[/:uid] · assign/change-plan · enable/disable
  • Subscription (admin)/api/v1/subscriptions[/:uid]

Sự kiện webhook

Vòng đời (cấu hình ở Admin → Webhooks): new_customer, new_subscription, change_plan, cancel_subscription, terminate_subscription, automation_webhook.
Tracking theo recipient (theo campaign): open, click, unsubscribe.

Tham chiếu API đầy đủ →
# Create a list
$ curl -X POST \
    https://your-acelle.example.com/api/v1/lists \
    -H "Authorization: Bearer $ACELLE_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Newsletter",
      "from_email": "hi@example.com",
      "from_name": "Hi Team",
      "subject": "Weekly digest"
    }'

# Add a subscriber
$ curl -X POST \
    .../api/v1/lists/:uid/subscribers \
    -H "Authorization: Bearer $ACELLE_TOKEN" \
    -d '{"EMAIL":"alice@example.com"}'

# Send a campaign
$ curl -X POST \
    .../api/v1/campaigns/:uid/send \
    -H "Authorization: Bearer $ACELLE_TOKEN"

# Receive a webhook
POST /your-webhook-endpoint
{
  "event": "campaign.sent",
  "campaign_uid": "abc123",
  "list_uid": "xyz789",
  "sent_at": "2026-05-06T12:34:56Z"
}

Tinh thần open-source

Code của bạn, mãi mãi. Không hộp đen, không phí định kỳ, không squat namespace.

Source không mã hoá

Từng dòng PHP chạy trên server của bạn. Không ionCube, không SourceGuardian, không obfuscation. git diff bản tuỳ chỉnh của bạn. grep để tìm bất cứ thứ gì.

Update trọn đời

Mọi release của AcelleMail từ 2016 đều có trong lịch sử download CodeCanyon của bạn. Pull cái bạn muốn khi bạn muốn. Không áp lực subscription.

Namespace của bạn, mãi mãi

Plugin sống dưới MyVendor\MyPlugin\ — cô lập hoàn toàn khỏi core Acelle\*. Có thể phân phối qua Composer tới team hoặc marketplace của bạn.

License một lần

Trả $80 (Regular) hoặc $199 (Extended cho SaaS). Không subscription, không tính per-subscriber, không tính per-email. Phép toán không đổi khi bạn lớn.

Tài nguyên & cộng đồng

Docs, plugin mẫu, và một đường dây trực tiếp tới support.

Tài liệu developer đầy đủ

Mười một deep-dive grounded theo source. Nhảy thẳng vào trang bạn cần:

Mở hub →
Chất lượng & Tham chiếu
Translation Vòng đời plugin Testing Showcase plugin

Hướng dẫn phát triển plugin

Năm file phía host, luồng boot-and-load, bốn trạng thái vòng đời, hai layer injection — mô hình tinh thần mà phần docs còn lại giả định.

/developers/plugin-architecture →

Tham chiếu REST API

Authentication, danh mục endpoint, payload webhook, shape lỗi — trang landing native kèm ví dụ.

/api →

Hook system — bốn pattern

REGISTRY / EVENT / BEHAVIOR / FILTER — bốn dạng extension plugin dùng, kèm call-site thực tế grep từ core, ngữ nghĩa xung đột và sáu anti-pattern.

/developers/hook-system →

Sending driver — pattern plugin đầy đủ

Ship một MTA backend mới hoàn toàn mà không fork core. Contract register_sending_server_driver, chín capability marker, và năm pitfall của plugin Postal.

/developers/sending-drivers →

Showcase plugin — acelle/ai

Plugin phức tạp chuẩn mực được dắt đi từ đầu đến cuối. Tám model, mười bốn migration, mười tám locale, mọi hook surface, UI chatbox, cộng với công thức học bốn bước.

/developers/showcase →

Hỗ trợ developer

Bí ở contract hook hoặc câu hỏi vòng đời plugin? Email cho chúng tôi — phản hồi điển hình trong 24h.

support@acellemail.com →
Index tài liệu · CAT

Duyệt toàn bộ 11 deep-dive →

Nền tảng, Xây dựng, Chất lượng, Tham chiếu — mọi trang ở một chỗ, grounded theo storage/app/plugins/acelle/ai/ và docs plugin chính thức.

/developers →

FAQ cho developer

Những câu developer thực sự hỏi trước khi mua.

Tôi có thể sửa core source code mà không mất update không? +

Có, nhưng pattern sạch nhất là plugin. Plugin sống ngoài namespace core và tự survive mọi upgrade. Sửa trực tiếp core thì được nhưng phải merge thủ công thay đổi upstream mỗi release. Hệ thống plugin tồn tại đúng là để tránh cái khổ đó.

Plugin có survive được khi upgrade AcelleMail không? +

Có, theo thiết kế. Core khai báo hook point; plugin phản ứng. Miễn là tên hook và signature ổn định (và chúng tôi version-stamp những thay đổi breaking), plugin của bạn vẫn chạy. Chúng tôi test plugin AI (acelle/ai) trên mỗi release.

Tôi có thể bán plugin thương mại không? +

Có. Extended License cho phép bạn phân phối plugin của riêng mình dưới license của riêng bạn. Plugin sống trong namespace riêng; bạn sở hữu code đó 100%. Vài team chạy marketplace riêng tư cho hệ sinh thái plugin nội bộ của họ.

Hệ thống plugin xử lý xung đột thế nào? +

Hook REGISTRY merge (mọi plugin đều đóng góp); hook EVENT đều fire (không thể xung đột); chuỗi FILTER tích luỹ; BEHAVIOR là pattern "exclusive" duy nhất — nếu hai plugin cùng cố claim một behavior, exception throw ngay. Không có chuyện override im lặng bất ngờ.

Các plugin có nói chuyện với nhau được không? +

Có, qua cùng Hook system. Plugin A có thể fire event cho Plugin B lắng nghe. Hoặc expose một class public — MyVendor\PluginA\Service là class PHP thông thường, import như bất kỳ class nào khác. Plugin acelle/ai expose hook để plugin khác đăng ký AI tool tuỳ chỉnh.

Khác biệt giữa Regular và Extended đối với developer là gì? +

Regular ($80) bao quát use riêng của bạn trên một domain. Extended ($199) thêm quyền tính phí end-user (resell dưới dạng SaaS), phân phối bản clone white-label, và bán plugin của riêng bạn. Cả hai ship cùng source code.

Migration plugin có chạm vào DB chính không? +

Chúng ghi vào cùng database, nhưng ở table có prefix tên vendor của bạn (ví dụ myvendor_loyalty_accounts). Migration chạy khi activate plugin, rollback khi delete plugin với $keepData = false. Zero rủi ro cho table core.

Tôi có thể viết plugin bằng ngôn ngữ khác PHP không? +

Runtime plugin là PHP/Laravel. Nhưng plugin của bạn có thể shell out tới bất cứ thứ gì — service Node, Python ML, binary Go, HTTP API ngoài. Vài team ship plugin wrap tool ngoài. Plugin là shell tích hợp; phần nặng có thể sống ở bất cứ đâu.

Plugin tương tác với AI subsystem thế nào? +

acelle/ai expose hook để plugin khác có thể dùng để đăng ký AI tool tuỳ chỉnh, swap engine, hoặc hook vào layer observability. Đọc source plugin AI để biết contract. Plugin của bạn cũng có thể fire event mà AI agent nhặt làm context.

Có marketplace plugin không? +

Chưa. Hôm nay, plugin phân phối qua bán trực tiếp, GitHub, hoặc registry riêng tư của bạn. Marketplace cộng đồng nằm trong roadmap khi đủ plugin bên thứ ba để làm cho nó đáng.

Ship plugin đầu tiên của bạn ngay chiều nay.

Toàn bộ PHP source code. Update trọn đời. Hook system. Ví dụ production thực tế để copy. Một lần $199 cho Extended License (redistribute plugin thương mại + dùng SaaS).

Mua Extended License — $199 Thử Live Demo