noetix/traction-bundle
Noetix Traction Bundle is a Symfony bundle that integrates Traction into PHP applications. It packages configuration and services so you can install via Composer, enable the bundle, and build against its integration points.
Treat this as reference code, not a dependency. It is a Symfony2-era bundle, archived, with 2 stars and zero dependents — there is no maintenance path, no security response, and no compatibility work for modern PHP. Read it, lift what you need, and move on.
Where to look first, in order:
composer.json — the require block is the most valuable file in the repo. It tells you which Symfony minor (likely 2.x), which Guzzle major (likely 3.x or 4.x), and which underlying Traction API client — if any — the bundle wraps. That underlying client is what you actually want.DependencyInjection/Configuration.php — the canonical list of config keys (base URL, credentials/token, timeouts). This is your spec.Resources/config/services.yml — the service IDs and constructor arguments. This tells you what to inject and what the public surface is.Client/, Api/, or Service/ — read the public methods; that is your real integration surface.Minimal Symfony2 usage, if you genuinely had to run it in situ:
// app/AppKernel.php
new Noetix\TractionBundle\NoetixTractionBundle(),
// app/config/config.yml
noetix_traction:
base_url: %traction_base_url%
token: %traction_token%
Then inject the client service into your own service rather than reaching for the container.
The Laravel path (recommended): do not install this bundle. Composer will fight you on symfony/symfony and Guzzle constraints. Instead, copy the client class out of the repo, and drive it from a small wrapper bound in a service provider using Laravel's Http client or a current Guzzle. First use case: one authenticated read — a GET against a single resource — asserted from php artisan tinker. Confirm auth and response shape before writing anything else.
Wrap, never inject the vendor client directly. Define an interface (TractionClient) in your app and a single adapter implementation. The archived client becomes an implementation detail you can replace in an afternoon.
Config-driven construction. Mirror the bundle's config tree in config/services.php:
'traction' => [
'base_url' => env('TRACTION_URL'),
'token' => env('TRACTION_TOKEN'),
'timeout' => env('TRACTION_TIMEOUT', 10),
],
Bind in a provider with $this->app->singleton(TractionClient::class, ...). Use the interface as the injection type everywhere else.
Decorator layers pay for themselves here. Because the underlying transport is old, put retry and caching in decorators around your interface rather than inside the client:
RetryingTractionClient — retry on 429/5xx with backoff.CachedTractionClient — cache idempotent reads with a short TTL.LoggingTractionClient — log method, path, status, duration, correlation ID. Never log the token or full bodies.Writes go through the queue. Any POST/PUT/DELETE should be a job with explicit retry and failure handling. Synchronous writes against an unmaintained HTTP client is how you get hung workers.
Testing. Fake the transport, not the interface, for the adapter's own tests (Http::fake() or Guzzle's MockHandler with recorded fixtures); fake the interface for everything downstream. Keep one opt-in contract test that hits a sandbox if one exists — that is your early warning that the API drifted.
If you must stay on Symfony2: define named clients via a compiler pass if you talk to multiple Traction instances, tag an event subscriber for request logging, and use KernelTestCase + the container to assert wiring. Override the bundle's service definition in your app's services.yml rather than editing vendor code — that is the sanctioned extension point.
composer why-not to be your first five minutes. Do not spend them; extract instead.connect_timeout, timeout, and http_errors explicitly. A default of "wait forever" is a production outage waiting for a slow upstream.Guzzle\Http\Exception\*. Catch them at the adapter boundary and rethrow as domain exceptions (TractionUnavailable, TractionRejected) so callers never catch (\Exception).getAlias() in DependencyInjection/*Extension.php — a wrong key fails quietly in Symfony 2.AppKernel entry; forgetting it yields "service not found," not a helpful error.parameters.yml/.env, never in the committed config block. The bundle's own docs (if any) frequently show the token inline.*Bundle.php, DependencyInjection/, Resources/config/) is the only part you throw away.How can I help you explore Laravel packages today?