Skip to content

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:

  1. Prepares the file — reads, validates, generates a unique name
  2. Processes the image — compresses, resizes, converts to WebP
  3. Uploads to ImageKit CDN for optimized delivery
  4. Stores the CDN URL in the assets database 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.

php
public function prepare(UploadedFile|string $file, array $options = []): array

Parameters:

OptionTypeDefaultDescription
width`intnull`null
height`intnull`null
folder`stringnull`null
qualityint80JPEG/WebP quality (0-100)
minimum_qualityint35Lowest acceptable quality
compression_stepint5Quality reduction per iteration
max_file_size_kb`intnull`null
convert_to_webpboolfalseConvert image to WebP format
file_name`stringnull`null
use_unique_file_nameboolfalseAppend UUID to filename
is_private_fileboolfalseMark as private in ImageKit
tags`arraynull`null

Returns:

php
[
    '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.

php
public function upload(string $contents, string $fileName, array $options = []): array

Returns:

php
[
    '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:

  1. If the file is not an image → return as-is
  2. Encode at initial quality (default: 80)
  3. If size ≤ maxBytes → return
  4. Reduce quality by step (default: 5) and re-encode
  5. Repeat until minimum quality is reached
  6. 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

The Asset model has an uploadedFile property that automatically triggers the pipeline via its observer:

php
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 URL

Via 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:

php
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 + _id suffix → Str::camel(str_replace('_id', '', $value))
    • logo_asset_id → relationship logoAsset()
  • String key → Str::camel($key)
    • 'banner' => 'banner_asset_id' → relationship banner()

Usage in controller/logic:

php
// 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

php
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 URL

Key Files

FilePurpose
app/Services/Asset/ImageKitService.phpMain orchestrator — prepare, upload, option resolution
app/Services/Asset/CompressService.phpIterative image compression to meet size targets
app/Services/Asset/ResizeService.phpImage resizing/scaling
app/Services/Asset/ConversionService.phpWebP conversion
app/Observers/Platform/AssetObserver.phpAuto-triggers upload pipeline on model save
app/Models/Platform/Asset/Asset.phpEloquent model with uploadedFile magic property