App Providers
Who this is for
You need a central place to wire DeskFlow — register JWT auth from a package, add billing middleware, or bind services in AppProvider without scattering boot logic across actions.
What you will learn
- How
Providerhooks map to middleware, auth, routes, and commands - Boot order relative to
[app_switches]andWebApplication::bootOnce() - Registration via
[app_providers]insettings.inivsaddAppProvider()in bootstrap
Before you start
- Extending Pionia overview — plugins vs providers
- DeskFlow or any app with
bootstrap/application.phpandenvironment/settings.ini - Optional: Authentication —
authentications()hook
How it works
Pionia separates plugins (Composer libraries that do not hook into boot) from providers (packages or app classes that register middleware, authentication, routes, commands, logging, cache, or exception behavior).
Providers implement Pionia\Contracts\ProviderContract by extending Pionia\Base\Provider\Provider. The legacy name Pionia\Base\Provider\AppProvider is a deprecated alias of Provider.
Creating a provider
DeskFlow might register Northwind-specific wiring in Application\Providers\AppProvider:
use Pionia\Auth\AuthenticationChain;
use Pionia\Base\Provider\Provider;
use Pionia\Http\Routing\PioniaRouter;
use Pionia\Middlewares\MiddlewareChain;
class AppProvider extends Provider
{
public function middlewares(MiddlewareChain $chain): MiddlewareChain
{
return $chain->add(RequestIdMiddleware::class);
}
public function authentications(AuthenticationChain $chain): AuthenticationChain
{
return $chain->addAuthentication(JwtAuthBackend::class);
}
public function commands(): array
{
return [
'deskflow:sync' => SyncProjectsCommand::class,
];
}
public function configureExceptions(\Pionia\Exceptions\ExceptionPipeline $exceptions): void
{
$exceptions->dontReport(ValidationException::class);
}
public function onBooted(): void
{
app()->set('northwind.client', fn () => new NorthwindClient());
}
}Scaffold an app provider:
php pionia make:provider AppProviderPackage authors use the same pattern — see BillingProvider example in Composer packages.
Provider hooks
| Method | Purpose |
|---|---|
middlewares() | Add middleware to the global chain (add, addBefore, addAfter) |
authentications() | Register auth backends (addAuthentication) |
commands() | Return alias => Command::class map |
routes() | Register API switches on the shared router |
configureLogging() | Extend LogManager channels |
configureCaching() | Register cache stores via CacheManager::extend() |
configureExceptions() | Customize ExceptionPipeline |
configureValidations() | Register custom validation rules |
onBooted() | Container bindings after all stacks are built |
onTerminate() | Cleanup on CLI shutdown |
Boot order
- Resolve registered providers
- Merge middleware, authentication, and commands from new providers
- Run
configureLogging,configureCaching,configureExceptions, andonBootedon all providers - Register provider API routes (switches)
App switches from [app_switches] in settings.ini register during AppRealm::boot(). Provider routes register during WebApplication::bootOnce().
Registering a provider
Recommended — bootstrap:
// bootstrap/application.php
return AppRealm::create(__DIR__)
->web()
->addAppProvider(\Application\Providers\AppProvider::class);INI — environment/settings.ini:
[app_providers]
app=Application\Providers\AppProvider
billing=Vendor\Billing\BillingProviderBoth methods can be combined. addAppProvider() is chainable.
Performance and cache
Pionia tracks fully booted providers in the bootstrapped_providers cache entry. When you add a provider, only that provider’s hooks run on the next request. When you remove a provider, run:
php pionia cache:clearOptional TTL in bootstrap:
pionia()->appItemsCacheTTL = 3600; // seconds; 0 = indefinite (default)
Package authors
- Ship a
Providersubclass in your package namespace. - Document the FQCN for consumers to add under
[app_providers]oraddAppProvider(). - Prefer unique API version strings in
routes()(e.g. your package name) to avoid switch collisions. - Keep
onBooted()lean — heavy work belongs in commands or async jobs.
Common mistakes
- Calling
logger()ortable()insidemiddlewares()before the container is ready — useonBooted()for bindings that need a fully booted app - Duplicating JWT registration in both
[app_authentications]andauthentications()without documenting order — backend order matters - Heavy database migrations in
onBooted()— slows every worker boot; use CLI commands instead - Removing a provider without
cache:clear— stale middleware or routes may persist
What’s next
Composer packages
Publish a provider on Packagist.
Middleware
Global pipeline after provider registration.
Authentication
JwtAuthBackend via authentications().