Service container

Category: Core Architecture

VelvetCMS\Core\Application doubles as the framework bootstrap and dependency injection container. Everything—services, facades, middleware, commands—flows through it.

Registering services #

Application::__construct() calls registerCoreServices() which binds:

  • The container itself under Application::class and VelvetCMS\Core\Application.
  • config → singleton ConfigRepository.
  • eventsEventDispatcher.
  • loggerServices\FileLogger (PSR-3) with level from config('app.log_level').
  • exceptions.handlerVelvetCMS\Exceptions\Handler with renderers/reporters from config/exceptions.php.
  • routerVelvetCMS\Core\Router with middleware aliases/global stack from config/http.php.
  • dbVelvetCMS\Database\Connection using config('db') (supports sqlite/mysql/pgsql).
  • cacheDrivers\Cache\FileCache or RedisCache depending on config('cache.default').
  • cache.tagsSupport\Cache\CacheTagManager for granular invalidation.
  • migrations.runnerDatabase\Migrations\DefaultMigrationRunner.
  • sessionServices\SessionManager.
  • markdown, content.driver, pages, theme, modules (see dedicated docs).

You can register your own bindings inside modules:

$app->singleton(AnalyticsService::class, function() use ($app) {
    return new AnalyticsService($app->make('events'));
});
$app->alias(AnalyticsService::class, 'analytics');

Autowiring #

If make(Foo::class) is called and the service is not explicitly bound, the container attempts to instantiate it by reflecting on the constructor, caching dependencies per class in $reflectionCache. Only class-hinted parameters are allowed; untyped parameters throw informative exceptions.

Aliases & facades #

  • Aliases let you resolve a binding using a human-readable key. Example: $app->alias('cache', CacheDriver::class); so app(CacheDriver::class) and app('cache') both return the same instance.
  • Facades (VelvetCMS\Facades\App, DB, Session) are ultra-thin: they store the container (App::setInstance($app)) and forward static calls to real services. No magic global state.

Helper functions #

src/Support/helpers.php exposes utility functions that always pull from the container:

  • app($abstract = null) – resolves services.
  • config('key') – fetches values via ConfigRepository with dot notation.
  • base_path(), storage_path(), content_path() – consistent path helpers.
  • view('template', $vars) – shorthand for ThemeService::view().
  • csrf_token(), csrf_field(), method_field() – session + security helpers.
  • route('name', $params) – proxies to Router::url().

Adding your own services #

  1. Create a service class anywhere under src/ or a module.
  2. Bind it in a service provider/module register() method via singleton() or bind().
  3. (Optional) add aliases or facades for ease-of-use.
  4. Resolve it through dependency injection, app(Service::class), or helper functions.

Because everything funnels through the same container, CLI commands, HTTP controllers, and modules all get the exact same instances, simplifying caching, logging, and DB access.