gponty/symfony-idloom-api-bundle
A Symfony bundle for integrating the IDloom API into your application. It provides configuration, service wiring, and convenient access to IDloom endpoints so you can manage events, registrations, and related data from Symfony services and controllers.
This is a Symfony bundle, not a Laravel package — the first thing to internalize. Laravel reuses Symfony components (HttpClient, Console, Finder) but not the bundle/kernel system, so you cannot composer require this and expect config/packages/idloom_api.yaml to do anything in a Laravel app. Plan your integration accordingly.
DependencyInjection/ and Resources/config/ in the repo. The bundle's real value is the service class(es) it registers (typically an IdloomClient / API wrapper) plus DTOs. Those classes are plain PHP and reusable; only the DI wiring is Symfony-specific.idloom/*-client package, depend on that directly in Laravel and skip the bundle. If it does not, composer require the bundle and use just its service classes (vendor/gponty/symfony-idloom-api-bundle/src/...), instantiating them manually or via a Laravel service provider.Configuration.php for the required options — almost certainly an API key/secret, an account/tenant identifier, and a base URL. Those become entries in config/services.php and .env for a Laravel wrapper.A minimal Laravel wrapper looks like:
// config/services.php
'idloom' => [
'base_uri' => env('IDLOOM_BASE_URI', 'https://api.idloom.com/'),
'token' => env('IDLOOM_API_TOKEN'),
'account' => env('IDLOOM_ACCOUNT_ID'),
],
// app/Providers/IdloomServiceProvider.php
public function register(): void
{
$this->app->singleton(IdloomClient::class, fn ($app) => new IdloomClient(
config('services.idloom.base_uri'),
config('services.idloom.token'),
// whatever constructor the bundle's client actually exposes
));
}
If the client's constructor is awkward (DI annotations, injected Symfony services), copy the class into app/Services/Idloom/ and adapt it — the wrapper is small enough that this is usually cheaper than fighting the container.
Read models first, write paths second. Idloom exposes events/registrations/attendees. Build a small repository layer around the client so your controllers and jobs never touch the raw HTTP contract:
final class IdloomEvents
{
public function __construct(private IdloomClient $client) {}
public function all(): Collection
{
return collect($this->client->getEvents())->map(fn ($e) => EventData::fromArray($e));
}
}
This insulates you if (when) you swap the bundle's client for a hand-rolled one.
Sync via queued jobs, not request cycles. Event registration lists are paginated and latency-bound. Put pulls in a SyncIdloomEvents job on a scheduler (->hourly()), and pushes (creating registrations, updating attendees) in dispatachable jobs with retry/backoff. The bundle's client is synchronous HTTP; do not call it inline from a web controller.
Webhook ingestion where available. If Idloom can call out on registration events, prefer that over polling — wire a route, verify the signature (check the bundle for a validator or implement HMAC against your secret), and queue the payload. This is the cleanest day-to-day integration for a Laravel app.
Idempotency on the write path. Keep a local idloom_registrations table keyed by the remote ID. Ingest jobs should upsert, not create, so re-running a sync or replaying a webhook is safe. The bundle likely gives you raw arrays — do the normalization (dates, statuses, attendee sub-lists) in your own DTOs.
Config from env, cached in prod. Bind base_uri, token, and account in config/services.php, and always php artisan config:cache in deployment. If the bundle carries a default base URI, override it explicitly — you want one source of truth, not a silent fallback.
Testing. Hit Http::fake() at the outer boundary — but only if you've refactored the client to use Laravel's HTTP client. If it's hard-wired to Symfony's HttpClient or Guzzle, fake that instead (GuzzleHttp\Handler\MockHandler) or wrap the client behind an interface and fake the interface. The latter is worth the ten minutes.
registerBundles, no Kernel, no compiler passes. If you see the bundle's service definitions, you'll either replicate them in a service provider or bypass them. Do not waste time trying to make Symfony DI work under Laravel — the container models are different.composer.json, vendor the relevant source into app/Services/Idloom/ if it's small, and add a note in your repo explaining where the code came from. You do not want a composer update in six months to become a breakage investigation over an unmaintained package.Authorization: Bearer header, an API-key query param, or a signed request. The bundle's client constructor and Configuration.php are the source of truth — read them, don't guess from the vendor's public API docs (they often drift from what the bundle actually sends).Carbon::parse(...)->utc()) and store that. Mixing them will produce off-by-one-hour bugs on registration deadlines that are miserable to trace.try { ... } catch { retry } will hammer the API on a 401 and get you rate-limited or blocked. Wire RateLimiter::for('idloom', ...) around the client.IdloomClientInterface in your app with an adapter implementation lets you add logging, retries, and metrics without touching vendored code. If the bundle exposes events/listeners (Symfony EventDispatcher), port those subscribers to Laravel listeners rather than trying to reuse the dispatcher wiring.composer show will tell you what version you actually got. If the tag looks wrong or the repo is stale, pull the client code into your project rather than depending on the tag.debug level in the adapter. Most integration failures here are a wrong base URI or a wrong auth header, and seeing the actual request resolves them in one cycle instead of five.How can I help you explore Laravel packages today?