firelink-media/tds-provider-bundle
A Laravel bundle that packages TDS provider integrations into one installable service provider. It registers bindings, config, and auto-discovery so you can wire TDS services into your app quickly, with sensible defaults and an extensible structure for adding custom providers.
This is a Symfony bundle, not a Laravel package — the Bundle suffix in the name and the tds-bundle repository are the giveaway. Before you composer require anything, decide how you're going to bridge it:
composer require firelink-media/tds-provider-bundle, then register FirelinkMedia\TdsProviderBundle\TdsProviderBundle in config/bundles.php and import the extension config under config/packages/tds_provider.yaml.ServiceProvider and no extra.laravel block, so package auto-discovery will do nothing. You either run it in a Symfony service container alongside your Laravel app, or write a thin Laravel ServiceProvider that instantiates the bundle's services manually (see Implementation Patterns).Where to look first, in this order:
src/DependencyInjection/Configuration.php — this is the source of truth for every config key, default, and required value. The README is likely thinner than this file.src/DependencyInjection/*Extension.php — shows how services are wired, which are public/private, and what parameters get set from config.src/Provider/… or src/Client/…) — that's your primary injection target.First use case: inject the provider service into a controller or console command, call its single "fetch/request" method with a minimal payload, and dump the response. Do this with hardcoded credentials in dev before wiring anything else — it proves the HTTP layer, auth, and DTO mapping all work end-to-end.
Inject the interface, not the client. Bundles of this shape almost always expose a …ProviderInterface. Type-hint that in your constructors and let the container resolve it; this keeps you swappable and testable (mock the interface in unit tests instead of hitting the network).
final class SyncCommand extends Command
{
public function __construct(private readonly TdsProviderInterface $tds) {
parent::__construct();
}
}
Drive everything from config, not code. Credentials, base URIs, and timeouts belong in config/packages/tds_provider.yaml with env-var indirection (%env(TDS_API_KEY)%). Never hardcode endpoints — the bundle's Configuration class will validate required keys at container compile time, which is exactly the failure mode you want (fail on cache:clear, not in production at 2 a.m.).
Wrap calls in your own adapter service. Don't sprinkle $this->tds->request(...) through controllers. Create a TdsGateway in your app that owns retries, logging, response normalization, and caching. The bundle gives you transport; your app owns policy.
For Laravel consumers, the cleanest pattern is a small bridge service provider:
public function register(): void
{
$this->app->singleton(TdsProviderInterface::class, function ($app) {
return new HttpTdsProvider(
config('services.tds.base_uri'),
config('services.tds.api_key'),
);
});
}
Then mirror the YAML keys into config/services.php. You lose the bundle's config validation, so add your own required checks in the provider.
Use events/decorators for cross-cutting concerns. If the bundle dispatches events (check for an Event/ directory), subscribe to them for audit logging. If it doesn't, decorate the provider interface and log around the delegate — no forking required.
Batch and queue the heavy calls. Anything that reaches out to a remote provider should be a queued job or a scheduled command, not a synchronous web request. The bundle won't do this for you.
composer.json ("firelink-media/tds-provider-bundle": "1.0.*", not ^1.0) and read the diff before every composer update. There is no community to Google when it breaks.public: true overrides in your app config or, better, inject via constructor and avoid $container->get() entirely.bin/console debug:container --tag=… and debug:config tds_provider. When a service "isn't there," it's almost always a missing bundle registration in bundles.php or a config key that doesn't match the Configuration tree's snake_case naming.CompilerPass, or an event dispatcher. If none exist, your only extension point is decoration — which is fine, but plan for it up front rather than after you've called the client in 40 places.composer.json constraints against your Symfony version before committing. A bundle this young may only support a narrow range, and composer why-not is the fastest way to find out.How can I help you explore Laravel packages today?