Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Core Laravel Package

flarum/core

Flarum is free, open-source forum software built on PHP with a fast, modern frontend. It's lightweight, extensible, and designed for smooth community discussions, offering a clean default theme and a rich extension ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

Flarum is not a Laravel application — it is a PHP forum platform built on selected Laravel components (illuminate/database, illuminate/validation, illuminate/support, service container, events). Treat it as a platform you extend, not an app whose framework you control.

Minimal setup:

composer create-project flarum/flarum forum
cd forum
php flarum install

For extension work, create a package with "type": "flarum-extension" and an extend.php file. The first places to read:

  • extend.php — registration point for all extenders.
  • vendor/flarum/core/src/Extend/ — the supported extension surface.
  • vendor/flarum/core/src/Api/ — JSON:API controllers, serializers, middleware.
  • vendor/flarum/core/src/User/ — users, groups, policies, permissions.
  • config.php and storage/logs/ — configuration and debugging.

Your first useful use case: expose a small custom API endpoint and surface it in the forum UI. Start with a route and a controller, then add a frontend extender once the backend works.

Useful CLI commands:

php flarum list
php flarum migrate
php flarum cache:clear
php flarum assets:publish
php flarum extension:enable acme/stats

php flarum cache:clear is your closest equivalent to php artisan optimize:clear; run it after almost every backend change.

Implementation Patterns

Extension anatomy. A typical extension has:

// extend.php
use Flarum\Extend;

return [
    (new Extend\Routes('api'))
        ->get('/acme/stats', 'acme.stats', Acme\Stats\Controller\StatsController::class),

    (new Extend\Frontend('forum'))
        ->js(__DIR__.'/js/dist/forum.js')
        ->css(__DIR__.'/less/forum.less'),

    new Extend\Locales(__DIR__.'/locale'),
];

Register service providers with (new Extend\ServiceProvider())->register(...) / ->boot(...) when you need container bindings.

API endpoints. Prefer Flarum’s JSON:API controllers and serializers over returning arbitrary arrays. A list endpoint typically extends Flarum\Api\Controller\AbstractListController, sets public $serializer, and implements data(). For custom, non-model payloads you can implement Psr\Http\Server\RequestHandlerInterface directly, but clients will lose JSON:API conventions such as data, relationships, and included.

Extending existing API output. This is often more valuable than new endpoints:

use Flarum\Api\Serializer\ForumSerializer;

(new Extend\ApiSerializer(ForumSerializer::class))
    ->attributes(function ($serializer, $model, array $attributes) {
        $attributes['acmeStats'] = [
            'online' => 42,
        ];

        return $attributes;
    });

Use (new Extend\ApiController(SomeController::class))->prepareDataForSerialization(...) to eager-load relations and avoid N+1 queries in serializers.

Models and migrations. Use Flarum\Database\AbstractModel and normal Eloquent relationships. Flarum migrations are array-style, not Laravel migration classes:

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;

return [
    'up' => function (Builder $schema) {
        $schema->create('acme_stats', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->timestamps();
        });
    },
    'down' => function (Builder $schema) {
        $schema->dropIfExists('acme_stats');
    },
];

Use the schema builder so the configured table prefix (flarum_ by default) is respected.

Events. Flarum dispatches domain events such as Flarum\Post\Event\Posted and Flarum\User\Event\Saving. Register listeners with:

(new Extend\Event())
    ->listen(\Flarum\Post\Event\Posted::class, Acme\Listeners\NotifyOnPost::class);

Settings. Inject Flarum\Settings\SettingsRepositoryInterface or resolve it from the container:

$settings = app(\Flarum\Settings\SettingsRepositoryInterface::class);
$settings->set('acme.stats.enabled', true);
$enabled = (bool) $settings->get('acme.stats.enabled');

Expose settings in the admin UI from the extension’s admin JS:

app.extensionData
  .for('acme-stats')
  .registerSetting({
    setting: 'acme.stats.enabled',
    type: 'boolean',
    label: 'Enable stats',
  });

