shammaa / laravel-smart-glide
Advanced image processing toolkit for Laravel with unified /img routing, responsive components, and intelligent caching.
Installs: 28
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/shammaa/laravel-smart-glide
Requires
- php: ^8.3
- illuminate/cache: ^11.0|^12.0
- illuminate/filesystem: ^11.0|^12.0
- illuminate/http: ^11.0|^12.0
- illuminate/routing: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- illuminate/view: ^11.0|^12.0
- league/flysystem: ^3.0
- league/glide: ^3.0
Requires (Dev)
- illuminate/testing: ^11.0|^12.0
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^10.0
README
Advanced image processing toolkit for Laravel with unified /img routing, responsive components, and intelligent caching.
Features
- 🔐 Signed URLs & Validation — Protect image transformations with HMAC signatures, strict parameter validation, and extension whitelisting.
- 🖼️ Responsive Components — Blade components generate
srcset,sizes, and background media queries automatically. - ⚙️ Smart Profiles — Define reusable WebP/AVIF compression presets and override them from an external PHP profile file.
- 📦 Unified Delivery Path — All image responses flow through
/img/..., simplifying CDNs, caching rules, and observability. - 🧠 Intelligent Caching — LRU-inspired cache manifest with size budgets, warmup metadata, and background eviction.
- 🧾 SEO-Ready — Configurable attributes (
loading,fetchpriority,aria,title, etc.) plus optional JSON-LDImageObjectsnippets. - 🛡️ Security Rules — Block remote sources (optional), enforce max width/height/quality ranges, and stricter MIME filters.
- 🧰 DX Friendly — Publishable config, auto-discovered components, and clean service provider wiring.
Requirements
- PHP 8.3+
- Laravel 11.x or 12.x
- GD or Imagick PHP extension
- League Glide 3.x (installed automatically)
Installation
composer require shammaa/laravel-smart-glide
The service provider and components are auto-discovered. If discovery is disabled, register the provider manually in bootstrap/app.php (Laravel 11+):
return Application::configure(basePath: dirname(__DIR__)) ->withProviders([ Shammaa\SmartGlide\SmartGlideServiceProvider::class, ]) ->create();
Publish the configuration file when you want to customize paths, caching, or SEO defaults:
php artisan vendor:publish --tag=smart-glide-config
This creates config/smart-glide.php in your application.
Environment Setup
- Signing keys
APP_KEY=base64:... # Optional dedicated key SMART_GLIDE_SIGNATURE_KEY=base64:...
After modifying the.envfile, always clear cached configuration:php artisan config:clear php artisan cache:clear
- Filesystem
- Originals default to
storage/app/public. - Processed images live in
storage/app/smart-glide-cache. - Run
php artisan storage:linkonce if your front-end needs to serve the originals.
- Originals default to
- Permissions Make sure the cache directory is writable by your web server user.
Quick Start
- Prepare assets
Place an image insidestorage/app/public(default source path). If you need public access to the originals, create the symlink once viaphp artisan storage:link. - Drop the component
<x-smart-glide-img src="team/photo.jpg" alt="Team portrait" width="960" height="640" />
- Load the page
The rendered<img>tag now contains a fully signedsrc, responsivesrcset,sizes, and fixed width/height for layout stability. All requests resolve to/img/team/photo.jpg?.... - Need a different source path?
Editconfig/smart-glide.php('source' => ...) and clear configuration cache:php artisan config:clear php artisan cache:clear
Verify in Tinker
php artisan tinker >>> app(Shammaa\SmartGlide\Support\SmartGlideManager::class)->deliveryUrl('team/photo.jpg', ['w' => 640])
Opening the returned URL in a browser should yield the processed image (HTTP 200). A 403 usually indicates an invalid or missing signature—double‑check your APP_KEY/SMART_GLIDE_SIGNATURE_KEY and clear cached config.
Facade Usage
Resolve URLs anywhere in your Laravel app through the SmartGlide facade. This is convenient for tables, JSON APIs, or Livewire components where you need pre-signed image URLs without rendering Blade components.
use Shammaa\SmartGlide\Facades\SmartGlide; $url = SmartGlide::croppedUrl( path: 'team/photo.jpg', width: 160, height: 160, parameters: [ 'profile' => 'thumbnail', // merges preset params (quality, format, etc.) 'focus' => 'faces', // any additional Glide parameters ], );
In a DataTable JSON feed:
return TeamMember::query() ->select(['id', 'name', 'photo_path']) ->get() ->map(fn ($member) => [ 'id' => $member->id, 'name' => $member->name, 'photo' => SmartGlide::croppedUrl($member->photo_path, 96, 96, [ 'profile' => 'thumbnail', ]), ]);
Then render it in JavaScript:
$('#team-table').DataTable({ columns: [ { data: 'name' }, { data: 'photo', render: (data, type, row) => type === 'display' ? `<img src="${data}" alt="${row.name}" class="h-12 w-12 rounded-full object-cover" />` : data, orderable: false, searchable: false, }, ], });
Configuration Overview
The config file (config/smart-glide.php) exposes several groups:
| Key | Description |
|---|---|
source, cache, delivery_path |
Control filesystem paths and the unified /img endpoint. |
security |
Toggle URL signing, allowed formats, max dimensions, quality ranges, and remote sources. |
profiles & profile_file |
Define reusable transformation presets; merge an external PHP file at runtime. |
breakpoints |
Global widths for automatic srcset generation. |
responsive_sets |
Named breakpoint aliases usable from the components. |
cache_strategy |
Enforce cache size (MB) and LRU time window. |
cache_headers |
Configure browser cache days and enable ETag responses. |
logging |
Enable processing logs and choose the log channel. |
seo |
Default attributes for <img> / background components and structured data behaviour. |
Tip: set
SMART_GLIDE_SCHEMA_ENABLED=trueto emit JSON-LDImageObjectsnippets for every component by default.
Default Profiles & Responsive Sets
Profiles (config('smart-glide.profiles'))
| Name | Description |
|---|---|
default |
Outputs WebP at quality 82. |
thumbnail |
320×320 crop for avatars or cards. |
hero |
1600×900 centered crop for hero banners. |
portrait |
800×1200 crop prioritising faces. |
square |
600×600 crop for grids. |
profile_photo |
400×400 crop focused on faces (avatars). |
cover |
2048×1152 crop suitable for cover art. |
background |
2560×1440 max-fit background-friendly rendition. |
Use them via profile="hero" or override/add more in your config file.
Responsive sets (config('smart-glide.responsive_sets'))
| Name | Widths |
|---|---|
hero |
640, 960, 1280, 1600, 1920 |
thumbnails |
240, 320, 480 |
square |
320, 480, 640 |
portrait |
480, 768, 1024 |
hd |
960, 1280, 1600 |
fhd |
1280, 1600, 1920, 2560 |
retina |
640, 960, 1280, 1920, 2560 |
Reference them with responsive="retina" or specify an inline array.
Blade Components
<x-smart-glide-img>
Render responsive <img> tags with minimal boilerplate.
<x-smart-glide-img src="portfolio/hero.jpg" profile="hero" alt="Product hero image" class="rounded-xl shadow-lg" style="display:block" aspect-ratio="16:9" :params="['fit' => 'crop', 'focus' => 'center']" :seo="[ 'fetchpriority' => 'high', 'title' => 'Hero Image', 'data-license' => 'CC-BY-4.0', ]" schema />
Common properties
| Prop | Type | Description |
|---|---|---|
src |
string | Relative path (inside smart-glide.source). |
profile |
string | Apply a config profile (profiles.hero). |
params |
array | Additional Glide parameters (width, height, etc.). |
style |
string | Inline styles applied to <img>. |
aspect-ratio |
string | Aspect ratio hint (e.g. 16:9, 1.5). |
width / height |
int | Native dimension attributes for layout stability. |
seo |
array | Extra HTML attributes (fetchpriority, title, itemprop, …). |
schema |
bool | Force JSON-LD emission for this image only. |
Structured data respects component-level overrides and merges extra fields from config('smart-glide.seo.structured_data.fields').
View Overrides & Fallback Rendering
When you publish the package views, make sure resources/views/vendor/smart-glide/components/img.blade.php is kept in sync with the version shipped by the package. If it is removed or left empty, the component now falls back to rendering the <img> tag inline, so URLs remain signed even with an incomplete override. You can safely delete the override to restore the default template.
How srcset Is Generated
- Smart Glide reads breakpoint widths from
config('smart-glide.breakpoints')by default. - For each width, the component clones your parameters, overrides
w, and calls the delivery URL to produce entries like/img/photo.jpg?w=640&... 640w. - The generated HTML will look similar to:
<img src="/img/photo.jpg?w=960&fm=webp&q=82&s=..." srcset="/img/photo.jpg?w=360&... 360w, /img/photo.jpg?w=640&... 640w, ..." sizes="(max-width: 360px) 100vw, (max-width: 640px) 100vw, ..." >
- Update the breakpoints globally by editing
config('smart-glide.breakpoints'), or define named presets inresponsive_setsfor reuse (responsive="hero"). - Override widths without touching the config:
<x-smart-glide-img src="gallery/piece.jpg" :responsive="[320, 640, 960]" />
- Use a named preset from
config('smart-glide.responsive_sets'):<x-smart-glide-img src="hero.jpg" responsive="hero" />
- Disable
srcsetentirely when you want a single rendition:<x-smart-glide-img src="logo.png" :responsive="false" />
- If you need full control, set the attributes manually via
:seo="['sizes' => '...']"or['srcset' => '...'].
<x-smart-glide-bg>
Responsive background helper with media queries and placeholders.
<x-smart-glide-bg src="banners/summit.jpg" profile="hero" class="hero-banner" alt="Annual summit banner" lazy="true" :seo="['data-license' => 'Internal use only']" responsive="hero" />
The component outputs a wrapper <div> with inline styles plus an optional <style> block for breakpoint-specific backgrounds. A blurred placeholder overlay is included when lazy is true.
<x-smart-glide-picture>
Responsive <picture> helper that lets you define multiple <source> breakpoints and a Smart Glide powered fallback.
<x-smart-glide-picture src="gallery/feature.jpg" alt="Feature image" class="feature-picture" aspect-ratio="1:1" :sources="[ ['media' => '(min-width: 1200px)', 'widths' => [1200], 'params' => ['fit' => 'crop', 'w' => 1200, 'h' => 675]], ['media' => '(min-width: 768px)', 'widths' => [900], 'params' => ['w' => 900, 'h' => 506]], ]" />
Each source entry accepts:
| Key | Description |
|---|---|
media |
Optional media query for the <source> tag. |
src |
Override image path for this source (defaults to component src). |
params |
Glide parameters merged with the base parameters. |
profile |
Profile name to merge with parameters. |
widths / responsive |
Array or preset used to generate the srcset. |
sizes |
Custom sizes attribute. |
type |
MIME type hint for the source. |
srcset |
Custom array of descriptors if you need full control. |
img-class / img-style |
Styling for the fallback <img>. |
img-width / img-height |
Native width/height attributes on fallback <img>. |
width / height |
Set attributes on the wrapping <picture>. |
aspect-ratio |
Sets CSS aspect-ratio on fallback <img>. |
The internal <img> fallback inherits Smart Glide features (SEO attributes, structured data, signed URLs) and can be styled separately via img-class.
Picture Presets
Skip repeating :sources="[]" arrays in every template by defining presets in config('smart-glide.picture_presets'):
'picture_presets' => [ 'feature' => [ ['media' => '(min-width: 1200px)', 'widths' => [1200], 'params' => ['fit' => 'crop', 'w' => 1200, 'h' => 675]], ['media' => '(min-width: 768px)', 'widths' => [900], 'params' => ['fit' => 'crop', 'w' => 900, 'h' => 506]], ], ],
Use the preset in Blade:
<x-smart-glide-picture preset="feature" src="{{ $article->featured_image }}" alt="{{ $article->title }}" aspect-ratio="16:9" />
You can still pass :sources="[]" along with a preset; they will be appended for page-specific overrides.
Signing & Security
- URLs are signed with HMAC using
SMART_GLIDE_SIGNATURE_KEYorAPP_KEYwhensecurity.secure_urlsis enabled. - Parameters are sanitized and validated against maximum width/height and quality limits.
- Remote image sources are rejected by default; enable them per environment with
SMART_GLIDE_ALLOW_REMOTE=true. - Allowed output formats (
fm) and file extensions are configurable to avoid unsafe file types.
Requests with invalid signatures or disallowed parameters return 403/400 responses automatically.
Smart Cache
Smart Glide stores processed images in the configured cache path and tracks metadata in a manifest stored in your cache store.
max_size_mblimits total cache footprint.lru_windowdetermines how far back "least recently used" entries remain before eviction.- Metadata includes
cached_at,last_accessed, andcache_filereferences to support background cleanup.
Responses include browser cache headers (Cache-Control, Expires) and optional ETag. HTTP clients respecting If-None-Match will receive 304 Not Modified when appropriate.
External Profiles
To allow non-developers to tweak compression parameters, create a PHP file that returns an array:
<?php return [ 'hero' => ['w' => 1920, 'h' => 1080, 'fm' => 'webp', 'q' => 80], 'thumbnail' => ['w' => 320, 'h' => 320, 'fit' => 'crop', 'q' => 72], ];
Point SMART_GLIDE_PROFILE_FILE to this file. Its definitions merge on top of the inline profiles config.
CLI Helpers
Add these convenience commands to your application (optional suggestions):
// app/Console/Commands/ClearSmartGlideCache.php
Then run:
php artisan smart-glide:clear-cache
(Command scaffolding is not bundled yet; feel free to implement it following your workflow.)
Scheduled Cache Purge
The package now ships with a built-in Artisan command and scheduler hook:
- Configure the daily purge time via
SMART_GLIDE_CACHE_PURGE_TIME(default03:00). - Smart Glide registers
smart-glide:clear-cacheautomatically; when the scheduler is enabled (php artisan schedule:run), cached renditions are cleared daily at the configured time. - Run manually with
php artisan smart-glide:clear-cache --force.
Testing
composer test # PHPUnit (requires Laravel testing harness) composer test-coverage # Coverage (if configured) composer analyse # Static analysis (PHPStan/Psalm if added)
Because this is a Laravel package, use Orchestra Testbench for isolated package tests.
Roadmap Ideas
- Remote URL whitelisting & SSRF hardening helpers.
- Middleware abstractions for header customization.
- First-class Artisan commands (cache clear, warmup, manifest stats).
- Vue/React components for hybrid stacks.
Contributions and issues are welcome once the package is public.
Troubleshooting
| Symptom | Likely Cause | Resolution |
|---|---|---|
403 Smart Glide signature missing/invalid |
APP_KEY or SMART_GLIDE_SIGNATURE_KEY changed, or URL copied without the signed query string. |
Restore the original key, run php artisan config:clear & php artisan cache:clear, and always use URLs generated by Smart Glide. |
Image returns 404 |
Asset not found inside config('smart-glide.source'). |
Move the file into the source directory (default storage/app/public) or update the config accordingly. |
| Links must be absolute | APP_URL missing or incorrect. |
Set APP_URL in .env; deliveryUrl() honours it when producing URLs. |
| Cached variant stale | Old rendition remains in storage/app/smart-glide-cache. |
Delete the cached file or add a lightweight Artisan command to purge Smart Glide cache entries. |
License
Released under the MIT License.