Documentation
¶
Overview ¶
Package medialibrary associates files with GORM models using a schema compatible with Spatie Laravel Media Library.
Basic usage:
_ = medialibrary.Migrate(db)
lib := medialibrary.New(db, medialibrary.WithDisk(medialibrary.NewLocalDisk("public", "./storage", "/storage")))
lib.Register("seo_metas", func(r *medialibrary.Registrar) {
r.Collection("seo_image").SingleFile().WithResponsiveImages()
r.Conversion("seo_image_large").ForCollection("seo_image").Crop(1200, 630).WithResponsiveImages()
})
media, err := lib.AddMedia(ctx, model, "seo_image", medialibrary.FromPath("photo.jpg"))
Index ¶
- Variables
- func ConversionPath(m Media, conversion string) string
- func Migrate(db *gorm.DB) error
- func OriginalPath(m Media) string
- func ParseResponsiveWidth(fileName string) (int, bool)
- func PrimaryKeyID(model any) (uint64, error)
- func ResponsiveConversionKey(conversionName string) string
- func ResponsiveImageFileName(m Media, conversionName string, width, height int) string
- func ResponsiveImagePath(m Media, fileName string) string
- func ResponsiveImagesDir(m Media) string
- type AddOption
- type CollectionBuilder
- func (b *CollectionBuilder) AcceptsMimeTypes(mimes ...string) *CollectionBuilder
- func (b *CollectionBuilder) ConversionsDisk(name string) *CollectionBuilder
- func (b *CollectionBuilder) Disk(name string) *CollectionBuilder
- func (b *CollectionBuilder) SingleFile() *CollectionBuilder
- func (b *CollectionBuilder) WithResponsiveImages() *CollectionBuilder
- type CollectionConfig
- type ConversionBuilder
- func (b *ConversionBuilder) Crop(width, height int) *ConversionBuilder
- func (b *ConversionBuilder) Fit(width, height int) *ConversionBuilder
- func (b *ConversionBuilder) ForCollection(name string) *ConversionBuilder
- func (b *ConversionBuilder) Quality(q int) *ConversionBuilder
- func (b *ConversionBuilder) WithResponsiveImages() *ConversionBuilder
- type ConversionConfig
- type Disk
- type FileSource
- type HasMedia
- type Library
- func (l *Library) AddMedia(ctx context.Context, model HasMedia, collectionName string, source *FileSource, ...) (*Media, error)
- func (l *Library) ClearMediaCollection(ctx context.Context, model HasMedia, collectionName string) error
- func (l *Library) DB() *gorm.DB
- func (l *Library) Delete(ctx context.Context, media Media) error
- func (l *Library) Find(id uint64) (*Media, error)
- func (l *Library) GetFirstMedia(model HasMedia, collectionName string) (*Media, error)
- func (l *Library) GetFirstMediaURL(model HasMedia, collectionName string, conversionName ...string) (string, error)
- func (l *Library) GetMedia(model HasMedia, collectionName ...string) ([]Media, error)
- func (l *Library) GetResponsiveImageURLs(media Media, conversionName ...string) ([]string, error)
- func (l *Library) GetSrcset(media Media, conversionName ...string) (string, error)
- func (l *Library) GetURL(media Media, conversionName ...string) (string, error)
- func (l *Library) Register(modelType string, fn func(*Registrar))
- type LocalDisk
- func (d *LocalDisk) Delete(_ context.Context, path string) error
- func (d *LocalDisk) Exists(_ context.Context, path string) (bool, error)
- func (d *LocalDisk) Name() string
- func (d *LocalDisk) Open(_ context.Context, path string) (io.ReadCloser, error)
- func (d *LocalDisk) Put(_ context.Context, path string, r io.Reader) (int64, error)
- func (d *LocalDisk) Root() string
- func (d *LocalDisk) URL(path string) string
- type Media
- type Option
- type Registrar
- type ResponsiveImageSet
- type ResponsiveImagesData
- type S3API
- type S3Disk
- func (d *S3Disk) Delete(ctx context.Context, rel string) error
- func (d *S3Disk) Exists(ctx context.Context, rel string) (bool, error)
- func (d *S3Disk) Name() string
- func (d *S3Disk) Open(ctx context.Context, rel string) (io.ReadCloser, error)
- func (d *S3Disk) Put(ctx context.Context, rel string, r io.Reader) (int64, error)
- func (d *S3Disk) URL(rel string) string
- type S3DiskConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidArgument is returned for nil inputs or empty required fields. ErrInvalidArgument = errors.New("medialibrary: invalid argument") // ErrNotFound is returned when media cannot be located. ErrNotFound = errors.New("medialibrary: not found") // ErrDiskNotFound is returned when a configured disk name is missing. ErrDiskNotFound = errors.New("medialibrary: disk not found") // ErrMimeNotAllowed is returned when a file MIME type is rejected by the collection. ErrMimeNotAllowed = errors.New("medialibrary: mime type not allowed") // ErrNotImage is returned when an image conversion is requested for a non-image. ErrNotImage = errors.New("medialibrary: not an image") // ErrReplaceFailed is returned when singleFile cannot remove previous media // after a successful upload (the new media is rolled back when possible). ErrReplaceFailed = errors.New("medialibrary: failed to replace previous media") )
Functions ¶
func ConversionPath ¶
ConversionPath is the relative disk path for a named conversion.
func Migrate ¶
Migrate creates the Spatie-compatible media table when missing. Skip this when the database already uses Laravel Media Library migrations.
func OriginalPath ¶
OriginalPath is the relative disk path for the original file (Spatie layout).
func ParseResponsiveWidth ¶
ParseResponsiveWidth extracts the width descriptor from a Spatie responsive file name.
func PrimaryKeyID ¶
PrimaryKeyID extracts a numeric primary key from a struct value.
func ResponsiveConversionKey ¶
ResponsiveConversionKey returns the JSON key used for a conversion (empty → original).
func ResponsiveImageFileName ¶
ResponsiveImageFileName builds a Spatie-compatible responsive file name. Example: hero___media_library_original_640_360.jpg
func ResponsiveImagePath ¶
ResponsiveImagePath is the relative disk path for a responsive image file name.
func ResponsiveImagesDir ¶
ResponsiveImagesDir is the relative directory for responsive image files (Spatie layout).
Types ¶
type AddOption ¶
type AddOption func(*addOptions)
AddOption configures AddMedia.
func OnConversionsDisk ¶
OnConversionsDisk stores conversions/responsive files on the named disk.
type CollectionBuilder ¶
type CollectionBuilder struct {
// contains filtered or unexported fields
}
CollectionBuilder configures a collection.
func (*CollectionBuilder) AcceptsMimeTypes ¶
func (b *CollectionBuilder) AcceptsMimeTypes(mimes ...string) *CollectionBuilder
AcceptsMimeTypes whitelists MIME types for uploads into this collection.
func (*CollectionBuilder) ConversionsDisk ¶
func (b *CollectionBuilder) ConversionsDisk(name string) *CollectionBuilder
ConversionsDisk sets the disk for conversions and responsive images.
func (*CollectionBuilder) Disk ¶
func (b *CollectionBuilder) Disk(name string) *CollectionBuilder
Disk sets the storage disk for originals in this collection.
func (*CollectionBuilder) SingleFile ¶
func (b *CollectionBuilder) SingleFile() *CollectionBuilder
SingleFile keeps only the latest media item in the collection.
func (*CollectionBuilder) WithResponsiveImages ¶
func (b *CollectionBuilder) WithResponsiveImages() *CollectionBuilder
WithResponsiveImages generates width variants for the original file in this collection.
type CollectionConfig ¶
type CollectionConfig struct {
Name string
SingleFile bool
AcceptsMimeTypes []string
ResponsiveImages bool
Disk string // optional; empty → library default disk
ConversionsDisk string // optional; empty → same as Disk
}
CollectionConfig describes a named media collection on a model type.
type ConversionBuilder ¶
type ConversionBuilder struct {
// contains filtered or unexported fields
}
ConversionBuilder configures an image conversion.
func (*ConversionBuilder) Crop ¶
func (b *ConversionBuilder) Crop(width, height int) *ConversionBuilder
Crop resizes and center-crops to exact width x height.
func (*ConversionBuilder) Fit ¶
func (b *ConversionBuilder) Fit(width, height int) *ConversionBuilder
Fit resizes to fit inside width x height preserving aspect ratio.
func (*ConversionBuilder) ForCollection ¶
func (b *ConversionBuilder) ForCollection(name string) *ConversionBuilder
ForCollection scopes the conversion to media in collection.
func (*ConversionBuilder) Quality ¶
func (b *ConversionBuilder) Quality(q int) *ConversionBuilder
Quality sets JPEG encode quality (1-100).
func (*ConversionBuilder) WithResponsiveImages ¶
func (b *ConversionBuilder) WithResponsiveImages() *ConversionBuilder
WithResponsiveImages generates width variants for this conversion.
type ConversionConfig ¶
type ConversionConfig struct {
Name string
Collection string
Width int
Height int
Crop bool
Quality int
ResponsiveImages bool
}
ConversionConfig describes an image conversion.
type Disk ¶
type Disk interface {
Name() string
Put(ctx context.Context, path string, r io.Reader) (int64, error)
Delete(ctx context.Context, path string) error
Exists(ctx context.Context, path string) (bool, error)
Open(ctx context.Context, path string) (io.ReadCloser, error)
// URL returns a publicly reachable URL for path (may be relative).
URL(path string) string
}
Disk stores and serves media bytes. Implement this for S3, GCS, etc.
type FileSource ¶
type FileSource struct {
// contains filtered or unexported fields
}
FileSource is a pending upload before it is attached to a model.
func FromPath ¶
func FromPath(path string) (*FileSource, error)
FromPath opens a local file as a FileSource.
func FromReader ¶
func FromReader(r io.Reader, fileName, mimeType string) (*FileSource, error)
FromReader wraps an in-memory or streamed upload.
func (*FileSource) WithName ¶
func (f *FileSource) WithName(name string) *FileSource
WithName overrides the media "name" column (defaults to file basename without extension).
type HasMedia ¶
HasMedia marks a model that can own media rows.
ModelType should be a stable morph alias (for example "seo_metas") when sharing a database with Laravel.
type Library ¶
type Library struct {
// contains filtered or unexported fields
}
Library is the entry point for media operations.
func (*Library) AddMedia ¶
func (l *Library) AddMedia(ctx context.Context, model HasMedia, collectionName string, source *FileSource, opts ...AddOption) (*Media, error)
AddMedia attaches a file to model under collectionName.
Disk I/O (original, conversions, responsive) runs outside DB transactions so row locks are not held across local/S3 uploads. On failure after any disk writes, uploaded files and the media row are removed (compensating cleanup).
For singleFile collections, existing rows are locked during insert so concurrent uploads serialize when the collection is non-empty. Previous items are removed only after the new media is stored successfully; if clearing them fails, the new media is removed as well. A post-success pass also drops older stragglers (lower ID) left by empty-collection races.
Disk selection (highest priority wins): AddOption → collection Disk → library default.
Example ¶
package main
import (
"context"
"fmt"
"image"
"image/color"
"image/jpeg"
"os"
"path/filepath"
medialibrary "github.com/noormaulida/gorm-medialibrary"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type examplePage struct {
ID uint64
}
func (p examplePage) MediaModelID() uint64 { return p.ID }
func (p examplePage) MediaModelType() string { return "pages" }
func main() {
db, _ := gorm.Open(sqlite.Open("file:example_media?mode=memory&cache=shared"), &gorm.Config{})
_ = db.AutoMigrate(&examplePage{})
_ = medialibrary.Migrate(db)
root, _ := os.MkdirTemp("", "medialib-ex-")
defer os.RemoveAll(root)
disk := medialibrary.NewLocalDisk("public", root, "/storage")
lib := medialibrary.New(db, medialibrary.WithDisk(disk))
lib.Register("pages", func(r *medialibrary.Registrar) {
r.Collection("cover").SingleFile()
r.Conversion("thumb").ForCollection("cover").Fit(40, 40)
})
page := examplePage{ID: 1}
_ = db.Create(&page)
imgPath := filepath.Join(root, "in.jpg")
f, _ := os.Create(imgPath)
img := image.NewRGBA(image.Rect(0, 0, 80, 60))
img.Set(0, 0, color.RGBA{R: 255, A: 255})
_ = jpeg.Encode(f, img, nil)
_ = f.Close()
src, _ := medialibrary.FromPath(imgPath)
media, _ := lib.AddMedia(context.Background(), page, "cover", src)
url, _ := lib.GetURL(*media, "thumb")
fmt.Println(media.CollectionName)
fmt.Println(url != "")
}
Output: cover true
func (*Library) ClearMediaCollection ¶
func (l *Library) ClearMediaCollection(ctx context.Context, model HasMedia, collectionName string) error
ClearMediaCollection deletes all media in a collection for model.
func (*Library) Delete ¶
Delete removes media files from disk, then deletes the database row. Disk errors are returned (not swallowed). If a disk delete fails, the DB row is left intact so the operation can be retried.
func (*Library) GetFirstMedia ¶
GetFirstMedia returns the first media item in a collection.
func (*Library) GetFirstMediaURL ¶
func (l *Library) GetFirstMediaURL(model HasMedia, collectionName string, conversionName ...string) (string, error)
GetFirstMediaURL is a convenience wrapper around GetFirstMedia + GetURL.
func (*Library) GetResponsiveImageURLs ¶
GetResponsiveImageURLs returns public URLs for responsive variants of the original or a named conversion (Spatie getResponsiveImageUrls).
All responsive files (including media_library_original) are stored on the conversions disk when set, matching AddMedia write paths.
func (*Library) GetSrcset ¶
GetSrcset builds an HTML srcset string for responsive images (Spatie getSrcset).
type LocalDisk ¶
type LocalDisk struct {
// contains filtered or unexported fields
}
LocalDisk stores files under Root and exposes them under URLPrefix.
func NewLocalDisk ¶
NewLocalDisk creates a filesystem disk.
NewLocalDisk("public", "./storage/app/public", "/storage")
type Media ¶
type Media struct {
ID uint64 `gorm:"primaryKey;autoIncrement"`
ModelType string `gorm:"size:255;not null;index:media_model_type_model_id_index;column:model_type"`
ModelID uint64 `gorm:"not null;index:media_model_type_model_id_index;column:model_id"`
UUID *string `gorm:"type:char(36);uniqueIndex"`
CollectionName string `gorm:"size:255;not null;column:collection_name"`
Name string `gorm:"size:255;not null"`
FileName string `gorm:"size:255;not null;column:file_name"`
MimeType *string `gorm:"size:255;column:mime_type"`
Disk string `gorm:"size:255;not null"`
ConversionsDisk *string `gorm:"size:255;column:conversions_disk"`
Size uint64 `gorm:"not null"`
Manipulations datatypes.JSON `gorm:"type:json;not null"`
CustomProperties datatypes.JSON `gorm:"type:json;not null;column:custom_properties"`
GeneratedConversions datatypes.JSON `gorm:"type:json;not null;column:generated_conversions"`
ResponsiveImages datatypes.JSON `gorm:"type:json;not null;column:responsive_images"`
OrderColumn *int `gorm:"column:order_column"`
CreatedAt *time.Time
UpdatedAt *time.Time
}
Media mirrors Spatie's media table.
func (Media) CustomPropertyMap ¶
CustomPropertyMap decodes custom_properties JSON.
func (Media) GeneratedConversionMap ¶
GeneratedConversionMap decodes generated_conversions JSON.
func (Media) ResponsiveImagesMap ¶
func (m Media) ResponsiveImagesMap() ResponsiveImagesData
ResponsiveImagesMap decodes responsive_images JSON (Spatie object shape).
type Option ¶
type Option func(*Library)
Option configures a Library.
func WithDefaultDisk ¶
WithDefaultDisk selects the disk name used when media.Disk is empty.
type Registrar ¶
type Registrar struct {
// contains filtered or unexported fields
}
Registrar collects collection and conversion definitions for a model type.
func (*Registrar) Collection ¶
func (r *Registrar) Collection(name string) *CollectionBuilder
Collection starts (or returns) a collection definition.
func (*Registrar) Conversion ¶
func (r *Registrar) Conversion(name string) *ConversionBuilder
Conversion starts a conversion definition.
type ResponsiveImageSet ¶
type ResponsiveImageSet struct {
URLs []string `json:"urls"`
Base64SVG string `json:"base64svg,omitempty"`
}
ResponsiveImageSet holds file names and optional tiny placeholder for one conversion key.
type ResponsiveImagesData ¶
type ResponsiveImagesData map[string]ResponsiveImageSet
ResponsiveImagesData is the Spatie-compatible responsive_images JSON shape.
type S3API ¶
type S3API interface {
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error)
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
}
S3API is the subset of the AWS S3 client used by S3Disk. *s3.Client from aws-sdk-go-v2 satisfies this interface.
type S3Disk ¶
type S3Disk struct {
// contains filtered or unexported fields
}
S3Disk stores media objects in Amazon S3 (or compatible APIs).
func NewS3Disk ¶
func NewS3Disk(cfg S3DiskConfig) (*S3Disk, error)
NewS3Disk creates an S3-backed Disk.
cfg, _ := config.LoadDefaultConfig(ctx)
client := s3.NewFromConfig(cfg)
disk, err := medialibrary.NewS3Disk(medialibrary.S3DiskConfig{
Name: "s3", Bucket: "my-bucket", BaseURL: "https://cdn.example.com", Client: client,
})
type S3DiskConfig ¶
type S3DiskConfig struct {
// Name is the disk name stored on media rows (e.g. "s3").
Name string
// Bucket is the S3 bucket name.
Bucket string
// Prefix is an optional key prefix (without leading/trailing slashes).
Prefix string
// BaseURL is the public URL prefix for URL(), e.g.
// "https://cdn.example.com" or "https://my-bucket.s3.amazonaws.com".
BaseURL string
// Client is an aws-sdk-go-v2 S3 client (or compatible mock).
Client S3API
}
S3DiskConfig configures a built-in S3 (or S3-compatible) disk.