kandysoft/media-kit

Storage-agnostic media library for Laravel: uploads, per-locale captions and on-demand image variants on top of the framework's filesystem disks.

Maintainers

Package info

github.com/TheKandySoft/media-kit

pkg:composer/kandysoft/media-kit

Transparency log

Statistics

Installs: 30

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-08-01 19:55 UTC

This package is auto-updated.

Last update: 2026-08-01 19:56:41 UTC


README

A media library for Laravel that stores files on your disks. Uploads, per-locale captions and on-demand image variants — with no storage abstraction of its own to get in the way.

composer require kandysoft/media-kit
php artisan migrate

Storage

Anything in config/filesystems.php works: local, S3, MinIO, Google Cloud, whatever the host has configured. Point the package at one:

MEDIA_KIT_DISK=public
MEDIA_KIT_DIRECTORY=media

A disk counts as public when it declares 'visibility' => 'public'. Public files get a permanent URL from the disk; private files get a temporary one, or a signed route that streams through the application when the driver cannot mint temporary URLs.

Writing

use KandySoft\MediaKit\Contracts\MediaWriter;
use KandySoft\MediaKit\Data\MediaCaption;
use KandySoft\MediaKit\Data\MediaUpload;

public function __construct(private readonly MediaWriter $media) {}

$this->media->sync($product, [
    MediaUpload::file($request->file('photo'), tag: 'gallery', captions: [
        new MediaCaption('en', alt: 'Front view'),
        new MediaCaption('uk', alt: 'Вигляд спереду'),
    ]),
    MediaUpload::keep($existingUuid, tag: 'gallery'),
]);

Anything not in the list is deleted, together with the variants generated from it. A file needs no owner at all — store() with $model = null keeps it standalone under its tag.

Replacing keeps the UUID, so links and stored references survive:

$this->media->replace($logo->uuid, $request->file('logo'));

Reading

use KandySoft\MediaKit\Contracts\MediaReader;
use KandySoft\MediaKit\Data\ImageSize;
use KandySoft\MediaKit\Data\MediaFilter;

$images = $reader->forModel(
    $product,
    MediaFilter::images(captionLocale: 'uk', withFallback: true),
    ImageSize::of(800, 600),
);

$images[0]->url;                       // the original
$images[0]->variant('medium')?->url;   // signed, generated on first request
$images[0]->caption?->alt;

Reads return MediaResource objects, never models or loose arrays. The disk, the path and the owning model stay inside the package.

Facade or injection

The facades resolve the very same singletons the container injects, so both styles are equivalent:

MediaRead::forModel($product);            // facade
app(MediaReader::class)->forModel($product);  // container

Identity

Files are addressed by UUID, everywhere. There is no auto-increment key — not in the schema, not in URLs, not in the payloads clients receive.

Configuration

php artisan vendor:publish --tag=media-kit-config for the full file. The interesting parts:

Key Purpose
disk, directory where files land
image.driver gd or imagick
image.variants named multipliers of the requested size
temporary_url_ttl, variant_url_ttl link lifetimes, in minutes
routes.enabled, routes.prefix, routes.middleware serving endpoints, or none at all

Commands

php artisan media:show <uuid>     # what a client would receive
php artisan media:adopt           # register files already on a disk

Documentation

License

MIT — see LICENSE and NOTICE.