itsgoingd/slim-services
Bring Laravel-style service providers and facades to Slim 3. Slim Services adds a service container, provider bootstrapping, and static facades so you can organize dependencies and app bootstrapping the way you would in Laravel.
composer require itsgoingd/slim-services
The package targets Slim 3 (Pimple-based Slim\Container) plus the illuminate/* components (5.x line). It exists to solve one specific problem: Laravel packages assume an Illuminate\Container\Container, a config repository, and a facade root — Slim gives you none of those. This package wires them together so a Laravel ServiceProvider can be registered and used inside a Slim app.
Where to look first: src/ — the manager class is small (a few hundred lines at most) and reading it kills all ambiguity about method names and boot order. Then the README example, which is essentially the whole API surface.
Minimal boot, in public/index.php before $app->run():
require __DIR__ . '/../vendor/autoload.php';
$app = new \Slim\App();
// 1. Build the bridge on top of the Slim container
$services = new \Itsgoingd\SlimServices\ServiceManager($app->getContainer());
// 2. Register any Laravel package providers you need
$services->register(Some\Package\ServiceProvider::class);
// 3. Run
$app->run();
Use it from a route:
$app->get('/thing', function ($request, $response) {
$service = $this->get('laravel')->make(Some\Package\SomeService::class);
return $response->write($service->run());
});
(In Slim 3, route closures are bound to the container, so $this->get(...) is the idiomatic access path.)
First real use case: pulling in a package like itsgoingd/clockwork, a Laravel-oriented mailer, or an image/PDF library that ships a ServiceProvider and reads its settings via $app['config']. Register the provider, seed the config key it expects, then resolve the service. That's the entire value proposition.
Bootstrap in one place. Do every register() call in your front controller or a dedicated bootstrap/services.php included from it. Provider ordering matters — later providers can depend on bindings from earlier ones — so keep the list linear and readable rather than scattering registrations across route files.
Config seeding. Laravel packages read through Illuminate\Contracts\Config\Repository. The bridge provides one; populate it before registering providers so mergeConfigFrom() finds what it expects:
$config = $container->get('config');
foreach (glob(__DIR__ . '/../config/*.php') as $file) {
$config->set(basename($file, '.php'), require $file);
}
Name the keys exactly as the package expects (clockwork, mail, view, etc.). Packages calling mergeConfigFrom($path, 'foo') will throw if foo isn't reachable on the repository.
Facades. Set the root once, before anything resolves a facade:
Illuminate\Support\Facades\Facade::setFacadeApplication($container->get('laravel'));
After that Config::get(...), Log::info(...), and package-provided facade aliases work. Skipping this yields the classic "A facade root has not been set."
Two access idioms, pick one. Explicit resolution ($this->get('laravel')->make(Foo::class)) is easier to test and grep. Static facades (Config::get()) are shorter but hide the dependency. I'd keep facades for config/logging and resolve anything with real business logic explicitly.
Middleware. Packages that ship Laravel middleware can usually be adapted: construct the middleware with the Laravel container, then wrap it in a Slim middleware that feeds it PSR-7 requests. Clockwork is the canonical example — the point is to share one Laravel container so data collected during the request is visible to the collector.
Testing. Because everything funnels through the Laravel container, you can stub at that seam:
$container->get('laravel')->instance(Mailer::class, $fakeMailer);
You don't need to touch Slim's container for this, which keeps test setup independent of route wiring.
Conditional registration. Providers that do heavy boot work (registering many bindings, reading files) can be registered behind a config flag so they only load in the environments that need them.
Two containers, no automatic sharing. Slim's Pimple container and the Laravel container are distinct. A package resolving Illuminate\Contracts\Container\Container gets the Laravel one and cannot see Slim's settings. If a package needs a Slim value, bind it explicitly: $laravel->instance('settings', $container->get('settings')).
Pimple closures are lazy and shared. $container['foo'] = function ($c) { ... }; caches the result. If a package expects a fresh instance per call, use $container->factory(...) on the Slim side, or bind() (not singleton()) on the Laravel side.
Facades memoize. Once a facade resolves its instance, later rebinding has no effect. Set the facade root and all bindings before the first request is handled.
register() is not boot(). A surprising number of packages only bind services in boot(). If resolution fails, verify the bridge actually called both phases — and in the right order across providers.
No Illuminate\Foundation\Application. Anything type-hinting the full-stack app (queue workers, Artisan, packages that call $app->runningInConsole(), most "full Laravel" packages) will not work. Stick to packages written against Illuminate\Container\Container plus individual components.
Composer auto-discovery is ignored. extra.laravel.providers in a package's composer.json is a Laravel-installer feature. Outside Laravel you must enumerate every provider manually. Check the package's README for the provider list.
Class aliases are not automatic. If a package's docs say App::make(...) or reference a facade alias registered via Laravel's aliases config, register it yourself with class_alias() or the facade won't exist.
Events must be wired. Packages that dispatch events need an Illuminate\Events\Dispatcher constructed with the container and bound as events. Without it, listeners silently never fire — no exception, just nothing.
Slim 4 is a different world. Slim 4 dropped Slim\Container and Pimple entirely. This package targets Slim 3; don't assume it drops into a Slim 4 app without checking for a fork or an updated branch.
Debugging checklist. var_dump(Facade::getFacadeApplication()) to confirm the root; $laravel->getBindings() after registration to see what was actually bound; $container->get('laravel')->make($class) in isolation to confirm the failure is in the package, not your Slim wiring.
Extension point. The registrar is small enough to fork or subclass. If you need custom ordering, deferred-provider handling, or an interop layer that proxies Slim bindings into the Laravel container, subclassing the manager is far cheaper than fighting provider boot order.
How can I help you explore Laravel packages today?