Asset Service
Directory: app/Services/Asset/
Handles file upload, image compression, resize, format conversion, and remote upload to ImageKit CDN.
Overview
When a user uploads a file (photo, document, video), the Asset service:
- Prepares the file — reads, validates, generates a unique name
- Processes the image — compresses, resizes, converts to WebP
- Uploads to ImageKit CDN for optimized delivery
- Stores the CDN URL in the
assetsdatabase table
The pipeline is: Upload → Compress → Resize → Convert → ImageKit → Database
Why It Exists
Without this service:
- Users could upload 20MB images directly, slowing page loads
- Every file upload would need duplicate resize/compress logic
- There would be no centralized CDN upload with URL tracking
- Image format would be inconsistent (some JPEG, some PNG, no WebP)
Methods
ImageKitService::prepare(UploadedFile|string $file, array $options): array
Entry point for all uploads. Reads the file, applies options, and returns a prepared payload.
public function prepare(UploadedFile|string $file, array $options = []): arrayParameters:
| Option | Type | Default | Description |
|---|---|---|---|
width | `int | null` | null |
height | `int | null` | null |
folder | `string | null` | null |
quality | int | 80 | JPEG/WebP quality (0-100) |
minimum_quality | int | 35 | Lowest acceptable quality |
compression_step | int | 5 | Quality reduction per iteration |
max_file_size_kb | `int | null` | null |
convert_to_webp | bool | false | Convert image to WebP format |
file_name | `string | null` | null |
use_unique_file_name | bool | false | Append UUID to filename |
is_private_file | bool | false | Mark as private in ImageKit |
tags | `array | null` | null |
Returns:
[
'contents' => string, // processed file binary
'file_name' => string, // final filename
'original_name' => string, // original uploaded name
'mime_type' => string, // MIME type
'extension' => string, // file extension
'size' => int, // final size in bytes
]ImageKitService::upload(string $contents, string $fileName, array $options = []): array
Uploads processed file to ImageKit CDN.
public function upload(string $contents, string $fileName, array $options = []): arrayReturns:
[
'url' => string, // CDN URL
'file_id' => string, // ImageKit file ID
'file_path' => string, // ImageKit path
'thumbnail_url' => string,// Thumbnail URL
'size' => int, // File size in bytes
'file_type' => string, // MIME type
'name' => string, // Original name
]CompressService::compress(string $contents, string $mediaType, int $quality = 80, ?int $maxBytes = null, int $minimumQuality = 35, int $step = 5): string
Compresses an image by reducing quality iteratively until the target file size is met.
Algorithm:
- If the file is not an image → return as-is
- Encode at initial quality (default: 80)
- If size ≤ maxBytes → return
- Reduce quality by
step(default: 5) and re-encode - Repeat until minimum quality is reached
- Return the best attempt
ResizeService::resize(string $contents, string $mediaType, ?int $width = null, ?int $height = null): string
Scales an image down to fit within given dimensions. Uses scaleDown() which never enlarges.
ConversionService::convertToWebp(string $contents, int $quality = 80): string
Converts any image format to WebP for browser optimization. WebP is ~25-35% smaller than JPEG at the same quality.
How to Use
Via the Asset Model (Recommended)
The Asset model has an uploadedFile property that automatically triggers the pipeline via its observer:
use App\Models\Platform\Asset\Asset;
$asset = new Asset;
$asset->uploadedFile = $request->file('photo');
$asset->uploadOptions = [
'folder' => 'schools/photos',
'use_unique_file_name' => true,
'convert_to_webp' => true,
'width' => 800,
];
$asset->save();
// $asset->path now contains the CDN URLVia the HasAsset Trait
For Eloquent models that need asset relationships (photo, logo, document, etc.), use the HasAsset trait. This dynamically creates BelongsTo relationships based on the model's $assets array.
Define assets on the model:
use App\Traits\Common\HasAsset;
class School extends Model
{
use HasAsset;
/**
* Define asset relationships.
* Key = relationship name, Value = foreign key column.
*/
protected array $assets = [
'logo_asset_id', // auto-named: logoAsset()
'banner' => 'banner_asset_id', // explicit: banner()
];
}Convention:
- Numeric key +
_idsuffix →Str::camel(str_replace('_id', '', $value))logo_asset_id→ relationshiplogoAsset()
- String key →
Str::camel($key)'banner' => 'banner_asset_id'→ relationshipbanner()
Usage in controller/logic:
// Assign an asset
$school->logoAsset()->associate($asset);
$school->save();
// Access the CDN URL
$school->logoAsset?->path; // https://ik.imagekit.io/...
// Access with fallback
$logoUrl = $school->logoAsset?->path ?? asset('images/default-logo.png');Via the Service Directly
use App\Services\Asset\ImageKitService;
$service = app(ImageKitService::class);
// 1. Prepare the file
$prepared = $service->prepare($uploadedFile, [
'width' => 400,
'height' => 300,
'convert_to_webp' => true,
'quality' => 85,
]);
// 2. Upload to ImageKit
$result = $service->upload(
$prepared['contents'],
$prepared['file_name'],
['folder' => 'uploads/images']
);
// $result['url'] is the CDN URLKey Files
| File | Purpose |
|---|---|
app/Services/Asset/ImageKitService.php | Main orchestrator — prepare, upload, option resolution |
app/Services/Asset/CompressService.php | Iterative image compression to meet size targets |
app/Services/Asset/ResizeService.php | Image resizing/scaling |
app/Services/Asset/ConversionService.php | WebP conversion |
app/Observers/Platform/AssetObserver.php | Auto-triggers upload pipeline on model save |
app/Models/Platform/Asset/Asset.php | Eloquent model with uploadedFile magic property |