Installation:
composer require pxlrbt/filament-favicon
Publish the config (if needed) with:
php artisan vendor:publish --provider="pxlrbt\FilamentFavicon\FilamentFaviconServiceProvider"
First Use Case:
Add a FaviconColumn to a Filament table to display favicons for domains stored in a model:
use pxlrbt\FilamentFavicon\Filament\FaviconColumn;
public function table(Table $table): Table
{
return $table
->columns([
FaviconColumn::make('domain')
->label('Website Favicon'),
// ... other columns
]);
}
Where to Look First:
config/filament-favicon.php for caching and request settings.src/Filament/FaviconColumn.php and src/Filament/FaviconEntry.php for customization hooks.Domain Extraction:
Use state() to transform URLs into domains:
FaviconColumn::make('url')
->state(fn ($record) => parse_url($record->url, PHP_URL_HOST))
->label('Favicon');
Caching Strategy:
Leverage the built-in caching (default: filament-favicon-cache) to avoid repeated HTTP requests:
// Override cache driver in config/filament-favicon.php
'cache' => [
'driver' => 'redis',
'key' => 'filament-favicon-cache',
],
Integration with Filament Resources:
Combine with FaviconEntry in infolists for a cohesive UI:
public function table(Table $table): Table
{
return $table->columns([
FaviconColumn::make('domain'),
]);
}
public function getTableInfolistItems(): array
{
return [
FaviconEntry::make('domain'),
// ... other entries
];
}
Dynamic State Handling: Use closures for dynamic domain resolution (e.g., from user-provided data):
FaviconColumn::make('website')
->state(fn ($record) => $record->getDomainFromComplexData())
->extraAttributes(['class' => 'w-8 h-8']);
Custom Fetch Logic:
Extend the FaviconFetcher service to handle non-standard domains (e.g., IP addresses):
// app/Providers/FilamentFaviconServiceProvider.php
public function register()
{
$this->app->bind(\pxlrbt\FilamentFavicon\Contracts\FaviconFetcher::class, function () {
return new class implements \pxlrbt\FilamentFavicon\Contracts\FaviconFetcher {
public function fetch(string $domain): ?string
{
// Custom logic for IP-based domains
return $domain === '127.0.0.1' ? 'data:image/svg+xml;base64,...' : null;
}
};
});
}
Conditional Rendering: Hide the column/entry based on record state:
FaviconColumn::make('domain')
->visible(fn ($record) => filled($record->domain))
->hiddenLabel();
Asset Optimization:
Pre-fetch favicons during model hydration (e.g., via app/Models/Concerns/FetchesFavicon.php):
public function getFaviconAttribute()
{
return cache()->remember(
"favicon-{$this->domain}",
now()->addHours(1),
fn () => FaviconFetcher::fetch($this->domain)
);
}
Domain Validation:
example.com). Invalid inputs (e.g., user@example.com) may break fetching.filter_var($domain, FILTER_VALIDATE_DOMAIN).Caching Quirks:
filament-favicon-. Clear the cache if favicons appear stale:
php artisan cache:clear
php artisan filament-favicon:clear-cache (if the package adds this command).HTTPS/HTTP Mixed Content:
// In config/filament-favicon.php
'fetch_options' => [
'ssl' => [
'verify_peer' => false, // Disable for self-signed certs (not recommended for production)
'verify_peer_name' => false,
],
],
Rate Limiting:
429 errors.use Illuminate\Support\Facades\Queue;
Queue::later(now()->addSeconds(2), fn () => FaviconFetcher::fetch($domain));
Log Fetch Failures:
Enable debug logging in config/filament-favicon.php:
'debug' => env('FILAMENT_FAVICON_DEBUG', false),
Check logs for failed requests (e.g., storage/logs/laravel.log).
Manual Testing:
Test favicon fetching manually via the FaviconFetcher facade:
use pxlrbt\FilamentFavicon\Facades\FaviconFetcher;
FaviconFetcher::fetch('google.com'); // Returns base64 string or null
Common HTTP Errors:
FaviconColumn::make('domain')
->default('data:image/svg+xml;base64,...') // Fallback icon
->extraAttributes(['onerror' => 'this.src="fallback-icon.png"']);
Custom Favicon Sources: Override the default favicon URL pattern (e.g., to support custom paths):
// In a service provider
$this->app->bind(\pxlrbt\FilamentFavicon\Contracts\FaviconUrlResolver::class, function () {
return new class implements \pxlrbt\FilamentFavicon\Contracts\FaviconUrlResolver {
public function resolve(string $domain): string
{
return "https://custom-cdn.com/favicons/{$domain}.ico";
}
};
});
React Components: Extend the Blade component for custom UI (e.g., add tooltips):
// resources/views/vendor/filament-favicon/components/favicon.blade.php
<img
src="{{ $src }}"
alt="{{ $label }}"
class="{{ $classes }}"
onerror="this.src='{{ asset('images/fallback.png') }}'"
title="{{ $domain }}"
>
Testing:
Mock the FaviconFetcher in tests:
$this->app->instance(\pxlrbt\FilamentFavicon\Contracts\FaviconFetcher::class, MockFetcher::new());
Batch Fetching: For large datasets, fetch favicons in batches using Laravel queues:
// Dispatch a job for each domain
FaviconFetchJob::dispatch($record->domain);
Lazy Loading: Defer favicon fetching until the column is rendered:
FaviconColumn::make('domain')
->lazyLoad()
->extraAttributes(['loading' => 'lazy']);
CDN Caching:
Serve favicons via a CDN with long cache headers (e.g., Cache-Control: public, max-age=31536000).
How can I help you explore Laravel packages today?