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 Manager Api Laravel Package

liberusoftware/module-manager-api

Laravel package providing a RESTful API for managing application modules. List, inspect, enable, and disable modules programmatically, with integration into the Liberu module manager for dynamic module administration.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a presentation adapter, not the module manager itself. Expect it to contain HTTP routes, controllers, request validation, API resources and the bindings that translate API calls into the underlying module-manager domain services from the Liberu ecosystem. That framing matters: you will spend most of your time here exposing behaviour that already exists upstream, not implementing it.

Minimal setup:

composer require liberusoftware/module-manager-api
php artisan vendor:publish --provider="LiberuSoftware\ModuleManagerApi\ModuleManagerApiServiceProvider"
php artisan migrate   # only if the core module manager ships migrations
php artisan route:list --path=api

Steps, in order:

  1. Read the service provider first. src/Providers/*ServiceProvider.php tells you everything: route file location, config keys, middleware group, and whether routes are auto-loaded or gated behind a config flag.
  2. Read routes/. This is the real API contract. Map each route to a controller method before touching anything else.
  3. Read the controllers. They should be thin — request → DTO → domain call → JsonResource. If they are not, note that as a red flag.
  4. Confirm the domain dependency resolves. php artisan tinker and resolve the module manager service/interface from the container. If it is unbound, you will get a container exception on the first request, not at boot.
  5. Run a smoke test against the list endpoint before writing code.

First use case: hitting GET /api/modules (or whatever the index route is named) to list installed modules, then GET /api/modules/{module} for a single module's metadata and status. From there, the enable/disable or install endpoints are the ones you will actually build UI against.

Because stars and dependents are effectively zero, treat documentation as non-existent. Your source of truth is routes/ + controller signatures + the composer require block, which tells you exactly which core package version this adapter was written against.

Implementation Patterns

Thin adapter, thin controller. The correct pattern for this package is: validate in a FormRequest, hand a plain array or DTO to the domain service, return an ApiResource. Resist the urge to add logic here — anything you add in this layer becomes a merge conflict when the package bumps.

Interface binding / swap for tests. The adapter likely binds a domain port to a concrete implementation. In TestCase or a dedicated service provider, rebind that interface to a fake so your feature tests do not need real module state:

$this->app->bind(ModuleRegistry::class, fn () => new InMemoryModuleRegistry([...]));

That is far cheaper than seeding a real module directory per test.

Extend by composition, not by editing vendor. Add your own routes and controllers in routes/api.php of your app that call the same domain services, then reuse the package's JsonResource classes for consistent output. If you need extra fields, subclass the resource and override toArray():

class ModuleResource extends \LiberuSoftware\ModuleManagerApi\Resources\ModuleResource
{
    public function toArray($request): array
    {
        return parent::toArray($request) + ['dependents' => $this->dependents()];
    }
}

Version the surface early. If the package does not already prefix with /api/v1, add your own prefix in RouteServiceProvider or the config. Given the release cadence implied by the metadata, the JSON shape will move; a version prefix is the cheapest insurance you have.

Auth and authorization. These routes almost certainly ship unauthenticated. Wrap them in auth:sanctum (or your guard) via the package config or by re-registering the route group. Module enable/disable is a privileged operation — gate it with a policy or Gate::define('manage-modules') rather than relying on route middleware alone.

Idempotency for state-changing calls. Enable/disable/install should be safe to retry. If the package's endpoints are not idempotent, wrap them: dispatch a queued job with a deterministic key, or check current status before calling. This matters when a UI retries on timeout.

Tenant-scoped module availability. In a modular monolith like Liberu, module enablement is often per-tenant. Add middleware that resolves the tenant and passes it to the domain call, and key any cache on tenant ID:

Cache::remember("modules:{$tenant->id}", 300, fn () => $registry->allFor($tenant));

Contract tests over unit tests. Since you do not control the internals, test the HTTP shape: status codes, data structure, validation errors. Use assertJsonStructure rather than full assertExactJson so upstream additions do not break your suite.

Consuming from a frontend. Because the adapter emits resources, you can point an SPA or a Livewire component at it directly. Cache the module list client-side and invalidate on any 2xx from a mutating endpoint.

Gotchas and Tips

  • License is NOASSERTION. Composer will not stop you, but this is not a clear grant. Confirm with the maintainer before shipping in anything commercial; otherwise treat it as read-only reference code.
  • Zero dependents, zero stars, future-dated releases. There is no community safety net. Pin the exact version in composer.json ("liberusoftware/module-manager-api": "1.2.*" or a commit hash) and expect breaking changes on minor bumps.
  • The adapter is not the domain. If a bug is in module resolution or dependency ordering, it lives in the core package. Forking this repo to fix that is the wrong layer — and you will pay for it at the next update.
  • Service provider not discovered. If routes 404, check bootstrap/providers.php (Laravel 11+) or config/app.php, and check whether the package gates route registration behind a config flag that defaults to false.
  • Route collisions. If the core module manager also registers routes under /api/modules, route:list will show duplicates and the last-registered wins. Reorder providers or rename your prefix.
  • Config cache staleness. php artisan config:cache after publishing, then php artisan optimize:clear whenever you change module state, otherwise the registry looks frozen.
  • JSON envelope surprises. Check whether responses are wrapped in data (default JsonResource) or flattened. Frontends written against one shape silently break against the other.
  • Container binding order. Resolving the module registry in a service provider's boot() (rather than lazily) will fail if the core package registers later. Defer everything.
  • Exception mapping. Domain exceptions (module not found, dependency conflict) need explicit mapping to 404/409 in bootstrap/app.php (withExceptions) or the package's handler. Otherwise everything is a 500 and your API is unusable.
  • Debugging tip. Drop Log::info(__METHOD__, func_get_args()) at the top of the controller method, hit the endpoint, and read storage/logs/laravel.log. With no docs, this is faster than reading source.
  • Extension points worth checking before forking: the published config file, any Resource/Collection classes (usually swappable via config), route middleware arrays, and events dispatched around module state changes — listening to those is the cleanest way to hook in without touching vendor code.
  • Testing gotcha. If the package binds to a real filesystem module path, tests will pass locally and fail in CI. Override the path in phpunit.xml via an env var, or rebind the registry fake as described above.
  • Performance. Module scanning often touches the filesystem on every request. If a cold GET /api/modules takes >50ms, add a cache layer in your wrapper, not in vendor, and invalidate it from a listener on the module-state-changed event.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core