milosa/social-media-aggregator-bundle
The Milosa Social Media Aggregator Bundle for Symfony lets you collect and display social media posts in one unified feed. Configure providers, aggregate content, and render results in your PHP app with minimal setup. Ideal for dashboards and marketing pages, with simple configuration and template-f...
Know what you're adopting first. This is a Symfony bundle — not a Laravel package. There is no Illuminate\Support\ServiceProvider, no package discovery, no config/*.php file, and no facade. As of v0.0.10 the plugin system has been removed and every integration now lives in this one repository, so there is no longer a plugin registry to discover providers through — you read src/ and find them directly. The repo is archived (last tagged release Dec 2020), so treat it as source code to read and mine, not a dependency to composer require and forget.
Where to look, in order:
composer.json — check the symfony/* constraints. A 2020 bundle will typically require Symfony 4.4/5.x. Laravel 10/11 ship Symfony 6/7 components, so composer require may hard-fail on conflict. If it does, stop here and vendor the classes manually.src/ — find the aggregator/feed class (the thing that returns a combined list) and the provider/adapter interface each platform implements. Since v0.0.10 merged the plugins in, all platform providers are colocated here; this is the only part with real value.DependencyInjection/ + Resources/config/services.* — shows how providers are registered and which parameters (API keys, limits, platform toggles) are expected. Note that the v0.0.10 merge means this file grew: every provider is now wired unconditionally, so check whether you can actually disable platforms you don't use.README — usually contains the sample config/packages/*.yaml block; that's your de-facto config schema. It may still describe the old plugin installation flow, so trust the YAML over the prose.Minimal first run (Symfony-style, in a scratch app or a Laravel dev container):
composer require milosa/social-media-aggregator-bundle
Register the bundle in bundles.php, copy the YAML config from the README into config/packages/, fill in the platform credentials, then resolve the aggregator service from the container and foreach over the returned messages.
First use case to target: a read-only "social wall" endpoint — one route, one call into the aggregator, one normalized JSON response. Don't try to solve two-way posting or per-user OAuth on day one; this package is a read aggregator.
1. Wrap, don't spread. Write a single Laravel service provider that binds the aggregator (or your own extracted SocialFeed class) as a singleton, and inject that everywhere. Never let a controller touch the bundle's container directly.
$this->app->singleton(SocialFeed::class, fn () => new SocialFeed(
providers: [
new TwitterProvider(config('services.twitter.token')),
new FacebookProvider(config('services.facebook.token')),
],
));
2. Adapter-per-platform is the real architecture. The bundle's model is one class per network returning a common message shape — and after v0.0.10 that's all still intact, just consolidated. Keep that contract and add your own providers as separate files implementing the same interface — this is also the cleanest way around the MPL-2.0 file-level copyleft.
3. Normalize at the boundary, not in the view. Map each platform payload to your own DTO (id, platform, author, body, mediaUrl, publishedAt, permalink). Every network names those fields differently and will rename them again. Your Blade/API layer should never see platform-specific keys.
4. Pull into a table, don't render live. The single biggest reliability win: an artisan social:sync command on the scheduler that writes messages into social_messages, deduped on (platform, external_id). Your feed endpoint then queries your own DB. API outages, rate limits, and token expiry stop being page-load failures.
5. Cache the API layer, queue the sync. Wrap each provider call in Cache::remember("social:{$platform}:{$account}", now()->addMinutes(10), ...) and run the sync in a queued job. The bundle has no caching or rate-limit handling of its own.
6. Test by faking the provider, not the network. Because providers are plain classes behind an interface, bind a stub implementation in the test container. Where the bundle does its own HTTP, use Laravel's Http::fake() — but only if it's built on Symfony HttpClient, which you'll need to verify in src/.
7. The removed plugin system changes what you fork. Previously you could in principle swap a plugin package without touching this repo; now you cannot. Fork strategy should be "copy src/Provider/* into app/Social/Providers/ and rewrite the HTTP layer", not "keep the bundle and override a plugin".
%parameters%, and older bundles use !tagged / compiler passes that Symfony 6.1+ removed or renamed. If you install it, you'll be translating DI config by hand — usually not worth it versus copying src/ into app/Social/.env() in bundle config won't survive config:cache. Move every credential into config/services.php and read it through config(). This is the number-one silent production break for ported Symfony config.expires_at and refresh before calling.since_id (Twitter), cursor strings (Facebook/Instagram), until/before (others). Don't try to unify into one cursor until you've read each provider's loop; a "fetch N" abstraction per platform is more honest.CarbonImmutable at the boundary and sort on that, not on string comparison.tinker, app()->make(...) the aggregator and dump() the raw per-provider responses before normalization. Log the raw payload for one sync cycle — it's the fastest way to see which platform broke.src/, lift the interface and the merge/dedup logic, and write the HTTP calls against Laravel's Http client with retry() and throw() semantics you actually control. At 16 stars and zero dependents, the maintenance risk of depending on it outweighs the ~200 lines you'd otherwise write.How can I help you explore Laravel packages today?