flarum/sticky
Flarum Sticky lets moderators pin important discussions to the top of the discussion list. Add a sticky toggle when composing or editing, keep announcements visible, and manage pinned threads with simple permissions.
Flarum 2.x is a forum application built on top of Laravel's Illuminate components — you get Eloquent, the container, the event dispatcher, migrations, and the queue, but not a Laravel skeleton. Reset your expectations before your first commit.
composer create-project flarum/flarum . for a new forum, or add flarum/core to an existing app. Minimum PHP is 8.1 for the 2.x line; v2.0.2 is a patch release on that line, so nothing changes here.extend.php (root) — the central registry of extenders; this is where 90% of your integration work happens.site.php — frontend/bootstrap wiring.config.php — the real config file (there is no config/ directory to speak of).storage/ — logs and cache, same as Laravel.vendor/flarum/core/src/ — read the source; the docs lag behind the extenders API.Routes extender in extend.php, point it at a controller extending Flarum\Api\Controller\AbstractShowController or AbstractListController, and give it a serializer. That single exercise teaches you routes, DI, policies, and serialization in one pass.php flarum info shows version and extension list; php flarum cache:clear is the "did my change take?" button.Extenders over service providers. The idiomatic Flarum pattern is a stack of small, composable extenders rather than a fat AppServiceProvider. Group them by concern and keep them in separate files under an src/ namespace that matches your composer autoload:
// extend.php
return [
(new Extend\Routes('api'))
->get('/reports', 'reports.index', ReportController::class),
(new Extend\ServiceProvider())
->register(ReportServiceProvider::class),
new Extend\Locales(__DIR__.'/locale'),
];
Reach for Extend\ServiceProvider only when you genuinely need bindings, singletons, or boot-time subscriptions — that's your Laravel AppServiceProvider equivalent, and it is registered into Flarum's container, not Laravel's.
Migrations and models. Drop migration classes in the extension's migrations/ directory; Flarum discovers and runs them through the same migrator you know, alongside core and every other extension. Models extend Flarum\Database\AbstractModel — the query builder is Eloquent, so scopes, relationships, and casts all behave as expected.
Authorization. Use gates and policies the Laravel way, but register them through Flarum's Gate and check against Flarum\User\User. Don't hand-roll permission checks in controllers; the API layer and the frontend both read the same permission registry.
API serialization. Every endpoint needs a serializer (AbstractSerializer subclass). Define getDefaultIncludes() and getRelations() deliberately — this is where you control eager loading, and where a lazy serializer quietly turns a list endpoint into an N+1 disaster. Use Extend\ApiController to add default includes or eager loads at the controller level rather than inside the serializer.
Settings. Inject Flarum\Settings\SettingsRepositoryInterface and use get()/set(). Settings are cached; writes invalidate automatically, but reads in long-running processes (queues, Octane-style workers) can go stale.
Local extension development. Use a Composer path repository pointing at your extension, symlink it, and iterate with php flarum cache:clear between changes. Run the suite with php flarum test (or invoke PHPUnit directly) — Flarum ships an integration test case that boots the full app, which is worth the slower startup for anything touching the container or the API.
Frontend. The admin and forum frontends are Mithril/TypeScript, built with Node tooling that is not part of Composer. Your PHP-side Extend\Frontend registrations and your JS bundle have to be published together; a backend-only deploy will silently keep serving the old JS.
extend.php is compiled and cached. Editing it and refreshing the browser does nothing. php flarum cache:clear after every change to extenders, locales, or routes. Nine out of ten "my route 404s" reports are this.routes/web.php, no app/Http/Kernel.php, no bootstrap/app.php. Middleware, route registration, and console commands all go through extenders. Don't copy Laravel tutorials verbatim; translate them into extenders first.app() helper is not reliably available in every context. Prefer constructor injection or explicit $container->make(...) in early bootstrap code.vendor/. Even a one-line patch to flarum/core will be blown away by the next composer update, and v2.0.2-style patch releases are exactly the kind you want to take without local diffs in the way.flarum/core with a caret constraint and update deliberately.config.php, then tail storage/logs/. Because the app boots through extenders, stack traces frequently point at framework internals — read the first frame inside your namespace, not the last.php flarum assets:publish. If users report stale UI after an otherwise clean release, this is the culprit.php flarum schedule:run / a queue worker. Confirm your worker is restarted after deploy — cached extenders in a long-lived process will serve the old code.Flarum\Extend before reaching for an event listener; if neither exists, a listener on the relevant Flarum\...\Event class is the supported escape hatch.How can I help you explore Laravel packages today?