https://github.com/elegantengineeringtech/laravel-media
Extremely powerful media library for Laravel 🖼️
https://github.com/elegantengineeringtech/laravel-media
images laravel media php upload videos
Last synced: 7 months ago
JSON representation
Extremely powerful media library for Laravel 🖼️
- Host: GitHub
- URL: https://github.com/elegantengineeringtech/laravel-media
- Owner: ElegantEngineeringTech
- License: mit
- Created: 2023-09-24T14:53:19.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-11-23T14:06:14.000Z (over 1 year ago)
- Last Synced: 2024-11-24T19:52:57.990Z (over 1 year ago)
- Topics: images, laravel, media, php, upload, videos
- Language: PHP
- Homepage:
- Size: 1.15 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
# Extremely powerful media library for Laravel 🖼️
[](https://packagist.org/packages/elegantly/laravel-media)
[](https://github.com/ElegantEngineeringTech/laravel-media/actions?query=workflow%3Arun-tests+branch%3Amain)
[](https://github.com/ElegantEngineeringTech/laravel-media/actions?query=workflow%3Acoverage+branch%3Amain)
[](https://github.com/ElegantEngineeringTech/laravel-media/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
[](https://packagist.org/packages/elegantly/laravel-media)
This package offers an extremely flexible media library, enabling you to store any type of file along with their conversions.
It provides advanced features such as:
- 🌐 Supports any filesystem solutions (local or cloud), such as S3, R2, Bunny.net, DO...
- ⚡ Supports any file conversion solutions (local or cloud), such as ffmpeg, Transloadit, Cloudflare, Coconut, and others.
- 🔄 Advanced nested media conversions
- 🚀 Rich metadata automatically extracted
- 🛠️ Highly flexible and customizable
I developed this package with the highest degree of flexibility possible and I have been using it in production for nearly two years, handling terabytes of files monthly.
## Table of Contents
1. [Requirements](#requirements)
1. [Installation](#installation)
1. [Basic Usage](#basic-usage)
- [Define Media Collection](#defining-media-collections)
- [Define Media Conversions](#defining-media-conversions)
- [Adding Media](#adding-media)
- [Retreiving Media](#adding-media)
- [Media properties](#media-properties)
- [Accessing Media Conversions](#accessing-media-conversions)
- [Blade components](#blade-components)
1. [Advanced Usage](#advanced-usage)
- [Transforming a file before storing it](#transforming-a-file-before-storing-it)
- [Async vs Sync conversions](#async-vs-sync-conversions)
- [Delayed conversions](#delayed-conversions)
- [`onAdded` MediaCollection Callback](#onadded-mediacollection-callback)
- [`onCompleted` MediaConversionDefinition Callback](#oncompleted-mediaconversiondefinition-callback)
- [Regenerating Child Conversions When a Parent Runs](#regenerating-child-conversions-when-a-parent-runs)
- [Custom conversions](#custom-conversions)
- [Manually generate conversions](#manually-generate-conversions)
- [Format Media Url](#format-media-url)
1. [Conversions Presets](#conversions-presets)
- [Image Placeholder](#image-placeholder)
1. [Customization](#customization)
- [Custom Media Model](#custom-media-model)
1. [Troubleshooting](#troubleshooting)
- [Ghostscript and Imagick Issues](#ghostscript-and-imagick-issues)
## Requirements
- PHP 8.1+
- Laravel 11.0+
- `spatie/image` for image conversions
- `spatie/pdf-to-image` for PDF to image conversions
- `ffmpeg` for video/audio processing
## Installation
You can install the package via composer:
```bash
composer require elegantly/laravel-media
```
You have to publish and run the migrations with:
```bash
php artisan vendor:publish --tag="media-migrations"
php artisan migrate
```
You can publish the config file with:
```bash
php artisan vendor:publish --tag="media-config"
```
This is the contents of the published config file:
```php
use Elegantly\Media\Jobs\DeleteModelMediaJob;
use Elegantly\Media\Models\Media;
use Elegantly\Media\Models\MediaConversion;
use Elegantly\Media\PathGenerators\UuidPathGenerator;
use Elegantly\Media\UrlFormatters\DefaultUrlFormatter;
return [
/**
* The media model.
* Define your own model here by extending \Elegantly\Media\Models\Media::class.
*/
'model' => Media::class,
/**
* The MediaConversion model.
* Define your own model here by extending \Elegantly\Media\Models\MediaConversion::class.
*/
'media_conversion_model' => MediaConversion::class,
/**
* The path used to store temporary file copies for conversions.
* This will be used with the storage_path() function.
*/
'temporary_storage_path' => 'app/tmp/media',
/**
* The default disk used for storing files.
*/
'disk' => env('MEDIA_DISK', env('FILESYSTEM_DISK', 'local')),
/**
* Determine if media should be deleted with the model
* when using the HasMedia Trait.
*/
'delete_media_with_model' => true,
/**
* Determine if media should be deleted with the model
* when it is soft deleted.
*/
'delete_media_with_trashed_model' => false,
/**
* Job class responsible for deleting media when the model is deleted.
* This helps with performance and monitoring by queuing media deletions.
*/
'delete_media_with_model_job' => DeleteModelMediaJob::class,
/**
* The default collection name assigned media.
*/
'default_collection_name' => 'default',
/**
* The default URL formatter class.
* Used when calling `$media->getUrl()`.
*/
'default_url_formatter' => DefaultUrlFormatter::class,
/**
* The default path generator class.
* Used when storing new files.
*/
'default_path_generator' => UuidPathGenerator::class,
/**
* Prefix for the generated file path.
* Set to null to disable the prefix.
* Override the generateBasePath method in the Media model for full customization.
*/
'generated_path_prefix' => null,
/**
* Queue connection name to use when dispatching media conversion jobs.
*/
'queue_connection' => env('QUEUE_CONNECTION', 'sync'),
/**
* Queue name to use for media conversion jobs.
* Set to null to use the default Laravel queue.
*/
'queue' => null,
/**
* Configuration for FFmpeg processing.
*/
'ffmpeg' => [
/**
* The binary path to the FFmpeg executable.
*/
'ffmpeg_binaries' => env('FFMPEG_BINARIES', 'ffmpeg'),
/**
* The binary path to the FFprobe executable.
*/
'ffprobe_binaries' => env('FFPROBE_BINARIES', 'ffprobe'),
/**
* Optional log channel for FFmpeg operations.
* Set to null to disable logging.
*/
'log_channel' => null,
],
];
```
Optionally, you can publish the views using
```bash
php artisan vendor:publish --tag="media-views"
```
## Basic Usage
### Defining Media Collections
Media Collections define how media are stored, transformed, and processed for a specific model. They provide granular control over file handling, accepted types, and transformations.
To associate a media collection with a Model, start by adding the `InteractWithMedia` interface and the `HasMedia` trait.
Next, define your collections in the `registerMediaCollections` method, as shown below:
```php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Elegantly\Media\Concerns\HasMedia;
use Elegantly\Media\Contracts\InteractWithMedia;
use Elegantly\Media\MediaCollection;
class Channel extends Model implements InteractWithMedia
{
use HasMedia;
public function registerMediaCollections(): array;
{
return [
new MediaCollection(
name: 'avatar',
single: true, // If true, only the latest file will be kept
disk: 's3', // (optional) Specify where the file will be stored
acceptedMimeTypes: [ // (optional) Specify accepted file types
'image/jpeg',
'image/png',
'image/webp'
]
)
];
}
}
```
### Defining Media Conversions
Media conversions create different variants of your media files. For example, a 720p version of a 1440p video or a WebP or PNG version of an image are common types of media conversions. Interestingly, a media conversion can also have its own additional conversions.
This package provides common converter to simplify your work:
- `MediaImageConverter`: This converter optimizes, resizes, or converts any image using `spatie/image`.
- `MediaMp4Converter`: This conversion optimizes, resizes, or converts any video or gif to mp4 video using `ffmpeg`.
- `MediaWebmConverter`: This conversion optimizes, resizes, or converts any video or gif to webm video using `ffmpeg`.
- `MediaWavConverter`: This conversion optimizes, resizes, converts or extract any audio in wav format using `ffmpeg`.
- `MediaMp3Converter`: This conversion optimizes, resizes, converts or extract any audio in mp3 format using `ffmpeg`.
- `MediaFrameConverter`: This conversion extracts a frame from a video using `ffmpeg`.
- `MediaPdfToImageConverter`: This conversion extracts an image from the PDF using `spatie/pdf-to-image`.
```php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Elegantly\Media\Concerns\HasMedia;
use Elegantly\Media\Contracts\InteractWithMedia;
use Elegantly\Media\MediaCollection;
use Elegantly\Media\MediaConversionDefinition;
use Elegantly\Media\Converters\MediaMp4Converter;
use Elegantly\Media\Converters\MediaFrameConverter;
use Elegantly\Media\Converters\MediaImageConverter;
class Channel extends Model implements InteractWithMedia
{
use HasMedia;
public function registerMediaCollections(): array;
{
return [
new MediaCollection(
name: 'videos',
conversions: [
new MediaConversionDefinition(
name: 'poster',
converter: fn ($media) => new MediaFrameConverter(
media: $media,
filename: "{$media->name}-thumbnail.jpg",
timecode: 0,
),
conversions: [
new MediaConversionDefinition(
name: '360p',
converter: fn ($media) => new MediaImageConverter(
media: $media,
filename: "{$media->name}.jpg"
width: 360
)
),
],
),
new MediaConversionDefinition(
name: '720p',
converter: fn ($media) => new MediaMp4Converter(
media: $media,
filename: "{$media->name}.mp4"
width: 720
)
),
]
)
];
}
}
```
### Adding Media
Add media to your model, using the `addMedia` method, from various sources:
- an url
- a resource or stream
- a `\Illuminate\Http\UploadedFile` instance
- a `\Illuminate\Http\File` instance
#### From a Controller
```php
use Elegantly\Media\Exceptions\InvalidMimeTypeException;
public function store(Request $request, Channel $channel)
{
try {
$channel->addMedia(
file: $request->file('avatar'),
collectionName: 'avatar',
name: "{$channel->name}-avatar"
);
} catch (InvalidMimeTypeException $exception){
// Will throw an error if the mime type is not included in the collection's `acceptedMimeTypes` parameter.
}
}
```
#### From a Livewire Component
```php
use Livewire\WithFileUploads;
use Elegantly\Media\Exceptions\InvalidMimeTypeException;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
class ImageUploader extends Component
{
use WithFileUploads;
/** @var ?TemporaryUploadedFile */
public $avatar = null;
public function save()
{
try {
$this->channel->addMedia(
file: $this->avatar->getRealPath(),
collectionName: 'avatar',
name: "{$this->channel->name}-avatar"
);
} catch (InvalidMimeTypeException $exception){
// Will throw an error if the mime type is not included in the collection's `acceptedMimeTypes` parameter.
}
}
}
```
#### From an Url
```php
use Elegantly\Media\Exceptions\InvalidMimeTypeException;
try {
$channel->addMedia(
file: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
collectionName: 'videos',
name: "BigBuckBunny"
);
} catch (InvalidMimeTypeException $exception){
// Will throw an error if the mime type is not included in the collection's `acceptedMimeTypes` parameter.
}
```
### Retreiving Media
Retrieve media from your model:
```php
// Get all media from a specific collection
$avatars = $channel->getMedia('avatar');
// Get the first media from a collection
$avatar = $channel->getFirstMedia('avatar');
// Check if media exists
$hasAvatar = $channel->hasMedia('avatar');
```
### Media properties
Each media item provides rich metadata automatically:
```php
$media = $channel->getFirstMedia('avatar');
// File properties
$media->name; // file_name without the extension
$media->file_name;
$media->extension;
$media->mime_type;
$media->size; // in bytes
$media->humanReadableSize();
// Image/Video specific properties
$media->width; // in pixels
$media->height; // in pixels
$media->aspect_ratio;
$media->duration; // for video/audio
```
You can use dot notation to access either the root properties or a specific conversion:
```php
// Get the original media URL
$originalUrl = $media->getUrl();
// Get a specific conversion URL
$thumbnailUrl = $media->getUrl(
conversion: '360p',
fallback: true // Falls back to original if conversion doesn't exist
);
$posterUrl = $media->getUrl(
conversion: 'poster.360p',
fallback: 'poster' // Falls back to an other conversion if conversion doesn't exist
);
// Use the same logic with other properties such as
$media->getPath();
$media->getWidth();
// ...
```
### Access Media Conversions
To directly access conversions, use:
```php
// Check if a conversion exists
$hasThumbnail = $media->hasConversion('100p');
// Get a specific conversion
$thumbnailConversion = $media->getConversion('100p');
// Get the 'poster' conversion
$media->getParentConversion('poster.360p');
// Only get children conversions of poster
$media->getChildrenConversions('poster');
```
### Blade components
The package also provides blade components.
```html
```
```html
```
## Advanced Usage
### Transforming a file before storing it
You can define a transformation step that runs before a file is stored by using the `transform` method on a media collection.
This is useful if you want to **resize, optimize, or process an uploaded file** before it gets saved to disk.
For example, the snippet below shows how you can resize and optimize an uploaded image before storing it:
```php
use Illuminate\Http\File;
use Spatie\TemporaryDirectory\TemporaryDirectory;
use Spatie\Image\Image;
use Spatie\Image\Enums\Fit;
new MediaCollection(
name: 'avatar',
acceptedMimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
transform: function (File $file, TemporaryDirectory $temporaryDirectory): File {
$input = $file->getRealPath();
$output = $temporaryDirectory->path('avatar.jpg');
Image::load($input)
->fit(Fit::Contain, 500)
->optimize()
->save($output);
return new File($output);
}
);
```
### Async vs. Sync Conversions
When adding new media, its conversions can be either dispatched asynchronously or generated synchronously.
You can configure the strategy in the conversion definition using the `queued` and `queue` parameters:
```php
new MediaCollection(
name: 'avatar',
conversions: [
new MediaConversionDefinition(
name: '360p',
queued: true, // (default) Dispatch as a background job
queue: 'slow' // (optional) Specify a custom queue
converter: fn ($media) => new MediaImageConverter(
media: $media,
filename: "{$media->name}.jpg"
width: 360
)
),
new MediaConversionDefinition(
name: '180p',
queued: false, // Generate the conversion synchronously
converter: fn ($media) => new MediaImageConverter(
media: $media,
filename: "{$media->name}.jpg"
width: 180
)
),
]
)
```
Synchronous conversions can be particularly useful in specific use cases, such as generating a poster immediately upon upload.
### Delayed Conversions
There are scenarios where you might want to define conversions that should not be generated immediately. For instance, if a conversion is resource-intensive or not always required, you can defer its generation to a later time.
To achieve this, configure the conversion with the `immediate` parameter set to `false`. This allows you to generate the conversion manually when needed:
```php
new MediaCollection(
name: 'avatar',
conversions: [
new MediaConversionDefinition(
name: '360p',
immediate: false, // Conversion will not be generated at upload time
converter: fn ($media) => new MediaImageConverter(
media: $media,
filename: "{$media->name}.jpg"
width: 360
)
),
]
)
```
To generate the conversion later, you can use the following methods:
```php
// Generate the conversion synchronously
$media->executeConversion(
conversion: '360',
force: false // Skips execution if the conversion already exists
);
// Dispatch the conversion as a background job
$media->dispatchConversion(
conversion: '360',
force: false // Skips execution if the conversion already exists
);
```
### `onAdded` MediaCollection Callback
The `onAdded` callback allows you to define custom logic that will be executed whenever new media is added to your collection.
To use it, simply set the `onAdded` parameter when defining a `MediaCollection`. For example:
```php
new MediaCollection(
name: 'avatar',
onAdded: function ($media) {
// Example: Notify the model when new media is added
// $media->model->notify(new MediaAddedNotification($media));
}
);
```
With this, you can easily hook into the media addition process and trigger actions like sending notifications, logging, or other custom behavior.
> [!TIP]
> The same behavior can be achieved by listening to `Elegantly\Media\Events\MediaAddedEvent`.
### `onCompleted` MediaConversionDefinition Callback
The `onCompleted` callback allows you to define custom logic that will be executed whenever a new conversion is generated.
To use it, simply set the `onCompleted` parameter when defining a `MediaConversionDefinition`. For example:
```php
new MediaConversionDefinition(
name: '360',
onCompleted: function ($conversion, $media, $parent) {
// Example: Refresh your UI
// broadcast(new MyEvent($media));
}
);
```
This allows you to hook into the conversion process and execute additional logic, such as updating your UI or triggering other actions.
> [!TIP]
> The same behavior can be achieved by listening to `Elegantly\Media\Events\MediaConversionAddedEvent`.
### Regenerating Child Conversions When a Parent Runs
In some cases, you may want all **child conversions** of a given conversion to be **regenerated whenever the parent conversion is re-executed**.
There are two ways to achieve this, depending on where you handle conversions:
#### From the conversion definition
You can hook into the `onCompleted` callback of a conversion definition and instruct Laravel to delete all child conversions when the parent finishes.
```php
new MediaConversionDefinition(
name: '360',
onCompleted: function (?MediaConversion $conversion, Media $media, ?MediaConversion $parent) {
if ($conversion) {
// Delete any children so they’ll be regenerated automatically
$media->deleteChildrenConversions($conversion->conversion_name);
}
}
);
```
#### From `addConversion`
When adding a new conversion programmatically, you can explicitly tell it to remove children and regenerate them automatically:
```php
$mediaConversion = $media->addConversion(
file: $file,
deleteChildren: true, // ensures child conversions are refreshed
);
// regenerate children conversion
$this->media->generateConversions(
parent: $mediaConversion,
);
```
### Custom Conversions
Conversions can be anything: a variant of a file, a transcription of a video, a completely new file, or even just a string.
You can use built-in presets or define your own custom converter. To create a custom converter, start by creating a new class extending the `MediaConverter` class:
```php
namespace App\Media\Converters\Image;
use Elegantly\Media\Converters\MediaConverter;
use Elegantly\Media\Enums\MediaType;
use Elegantly\Media\Models\Media;
use Elegantly\Media\Models\MediaConversion;
use Illuminate\Contracts\Filesystem\Filesystem;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Image;
use Spatie\ImageOptimizer\OptimizerChain;
use Spatie\TemporaryDirectory\TemporaryDirectory as SpatieTemporaryDirectory;
class MediaImageConverter extends MediaConverter
{
public function __construct(
public readonly Media $media,
public string $filename,
public ?int $width = null,
public ?int $height = null,
public Fit $fit = Fit::Max,
public ?OptimizerChain $optimizerChain = null,
) {}
public function shouldExecute(Media $media, ?MediaConversion $parent): bool
{
$source = $parent ?? $media;
return $source->type === MediaType::Image;
}
public function convert(
Media $media,
?MediaConversion $parent,
?string $file,
Filesystem $filesystem,
SpatieTemporaryDirectory $temporaryDirectory
): ?MediaConversion {
if (! $file) {
return null;
}
$input = $filesystem->path($file);
$output = $filesystem->path($this->filename);
Image::load($input)
->fit($this->fit, $this->width, $this->height)
->optimize($this->optimizerChain)
->save($output);
return $media->addConversion(
file: $output,
conversionName: $this->conversion,
parent: $parent,
);
}
}
```
The `convert` method is where the logic for the conversion is implemented. It provides the following parameters:
- **`$media`**: The Media model.
- **`$parent`**: The MediaConversion model, if the conversion is nested.
- **`$file`**: A local copy of the file associated with either `$media` or `$parent`.
- **`$filesystem`**: An instance of the local filesystem where the file copy is stored.
- **`$temporaryDirectory`**: An instance of `TemporaryDirectory` where the file copy is temporarily stored.
You don’t need to worry about cleaning up the files, as the `$temporaryDirectory` will be deleted automatically when the process completes or fails.
To finalize the conversion, ensure you save it by calling `$media->addConversion` or `$media->replaceConversion` at the end of the `handle` method.
### Manually Generate Conversions
You can manage your media conversions programmatically using the following methods:
```php
// Store a new file as a conversion
$media->addConversion(
file: $file, // Can be an HTTP File, URL, or file path
conversionName: 'transcript',
parent: $mediaConversion // (Optional) Specify a parent conversion
// Additional parameters...
);
// Replace an existing conversion safely
// If the same conversion already exists, it ensures the new file is stored before deleting the previous one.
$media->replaceConversion(
conversion: $mediaConversion
);
// Safely delete a specific conversion and all its children
$media->deleteConversion('360');
// Safely delete only the child conversions of a parent conversion
$media->deleteChildrenConversions('poster');
// Dispatch or execute a conversion
$media->dispatchConversion('360'); // Runs asynchronously as a job
$media->executeConversion('poster.360'); // Executes synchronously
$media->getOrExecuteConversion('poster.360'); // Retrieves or generates the conversion
// Retrieve conversion information
$media->getConversion('360'); // Fetch a specific conversion
$media->hasConversion('360'); // Check if a conversion exists
$media->getParentConversion('poster.360'); // Retrieve the parent (poster) of a conversion
$media->getChildrenConversions('poster'); // Retrieve child conversions
```
Additionally, you can use an Artisan command to generate conversions with various options:
```bash
php artisan media:generate-conversions
```
You can also use the following command to retry failed conversions:
```bash
php artisan media-conversions:retry
```
This provides a convenient way to process conversions in bulk or automate them within your workflows.
### Format Media URLs
Some cloud providers like Cloudflare, Bunny, or ImageKit allow you to create instant transformations of your images and videos using specially formatted URLs.
This package gives you a simple way to format your URLs so you can take advantage of these services.
When using the `$media->getUrl()` method, you can specify two parameters:
- `parameters`: An array of values
- `formatter`: The class name of the formatter you want to use
By combining these parameters, you can retrieve formatted URLs like this:
```php
use \Elegantly\Media\UrlFormatters\CloudflareImageUrlFormatter;
// Default formatter (query parameters)
$default = $media->getUrl(
parameters: ['width' => 360],
); // https://your-url.com?width=360
// Cloudflare formatter (path-based format)
$cloudflare = $media->getUrl(
parameters: ['width' => 360],
formatter: CloudflareImageUrlFormatter::class
); // /cdn-cgi/media/width=360/https://your-url.com
```
This package comes with 3 formatters out of the box:
- `\Elegantly\Media\UrlFormatters\DefaultUrlFormatter`
- `\Elegantly\Media\UrlFormatters\CloudflareImageUrlFormatter`
- `\Elegantly\Media\UrlFormatters\CloudflareVideoUrlFormatter`
Feel free to implement your own formatter by extending `\Elegantly\Media\UrlFormatters\AbstractUrlFormatter`.
## Conversions Presets
### Image Placeholder
When loading images on the web, it’s common to show a low-resolution blurred placeholder first, then swap it with the full-resolution image once it’s ready. This improves perceived performance, reduces layout shifts, and gives a polished feel to your UI.
This package includes a ready-to-use preset for generating these placeholders in Base64-encoded format. The placeholder is tiny in size, quick to load, and designed to be displayed while the actual image is being fetched.
#### Defining the Placeholder Conversion
To create a placeholder, simply define a media conversion using the `MediaImagePlaceholderConverter`:
```php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Elegantly\Media\Concerns\HasMedia;
use Elegantly\Media\Contracts\InteractWithMedia;
use Elegantly\Media\MediaCollection;
use Elegantly\Media\MediaConversionDefinition;
use Elegantly\Media\Converters\MediaImagePlaceholderConverter;
class User extends Model implements InteractWithMedia
{
use HasMedia;
public function registerMediaCollections(): array;
{
return [
new MediaCollection(
name: 'avatar',
single: true,
conversions: [
new MediaConversionDefinition(
name: 'placeholder',
converter: fn ($media) => new MediaImagePlaceholderConverter($media),
),
]
)
];
}
}
```
#### Using the Placeholder in Your Views
If you’re using the built-in Blade component, you can directly specify the placeholder conversion name:
```html
```
This will automatically load the blurred placeholder as a background-image.
#### Retrieving the Base64 Value Directly
If you need to manually work with the placeholder (e.g., for API responses or inline styles), you can retrieve it like this:
```php
$placeholder = $media->getConversion('placeholder');
$base64Image = $placeholder?->contents;
```
## Customization
### Custom Media Model
You can define your own Media model to use with the library.
First, create your own model class:
```php
namespace App\Models;
use Elegantly\Media\Models\Media as ElegantlyMedia;
class Media extends ElegantlyMedia
{
// ...
}
```
Then, update the `config` file:
```php
use App\Models\Media;
return [
'model' => Media::class,
// ...
];
```
The library is typed with generics, so you can use your own Media model seamlessly:
```php
namespace App\Models;
use App\Models\Media;
use Elegantly\Media\Concerns\HasMedia;
use Elegantly\Media\Contracts\InteractWithMedia;
/**
* @implements InteractWithMedia
*/
class Post extends Model implements InteractWithMedia
{
/** @use HasMedia **/
use HasMedia;
// ...
}
```
## Troubleshooting
### Ghostscript and Imagick Issues
This package relies on the `spatie/pdf-to-image` library, which uses Ghostscript via Imagick to convert PDFs into images.
If you encounter errors while generating images from PDFs, such as:
- `attempt to perform an operation not allowed by the security policy 'PDF'`
- `Uncaught ImagickException: FailedToExecuteCommand 'gs'`
these issues are likely related to the configuration of Ghostscript or Imagick on your system.
For detailed guidance on resolving these errors, refer to the [spatie/pdf-to-image documentation on Ghostscript issues](https://github.com/spatie/pdf-to-image/blob/main/README.md#issues-regarding-ghostscript).
## Testing
```bash
composer test
```
## Changelog
Please see the [CHANGELOG](CHANGELOG.md) for more information on recent changes.
## Contributing
Feel free to open an issue or a discussion.
## Security Vulnerabilities
Please contact [me](https://github.com/QuentinGab) to report security vulnerabilities.
## Credits
- [Quentin Gabriele](https://github.com/QuentinGab)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see the [License File](LICENSE.md) for more information.