ticketpark/file-bundle
Ticketpark FileBundle is a Symfony bundle for handling file uploads and attaching files to Doctrine entities. It manages file storage and lifecycle, helping applications add reusable file management without writing custom upload logic.
This is a Symfony bundle, not a Laravel package — there is no service provider, no facade, no config publish. Treat it as a small library you either (a) use inside a Symfony app, or (b) mine for its two useful pieces: a remote-file cache and a download response.
First three steps
composer.json before anything else. A 2016-12-24 release almost certainly pins symfony/http-foundation and symfony/filesystem to ~2.7|~3.0 and PHP >=5.5. On a modern Laravel app (Symfony 6/7 components, PHP 8.2) composer require ticketpark/file-bundle will fail dependency resolution. Expect to either fork/vendor the classes or reimplement.Resources/config/services.yml first — that is where the service IDs live (typically a cache service and a response/download helper). Then read the DependencyInjection/Configuration.php for the cache directory and TTL knobs.Minimal Symfony wiring:
# config/packages/ticketpark_file.yaml
ticketpark_file:
cache_dir: '%kernel.cache_dir%/file_cache'
# lifetime / ttl keys vary by version — check Configuration.php
// Symfony controller
public function download(FileCache $cache): Response
{
$path = $cache->get('https://partner.example/feed.csv', 3600);
return new FileDownloadResponse($path, 'feed.csv');
}
If you are staying in Laravel, the realistic "getting started" is: copy the two or three real classes out of src/ into app/Support/FileCache/, drop the bundle plumbing, and bind them in a service provider. You keep the behaviour, you lose the dead dependency constraints.
1. Cache-aside wrapper around remote fetches
The core value is path = f(url, ttl). Wrap it once and route every remote-fetch call site through it so TTL and cache location are centralised:
// app/Support/FileCache.php (reimplementation or vendored copy)
class FileCache
{
public function __construct(private string $dir, private Http $http) {}
public function get(string $url, int $ttl = 3600): string
{
$key = hash('sha256', $url);
$path = "{$this->dir}/{$key}";
if (is_file($path) && filemtime($path) + $ttl > time()) {
return $path;
}
$tmp = $path.'.'.bin2hex(random_bytes(6)).'.tmp';
$this->http->timeout(30)->get($url)->throw()->body(); // stream to $tmp
rename($tmp, $path); // atomic-ish swap
return $path;
}
}
2. Bind it as a singleton and inject it
In Laravel, register in a provider so the cache dir and TTL come from config/files.php, not from hardcoded strings scattered through controllers:
$this->app->singleton(FileCache::class, fn ($app) => new FileCache(
storage_path('app/file-cache'),
$app->make(\Illuminate\Http\Client\Factory::class),
));
3. Download endpoint pattern
Keep the download route dumb: resolve the cached path, then return a streamed response with an explicit filename and content type. Do not leak the on-disk path in headers.
Route::get('/feeds/{feed}', function (string $feed, FileCache $cache) {
$path = $cache->get(config("feeds.urls.$feed"), 3600);
return response()->streamDownload(fn () => readfile($path), "{$feed}.csv", [
'Content-Type' => 'text/csv',
]);
})->middleware('auth');
4. Warm the cache off the request path
Remote fetches have tail latency and can fail. Schedule a feed:warm command every N minutes so the web request almost always hits a fresh file, and treat a cache miss as the slow path rather than the normal one. This is also where you notice the remote endpoint went down before your users do.
5. Integration tip — use it as a seam, not a framework
If you keep the Symfony classes, isolate them behind a one-method interface (FileCache::get) and never let FileDownloadResponse leak into your controllers. That way replacing the vendored 2016 code with Laravel's Storage + Http later is a one-file change.
--ignore-platform-reqs; you'll end up with two incompatible symfony/http-foundation copies. Fork the repo, loosen composer.json, or vendor the classes.discover. Any "it's not registered" confusion is because there was never anything to register.rename() into place, and set the cache TTL from the file's mtime only after the rename.CachingFileCache implements FileCacheInterface) to add metrics, logging, or a fallback-to-S3 path. In Symfony that's a decorates: entry; in Laravel it's a extend/bind wrapper.How can I help you explore Laravel packages today?