symfony/asset
Symfony Asset Component handles generating URLs for web assets (CSS, JS, images) and managing versioning for cache busting. Works with different base paths/hosts and package setups to produce consistent, deploy-friendly asset links.
composer require symfony/asset
// app/Providers/AppServiceProvider.php
use Symfony\Component\Asset\Packages;
use Symfony\Component\Asset\PathPackage;
public function register()
{
$this->app->singleton(Packages::class, function () {
return new Packages([
new PathPackage(public_path(), true), // Enable fingerprinting
]);
});
}
@inject('asset', 'Symfony\Component\Asset\Packages')
<link href="{{ $asset->getUrl('css/app.css') }}" rel="stylesheet">
Output (with fingerprinting): /css/app.abc123.cssPathPackage (for static assets) and JsonManifestPackage (for Laravel Mix/Vite)Packages class (combines multiple strategies)StaticVersionStrategy (no versioning), FingerprintVersionStrategy (file hashing), JsonManifestVersionStrategy (Mix/Vite manifests)// In a controller or service
$assetPackage = app(Packages::class);
$url = $assetPackage->getUrl('css/app.css'); // /css/app.abc123.css
use Symfony\Component\Asset\JsonManifestPackage;
$manifestPackage = new JsonManifestPackage(
public_path('mix-manifest.json'),
'app',
true // Enable fingerprinting
);
$assetPackage = new Packages([$manifestPackage]);
$package = new PathPackage(public_path(), false, [
'base_urls' => ['https://cdn.example.com'],
]);
$assetPackage = new Packages([$package]);
// Output: https://cdn.example.com/css/app.css
$tenantId = 'tenant1';
$package = new PathPackage(public_path("tenants/{$tenantId}"), true);
$assetPackage = new Packages([$package]);
// Output: /tenants/tenant1/css/app.abc123.css
Blade Directives: Create a custom Blade directive for cleaner syntax:
// app/Providers/BladeServiceProvider.php
Blade::directive('asset', function ($expression) {
return "<?php echo app('Symfony\Component\Asset\Packages')->getUrl({$expression}); ?>";
});
Usage:
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
Middleware for Environment-Specific Behavior:
// app/Http/Middleware/AssetVersioning.php
public function handle($request, Closure $next)
{
if (app()->environment('production')) {
$this->app->singleton(Packages::class, fn() =>
new Packages([new PathPackage(public_path(), true)])
);
} else {
$this->app->singleton(Packages::class, fn() =>
new Packages([new PathPackage(public_path(), false)])
);
}
return $next($request);
}
Caching Packages: Cache the Packages instance in Laravel’s cache:
$this->app->singleton(Packages::class, function () {
return Cache::remember('asset.packages', now()->addHours(1), function () {
return new Packages([new PathPackage(public_path(), true)]);
});
});
Fingerprinting Overhead:
true in PathPackage triggers file hashing, which adds ~10-20ms per request during generation (but caches after first use).dev environments or use JsonManifestPackage for pre-hashed assets.Manifest File Missing:
JsonManifestPackage throws exceptions if mix-manifest.json is missing.non_strict: true in the constructor or handle the exception:
try {
$package = new JsonManifestPackage(public_path('mix-manifest.json'), 'app', true);
} catch (JsonException $e) {
$package = new PathPackage(public_path('js'), false);
}
Base URL Conflicts:
base_urls with absolute paths can cause double-encoding (e.g., https://cdn.example.com//css/app.css).base_urls are absolute and end with /:
'base_urls' => ['https://cdn.example.com/'],
Case Sensitivity:
app.abc123.css), which may break case-sensitive filesystems (e.g., Docker on Linux).JsonManifestPackage for consistency.Verify Fingerprinting:
$package = new PathPackage(public_path(), true);
$url = $package->getUrl('css/app.css');
var_dump($package->getVersionStrategy()->getVersion('css/app.css')); // Check hash
Inspect Packages:
$assetPackage = app(Packages::class);
var_dump($assetPackage->getPackages()); // List all registered packages
Disable Versioning Temporarily:
$package = new PathPackage(public_path(), false); // Disable fingerprinting
Custom Version Strategies:
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
class CustomVersionStrategy implements VersionStrategyInterface
{
public function getVersion(string $path): string
{
return 'v' . config('app.version');
}
}
Usage:
$package = new PathPackage(public_path(), false, [], new CustomVersionStrategy());
Dynamic Package Registration:
$this->app->singleton(Packages::class, function () {
$packages = [];
foreach (config('asset.packages') as $config) {
$packages[] = new PathPackage($config['path'], $config['fingerprint']);
}
return new Packages($packages);
});
Config:
'asset' => [
'packages' => [
['path' => public_path(), 'fingerprint' => true],
['path' => public_path('vendor'), 'fingerprint' => false],
],
],
Event-Driven Asset Updates: Listen for file changes and invalidate caches:
Storage::disk('public')->delete('css/app.css');
Cache::forget('asset.packages'); // Clear cached Packages instance
Asset Helper Conflict:
Laravel’s built-in asset() helper ignores the Symfony package. Solution: Override the helper in AppServiceProvider:
if (!function_exists('asset')) {
function asset($path)
{
return app('Symfony\Component\Asset\Packages')->getUrl($path);
}
}
Vite/Laravel Mix:
Ensure mix-manifest.json is generated and placed in public/. For Vite, use @vite('resources/css/app.css') in Blade and let the Symfony package handle the manifest.
Storage Links:
If using php artisan storage:link, ensure the public/storage symlink is included in your PathPackage:
$package = new PathPackage(public_path(), true, [], null, ['public/storage']);
How can I help you explore Laravel packages today?