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

Module Laravel Package

akaunting/module

A Laravel package that adds a modular architecture to Akaunting, letting developers package features as self-contained modules with service providers, routes, views, migrations, and settings. It simplifies registering, installing, enabling, and managing modules within an Akaunting application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package alongside its Laravel dependencies:

composer require akaunting/module

Publish the config and run the shipped migration so the modules table exists (this table stores the enabled/disabled state and installed version of each module):

php artisan vendor:publish --provider="Akaunting\Module\Providers\Laravel"
php artisan migrate

The first place to look is config/module.php. It defines where modules live (default modules/ at the project root), the path pattern, cache behaviour, and the auto-discovery options. Then scaffold your first module:

php artisan module:make Blog

This creates modules/Blog/ with a module.json manifest, a Providers/ directory, and stubs for routes/config/views. Enable it:

php artisan module:enable Blog
php artisan module:list

Your first real use case is almost always: "add a self-contained feature (billing, exports, an admin panel) that I can toggle on and off per environment without touching composer.json and without redeploying a dependency." The package gives you that toggle via Modules::isEnabled('blog') and the corresponding DB row.

Implementation Patterns

The module.json manifest is the source of truth for bootstrapping. A minimal manifest:

{
  "name": "Blog",
  "alias": "blog",
  "description": "Blog module",
  "version": "1.0.0",
  "keywords": [],
  "priority": 0,
  "providers": [
    "Modules\\Blog\\Providers\\BlogServiceProvider"
  ],
  "aliases": {},
  "files": [],
  "requires": []
}

providers are registered at boot (only when enabled), aliases are registered into the class alias loader, and files are required — don't put side-effecting code in files unless you really need global helpers.

Gate code on module state rather than on class existence. Use the Modules facade (or the injected Akaunting\Module\Contracts\Repository):

use Akaunting\Module\Facades\Module as Modules;

if (Modules::isEnabled('blog')) {
    // register routes, menus, etc.
}

$blog = Modules::get('blog');    // Module instance or null
$all  = Modules::all();          // all installed modules
$on   = Modules::enabled();

This is how you implement "feature flag by package" without wiring in a separate flag system.

Toggle from an admin UI. The enable/disable operations are idempotent; wrap them in a controller and call $module->enable() / $module->disable(), or shell out to the artisan commands. Remember to clear the module cache after (see Gotchas).

Depend on other modules. The requires array in module.json lists aliases; install/enable will fail loudly if a dependency is missing or disabled. Use it to model optional features that compose — e.g. Reporting requires Blog.

Version and upgrade flow. The modules table has version; compare it to the manifest's version to decide whether to run a module's migrations or upgrade script. This is the intended way to ship schema changes inside a toggleable module.

Keep providers thin. The package only knows how to load your provider; registration order, route prefixes, and middleware are your job. A typical module provider does:

public function register()
{
    $this->mergeConfigFrom(__DIR__.'/../Config/blog.php', 'blog');
}

public function boot()
{
    $this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
    $this->loadViewsFrom(__DIR__.'/../Resources/views', 'blog');
    $this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
    $this->publishes([
        __DIR__.'/../Config/blog.php' => config_path('blog.php'),
    ], 'config');
}

Gotchas and Tips

  • State lives in two places. The database row says "enabled"; the filesystem says "installed"; and config/module.php says "where to look." All three can drift. If module:list disagrees with reality it's usually a stale row — the module:disable/module:enable commands are safe to re-run to reconcile.
  • Cache bites in production. Module status is cached; after enabling/disabling (or after a deploy that adds a module) run php artisan cache:clear (or the package's clear command) or you'll chase a ghost where a disabled module's routes still respond. In CI, clear cache as part of every deploy.
  • Autoloading is not Composer's job. Modules are PSR-4 loaded from the manifest, not from your root composer.json. If a class isn't found after module:make, check the namespace prefix in the manifest matches the directory and that you actually composer dump-autoloadd (the package still relies on the base autoloader for shared deps).
  • Provider boot order matters. Module providers are registered after the application's providers. If your module needs to decorate a binding, do it inside register() via $this->app->extend(...) rather than assuming your binding wins.
  • Manifest typos fail at boot. A malformed module.json (trailing comma, wrong providers path) throws during the service provider bootstrap, and the error often points at the framework rather than the module. When a fresh clone explodes before any route matches, json_decode the manifest manually first.
  • Aliases collide globally. Anything in aliases becomes a global class alias — two modules declaring the same alias will silently shadow each other depending on priority. Use unique, namespaced aliases (BlogFacade not Blog).
  • Don't abuse files. The files array is required on every request; keep it to helper functions, never registrations or heavy bootstrapping. Prefer providers.
  • requires is checked at install/enable, not continuously. Disabling a module that another enabled module requires may leave the dependent in a broken state — enforce that in your admin UI.
  • Permissions on modules/. module:install, module:make, and publish commands write to disk. On immutable or read-only production filesystems, treat modules as build artifacts: install locally, commit modules/, and only enable in production.
  • It's an Akaunting-first package. Naming conventions, the alias-as-lowercase pattern, and the config layout are tuned for Akaunting. Using it standalone works, but expect to write your own admin controller, permission checks, and upgrade hooks — it's a management skeleton, not a full module ecosystem.
  • Debugging tip: Modules::get('blog') returning null while module:list shows it is nearly always a cache issue or a path/namespace mismatch in config/module.php. Check both before touching the manifest.
  • Testing: clear the module cache in setUp ($this->artisan('cache:clear') or the package command) and use Modules::isEnabled() guards in test helpers so tests can run with the module off without fatal class-not-found errors.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons