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

Facade Bundle Laravel Package

indragunawan/facade-bundle

A Laravel package that bundles a set of ready-to-use facades, giving you concise static access to commonly used services and helpers. Reduce boilerplate imports and keep your code clean with simple, consistent facade calls.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require indragunawan/facade-bundle

With Symfony Flex the recipe (if present) registers it; otherwise add it manually to config/bundles.php:

return [
    // ...
    IndraGunawan\FacadeBundle\FacadeBundle::class => ['all' => true],
];

v0.2.0 adds Symfony 5 compatibility, so composer require on a Symfony 5 app is now a supported combination rather than an untested gamble. On Symfony 4.4 and 5.x you should be able to install without constraint gymnastics; on Symfony 6/7 still check the solver first (see Gotchas). There is no configuration tree worth worrying about for a first pass — the bundle is a thin container wrapper, so the only thing you configure is your own facade classes.

Where to look first

Open vendor/indragunawan/facade-bundle/src/ and read the base facade class (the analogue of Laravel's Illuminate\Support\Facades\Facade) plus the bundle's DependencyInjection extension/compiler pass. Two questions to answer in five minutes:

  1. What method must a subclass implement to point at a service id? (Conventionally getFacadeAccessor(), but confirm the exact name and visibility from the base class — the API is small enough that you should read it rather than assume.)
  2. How does the facade reach the container? This determines whether the services you want to proxy must be public, aliased, or exposed through a service locator. This is the single most important detail for using the bundle on a modern Symfony app.

Also glance at composer.json for the symfony/* constraints now that v0.2.0 has landed — the release note is a single line ("Symfony 5 compatibility"), so the constraint file is the authoritative statement of what is actually supported.

First use case

The classic fit: a service you inject into a dozen controllers, commands and listeners purely to make one call. A settings/feature-flag service is a good candidate.

namespace App\Facade;

use IndraGunawan\FacadeBundle\Facade; // or AbstractFacade — check vendor

/**
 * @method static mixed get(string $key, mixed $default = null)
 * @method static void set(string $key, mixed $value)
 *
 * @see \App\Service\Settings
 */
class Settings extends Facade
{
    protected static function getFacadeAccessor(): string
    {
        return \App\Service\Settings::class; // the container service id
    }
}

Usage:

use App\Facade\Settings;

$timezone = Settings::get('timezone', 'UTC');

That is the whole value proposition: Settings::get() instead of a constructor parameter you never otherwise touch. Note the @method docblock — static proxies are invisible to PHPStan/Psalm and to your IDE, so the annotations are not optional in a codebase that cares about static analysis.

Implementation Patterns

One facade per service, kept in a dedicated namespace. App\Facade\* with a single getFacadeAccessor() returning the service id. Treat each facade as a five-line file with a full @method block copied from the target service's public API (or generated once and pasted). If a facade grows logic, that logic belongs in the service.

Proxying framework and third-party services. The bundle earns its keep when wrapping services Symfony hands you as ids rather than constructor arguments: router, twig, security.token_storage, doctrine.orm.entity_manager, translator. These are usually private, so you'll typically create your own alias or a small wrapper service in config/services.yaml and point the facade at that — which is often cleaner anyway, because it lets you narrow the surface to two or three methods.

Layer discipline as a team convention. A workable rule: facades are allowed in the edge layers — controllers, console commands, event listeners, Twig extensions, migration scripts — and forbidden in domain services and anything you'd want to reuse in another project. This gets you Laravel-ish ergonomics where DI ceremony adds nothing, without turning your service layer into a pile of hidden globals. Write the rule in CONTRIBUTING.md before the first PR, not after.

Testing. Because resolution happens at call time, you can substitute the underlying service in the test container and the facade picks it up:

self::getContainer()->set(Settings::class, $fakeSettings);
// or, in a KernelTestCase, override the alias before boot

If the base facade caches resolved instances, look for a reset/clear method and call it in tearDown() — otherwise a fake leaks into subsequent tests in the same process.

Migration ergonomics. The strongest real-world pattern for this package is porting Laravel-ish code or a legacy app onto Symfony: Cache::get(), Log::info(), Config::get() keep working while the implementation behind them becomes a proper Symfony service. That is a legitimate, temporary bridging strategy — mark the facades as such with @deprecated when you introduce them if you intend to unwind them later. On Symfony 5 specifically, this is now the best-supported path the package offers: ServiceSubscriberInterface, autowired locators and kernel.reset all behave as you'd expect, so the bridge is straightforward to wire.

Pair with a service locator for the accessor. If the bundle exposes a ServiceSubscriberInterface-style locator, register the ids you actually need rather than making services public. Keeps debug:container honest and avoids the "everything is public" slide. Symfony 5's locator ergonomics make this cheap — prefer it over public aliases when proxying framework services.

Gotchas and Tips

Private services are the number one failure mode. Since Symfony 4.0 services are private by default and $container->get('app.settings') throws for a private id. If Settings::get() blows up with a "service not found / private service" error, that is almost always why. Fix by aliasing to a public id in services.yaml:

services:
    app.settings.public:
        alias: App\Service\Settings
        public: true

Then point the accessor at the alias. Prefer this over blanket _defaults: public: true.

Do not call facades during container bootstrapping. Compiler passes, bundle boot(), and anything running before the container is frozen will fail or return half-initialised services. Facades are for request-time code only.

Caching and staleness. If the base class memoises the resolved instance, a facade called before your test override stays bound to the real service. Find the reset method (Laravel's is clearResolvedInstances(); this bundle may or may not expose one — v0.2.0's notes do not mention one) and use it in test teardown, or accept per-test isolation via separate kernel boots.

No autocompletion, no static analysis. Every facade needs @method annotations or an IDE-helper stub, and even then PHPStan will treat the return types as mixed unless the annotations are precise. Budget for this — it is the main tax on the pattern.

Debugging recipe. bin/console debug:container <id> tells you whether the service exists and whether it's public; bin/console lint:container catches wiring errors before runtime; bin/console debug:container --tag=... if the bundle tags anything. When a facade call returns something unexpected, get_class() the resolved instance — it is frequently a decorated or aliased service, not the one you assumed.

Symfony version compatibility: improved, not solved. v0.2.0 is explicitly "Symfony 5 compatibility," which retires most of the original 4.4-era risk and makes Symfony 5 the target platform. What it does not do is establish Symfony 6/7 support. Before adopting:

composer why-not symfony/framework-bundle
composer show indragunawan/facade-bundle   # inspect the symfony/* constraints

If the constraint reads ^4.4|^5.0, Symfony 6 is out of scope and Symfony 7 will not solve. Two specific things to grep for on a 6.x attempt: the ContainerInterface::get() return-type narrowing (?object/mixed → object in 6.0) and any remaining ContainerAwareInterface usage, removed in 6.0. A five-minute smoke test — install, define one facade, call it from a controller — is worth more than reading the changelog, because the changelog is one line.

Vendor risk mitigation. This is roughly forty lines of code: a base class holding a static container reference and a __callStatic that resolves the accessor and forwards. v0.2.0 is a compatibility bump, not a feature release, so the underlying implementation is essentially the same as the 2020 version — the pattern is still small enough to own. If you hit friction, or if you need Symfony 6+, vendor it into src/Support/Facade.php in your own app, wire the container in a small compiler pass, and drop the dependency. Given the package's age and adoption, that remains a defensible default — the bundle's value is the convention, not the implementation.

Extension point. If the bundle ships a compiler pass that auto-registers facades by tag or attribute, use it instead of hand-registering each facade as a service; otherwise just tag them yourself (kernel.reset if the base class holds state) so they participate in the normal container lifecycle.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package