Permissions. Define policies and register them:

(new Extend\Policy())
    ->modelPolicy(Acme\Stats::class, Acme\Policy\StatsPolicy::class);

Always enforce permissions server-side with $actor->assertCan(...) or policy checks inside controllers. Never rely on the frontend hiding a button.

Frontend extension. Flarum uses Mithril.js, not Blade/Vue/React. Extend existing components with extend():

import { extend } from 'flarum/common/extend';
import IndexPage from 'flarum/forum/components/IndexPage';

extend(IndexPage.prototype, 'hero', function (items) {
  items.add('acme-stats', <div className="AcmeStats">...</div>);
});

Register custom frontend routes in JS via app.routes['acme.stats'] = { path: '/stats', component: StatsPage };.

Calling Flarum from a Laravel app. Do not merge codebases or share Eloquent models directly. Run Flarum as a separate service and integrate over its JSON:API:

curl -X POST https://forum.example.com/api/token \
  -H 'Content-Type: application/json' \
  -d '{"identification":"admin","password":"secret"}'

Then send Authorization: Token <token> and Accept: application/vnd.api+json on subsequent requests. Use pagination via page[number] and page[limit], and relationships via include.

Testing. Use flarum/testing integration tests. They boot a real Flarum app, run migrations, and let you act as a user:

use Flarum\Testing\integration\TestCase;

class StatsTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        $this->extension('acme/stats');
    }

    public function test_endpoint_requires_auth(): void
    {
        $response = $this->send(
            $this->request('GET', '/api/acme/stats')
        );

        $this->assertEquals(401, $response->getStatusCode());
    }
}

Gotchas and Tips

  • It is not Laravel. There is no php artisan, no Blade, no routes/web.php, and no full HTTP kernel. You get Laravel components, not the framework. Do not expect Laravel helpers, facades, or queue workers to behave like a standard Laravel app.
  • config.php, not .env. Database, URL, and debug settings live in config.php. Set 'debug' => true locally and check storage/logs/flarum-*.log.
  • Extensions must be enabled. composer require acme/stats only downloads code. Enable it through the admin panel or php flarum extension:enable acme/stats. If classes are missing after adding a package, run composer dump-autoload and php flarum cache:clear.
  • Clear cache constantly. After editing extend.php, listeners, routes, or service providers, run php flarum cache:clear. Many “my extender isn’t running” bugs are cached container/extender state.
  • Frontend changes need a build. The js/dist/*.js files are what Flarum serves. Run npm run build in the extension and php flarum assets:publish after installing or updating extensions. If UI changes do not appear, verify the built assets and clear the cache.
  • Migrations are array-style. Do not paste Laravel class ... extends Migration files into a Flarum extension. Run php flarum migrate after enabling the extension. Never edit core migrations.
  • JSON:API is strict. Serializer IDs must be strings, relationships must be declared, and clients expect data/included. If you return raw arrays from a JSON:API endpoint, standard clients will break.
  • Watch for N+1 queries. Serializers run per model. Eager-load relationships in ApiController extenders or controller data() methods, especially for list endpoints.
  • Permissions are server-side. Use policies and assertCan. Guest actors are real actors; test unauthenticated and unauthorized cases explicitly.
  • Do not modify vendor/flarum/core. Use extenders. If you need a core change, fork and pin the package, because the next composer update will overwrite edits.
  • Composer constraints matter. Extensions declare flarum/core requirements. Use composer why-not flarum/core when an extension will not install, and pin core versions in production.
  • Upgrade sequence. Back up the database, update dependencies, then run php flarum migrate, php flarum cache:clear, and php flarum assets:publish. Skipping the asset publish step is a common cause of broken admin/forum UI after upgrades.
  • Frontend extender ordering can matter. When multiple extensions modify the same component method, behavior depends on registration and priority. Keep extenders small and defensive; do not assume your item is first or last.
  • Translations are namespaced. Register locales with new Extend\Locales(__DIR__.'/locale') and prefix keys with your extension ID to avoid collisions.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager