medicorenl/stash-api-bundle
Laravel/PHP bundle that wraps the Stash caching library in a convenient API, letting you register cache pools, switch storage drivers, and manage keys with minimal configuration for fast, PSR-compliant caching.
composer require medicorenl/stash-api-bundle
config/bundles.php:
MedicoreNL\StashApiBundle\StashApiBundle::class => ['all' => true],
config/packages/stash_api.yaml (or .yml). You'll need the Stash/Bitbucket Server base URL, and either an HTTP basic user + personal access token or an OAuth consumer. Check the bundle's DependencyInjection/Configuration.php first: it defines the exact config tree (stash_api.base_url, stash_api.username, stash_api.password/token, or similar). That file is your source of truth for what's configurable.$projects = $client->get('/rest/api/1.0/projects'); // endpoint wrapper or raw GET, depending on the client
If that returns JSON, you're wired up. From there, the most common real task is listing repositories under a project or reading pull-request metadata.Where to look first, in order: README.md (installation snippets), DependencyInjection/Configuration.php (config schema), DependencyInjection/StashApiExtension.php (how services are built), and the Service/ or Client/ directory (the public API surface you'll actually inject).
Inject the bundle's client service. Symfony auto-wiring usually exposes a single client service (e.g. StashApiBundle\Service\StashClient or an interface). Constructor-inject it into your own service rather than reaching into the container:
final class RepositorySync
{
public function __construct(private StashClient $stash) {}
public function sync(string $projectKey): array
{
return $this->stash->get("/rest/api/1.0/projects/{$projectKey}/repos");
}
}
Wrap the client behind your own adapter. Because this is a thin, low-traffic package (zero dependents, no stars), treat its client as an implementation detail. Define your own StashGateway interface, implement it with the bundle's client, and mock the interface in tests. If the package stalls, you swap one adapter instead of hunting calls across the codebase.
Paginate explicitly. Stash/Bitbucket Server uses isLastPage, nextPageStart, and limit/start params rather than page numbers. Typical day-to-day flows — mirroring repo lists, exporting PRs, syncing branch permissions — will hit pagination immediately:
$start = 0;
do {
$page = $this->stash->get('/rest/api/1.0/projects', ['start' => $start, 'limit' => 100]);
$start = $page['nextPageStart'] ?? null;
} while (!$page['isLastPage']);
Use it from console commands and message handlers. The bundle is config-driven and stateless, so it fits naturally in a Symfony\Component\Console\Command\Command (nightly sync, permission audits) or a Messenger handler (reacting to webhook payloads by re-fetching canonical state from the API).
Webhook + API combo. Configure Stash webhooks to POST into your Symfony app; the bundle's client handles the follow-up read (e.g. "PR opened → fetch diff stats and reviewers"). Keep the webhook handler thin: persist the event, dispatch a message, let a worker call the API.
Auth pattern. Prefer personal access tokens over account passwords where the Stash version supports them; configure them as environment variables (STASH_TOKEN) referenced from the bundle config rather than committing secrets. Symfony's %env(...)% resolution flows through normal bundle config.
config/stash.php, and no facade. In a Laravel codebase you cannot composer require this and expect it to boot — you'd either run it beside a small Symfony service, or (more likely) copy the request/auth logic into a Laravel HTTP client wrapper. Evaluate it as a reference implementation, not a drop-in dependency.composer.json. Fork it if you need fixes; there's no community to lean on./rest/api/1.0 vs newer). Confirm the bundle targets your server version; the endpoints may be hard-coded as strings rather than modelled.{"errors":[{"message":...,"exceptionName":...}]}. If the bundle doesn't already throw on non-2xx, add a check in your adapter — a silently returned error array is a classic source of "empty sync" bugs.limit to 25; always pass an explicit limit and handle nextPageStart, or you'll quietly truncate results.Middleware::log) or enable Symfony's HTTP client/profiler data collector so you can see the exact request URL, headers, and response when a call misbehaves. Auth failures (401) and permission scopes (403) look identical in your code unless you log the response body.config/services.yaml (same ID, your class), or use a decorator around the client interface. Prefer decoration over editing vendor files; it survives composer update.How can I help you explore Laravel packages today?