mmoreram/spotify-api-bundle
Symfony bundle that integrates the Spotify Web API into your app, wrapping the PHP client with service configuration, dependency injection, and ready-to-use endpoints for auth, playlists, tracks, and user data. Lightweight and easy to wire into existing Symfony projects.
Before you install, know what you're getting. This bundle targets Symfony 2.x and was last released in mid-2014. It is a thin wrapper around the Spotify Web API (metadata: albums, artists, tracks, playlists, search) — it is not the playback SDK, and it has no OAuth flow built in. Zero dependents and 12 stars means you are effectively adopting unmaintained code. Use it if you're already on a legacy Symfony 2 app; otherwise read the "Gotchas" section first and consider a modern client instead.
Minimal setup on a Symfony 2 app:
composer require mmoreram/spotify-api-bundle
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Mmoreram\SpotifyApiBundle\MmoreramSpotifyApiBundle(),
);
}
Where to look first:
src/Mmoreram/SpotifyApiBundle/Services/ — the client class. This is the package; read it top to bottom before writing any code. It's a few hundred lines at most.Resources/config/services.yml — the service id (something like mmoreram.spotify_api). Confirm with:
php app/console container:debug | grep -i spotify
DependencyInjection/Configuration.php — the small set of config keys (base URL, timeouts, any credentials).First use case — fetch an album and render its tracks, which exercises the whole happy path (HTTP call → response mapping → your template):
// Acme/DemoBundle/Controller/AlbumController.php
public function showAction($spotifyId)
{
$album = $this->get('spotify_api')->getAlbum($spotifyId);
return $this->render('AcmeDemoBundle:Album:show.html.twig', array(
'album' => $album,
));
}
If that call returns an array/stdClass with name, artists, and tracks, the bundle is wired correctly and you can build outward from there.
Inject, don't fetch. The service is defined in the container, so wire it into your own classes via constructor or setter injection. Reaching into $this->get('spotify_api') from a service is the fastest route to an untestable codebase.
# Acme/DemoBundle/Resources/config/services.yml
services:
acme.spotify.repository:
class: Acme\DemoBundle\Spotify\SpotifyRepository
arguments: ['@spotify_api']
Wrap it in your own adapter. Given the package's age, treat it as a disposable transport. Define a small interface in your domain (AlbumProviderInterface::find($id)), implement it with a SpotifyApi-backed class, and never let SpotifyApi types leak into controllers or templates. When Spotify's response shape changes — or you swap to jwilsson/spotify-web-api-php — you change one class.
Use it for search and lookups, not for user data. The bundle's surface is essentially read-only metadata (getAlbum, getArtist, getTrack, search...). The natural day-to-day workflows are:
search from a JSON endpoint, throttle the input on the client side, and cache results per normalized query string.Cache aggressively at the adapter layer. Spotify metadata is stable for hours, and you will hit rate limits quickly on a busy site. Use Symfony's doctrine_cache or a simple stash/Redis layer keyed by method + arguments:
$key = 'spotify.album.' . $id;
if (false === $album = $this->cache->fetch($key)) {
$album = $this->api->getAlbum($id);
$this->cache->save($key, $album, 3600);
}
Handle errors by translation, not propagation. Guzzle will throw on 4xx/5xx. Catch at the adapter boundary and return null for not-found, re-throw for transport failures, and retry on 429 after honoring Retry-After.
try {
return $this->api->getTrack($id);
} catch (\Guzzle\Http\Exception\ClientErrorResponseException $e) {
return null; // 404, or a malformed id
}
Pagination is manual. Spotify uses limit/offset and returns next/previous URLs. The bundle predates any convenience wrapper, so your sync command must loop on offset until the result set is empty. Cap limit at 50.
Testing. Because the service is small, mock it. In a Symfony 2 functional test, replace the spotify_api service in the test container with a stub; in unit tests, mock your own adapter interface and never touch HTTP. Add a thin integration test (marked as slow) that hits /v1/albums/... with a known ID so you notice the day Spotify breaks the bundle's response mapping.
Debug the transport, not the wrapper. If a call fails, temporarily bind a logging listener to the underlying Guzzle client (or just var_dump the raw response body) before blaming the bundle — the wrapper adds very little, so almost every bug is an HTTP/auth problem.
AppKernel registration is required. On Symfony 4+ the bundle class registration still works via config/bundles.php in some cases, but the dependency constraints (PHP 5.x-era Guzzle, old symfony/* ranges) will fight Composer. Expect to fork it, or copy the single service class into your app and drop the bundle scaffolding entirely — honestly, that's often the fastest path.https://accounts.spotify.com/api/token with grant_type=client_credentials) and inject Authorization: Bearer ... into every request — either by subclassing the service or by configuring the underlying Guzzle client's default headers.getAlbum returns unexpected keys, log the raw JSON and patch the mapping locally rather than waiting for an upstream fix that will never come.Guzzle\Http\Client, Guzzle\Http\Exception\... namespaces), while most of your other dependencies assume Guzzle 6/7 (GuzzleHttp\Client). Mixing them produces confusing "class not found" errors that look like bundle bugs but are version conflicts. composer why guzzle/guzzle is your friend.container:debug beats reading docs that don't exist. The id, the class name, and the config keys are all visible there.Configuration.php; if not, override the service definition in your app's services.yml rather than editing vendor code.composer update; vendor edits do not.parameters.yml / environment variables and pass them into your token provider.How can I help you explore Laravel packages today?