@codingfactory/mediables-vue 2.0.1 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +22 -0
- package/README.md +923 -0
- package/dist/{PixiFrameExporter-Bb3QNWP-.js → PixiFrameExporter-B5tJ62bD.js} +2 -2
- package/dist/{PixiFrameExporter-Bb3QNWP-.js.map → PixiFrameExporter-B5tJ62bD.js.map} +1 -1
- package/dist/{PixiFrameExporter-BTU38EVl.cjs → PixiFrameExporter-BTXhmL54.cjs} +2 -2
- package/dist/{PixiFrameExporter-BTU38EVl.cjs.map → PixiFrameExporter-BTXhmL54.cjs.map} +1 -1
- package/dist/components/ImageEditorModal.vue.d.ts +2 -0
- package/dist/composables/useVideoFilters.d.ts +20 -0
- package/dist/{index-CcGWfCCV.js → index-BsOWbGbb.js} +879 -653
- package/dist/index-BsOWbGbb.js.map +1 -0
- package/dist/{index-VrUG0lmk.js → index-CWcyKIOz.js} +6851 -6650
- package/dist/index-CWcyKIOz.js.map +1 -0
- package/dist/index-C_X9_ptj.cjs +42 -0
- package/dist/{index-CcGWfCCV.js.map → index-C_X9_ptj.cjs.map} +1 -1
- package/dist/index-DVJg3EKN.cjs +76 -0
- package/dist/index-DVJg3EKN.cjs.map +1 -0
- package/dist/mediables-vanilla.cjs +1 -1
- package/dist/mediables-vanilla.mjs +1 -1
- package/dist/mediables-vue.cjs +1 -1
- package/dist/mediables-vue.mjs +2 -2
- package/dist/services/VideoJobClient.d.ts +7 -4
- package/dist/style.css +1 -1
- package/dist/types/api.d.ts +20 -18
- package/package.json +2 -2
- package/dist/index-6yUGA--H.cjs +0 -42
- package/dist/index-6yUGA--H.cjs.map +0 -1
- package/dist/index-DTUgsw7J.cjs +0 -76
- package/dist/index-DTUgsw7J.cjs.map +0 -1
- package/dist/index-VrUG0lmk.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,923 @@
|
|
|
1
|
+
# Laravel Mediables v2.0
|
|
2
|
+
|
|
3
|
+
A powerful, backend-focused Laravel media management package with a clean, trait-based API. Inspired by Spatie's Media Library but built for maximum flexibility and performance.
|
|
4
|
+
|
|
5
|
+
[](https://packagist.org/packages/mediables/mediables)
|
|
6
|
+
[](https://packagist.org/packages/mediables/mediables)
|
|
7
|
+
[](LICENSE.md)
|
|
8
|
+
|
|
9
|
+
> **Upgrading from v1.x?** Check out our comprehensive [Upgrade Guide](UPGRADE.md) for a smooth migration path.
|
|
10
|
+
|
|
11
|
+
## What's New in v2.0
|
|
12
|
+
|
|
13
|
+
### Service-Based API
|
|
14
|
+
|
|
15
|
+
```php
|
|
16
|
+
use Mediables\Facades\MediaService;
|
|
17
|
+
|
|
18
|
+
// Option 1: Two-step (store then attach)
|
|
19
|
+
$media = MediaService::storeMediaFile(
|
|
20
|
+
$request->file('image'),
|
|
21
|
+
'public', // disk
|
|
22
|
+
'products', // collection
|
|
23
|
+
['alt' => 'Photo'] // custom properties
|
|
24
|
+
);
|
|
25
|
+
$product->attachMedia($media, 'images');
|
|
26
|
+
|
|
27
|
+
// Option 2: Combined method (recommended)
|
|
28
|
+
$media = MediaService::uploadAndAttachToModel(
|
|
29
|
+
auth()->user(), // actor
|
|
30
|
+
$request->file('image'),
|
|
31
|
+
$product, // target model
|
|
32
|
+
'images', // collection
|
|
33
|
+
['alt' => 'Photo'] // properties
|
|
34
|
+
);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Key Features
|
|
38
|
+
|
|
39
|
+
- **Trait-Based Architecture** - Simple, clean integration with any Eloquent model
|
|
40
|
+
- **Service-Based Upload** - Flexible upload workflows via MediaService facade
|
|
41
|
+
- **Media Collections** - Organize media with powerful collection definitions
|
|
42
|
+
- **Smart Image Processing** - Automatic conversions, responsive images, and 46 PIXI.js filters
|
|
43
|
+
- **Cloud-First Design** - Native S3, CDN, and signed URL support
|
|
44
|
+
- **Performance Optimized** - Multi-layer caching and lazy loading
|
|
45
|
+
- **Backward Compatible** - Smooth migration path from v1.x
|
|
46
|
+
- **Modular Frontend** - Optional Vue components (Blade and Livewire coming soon)
|
|
47
|
+
|
|
48
|
+
## Table of Contents
|
|
49
|
+
|
|
50
|
+
- [Installation](#installation)
|
|
51
|
+
- [Quick Start](#quick-start)
|
|
52
|
+
- [Defining Media Collections](#defining-media-collections)
|
|
53
|
+
- [Working with Media](#working-with-media)
|
|
54
|
+
- [Image Processing](#image-processing)
|
|
55
|
+
- [Frontend Components](#frontend-components)
|
|
56
|
+
- [Configuration](#configuration)
|
|
57
|
+
- [Migration from v1.x](#migration-from-v1x)
|
|
58
|
+
- [API Reference](#api-reference)
|
|
59
|
+
- [Contributing](#contributing)
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
composer require mediables/mediables:^2.0
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Publish and run migrations:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
php artisan vendor:publish --tag=mediables-migrations
|
|
71
|
+
php artisan vendor:publish --tag=mediables-config
|
|
72
|
+
php artisan migrate
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Optional Frontend Packages
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Vue 3 components (available now)
|
|
79
|
+
npm install @mediables/vue
|
|
80
|
+
|
|
81
|
+
# Blade components (coming soon)
|
|
82
|
+
# composer require mediables/blade-components
|
|
83
|
+
|
|
84
|
+
# Livewire components (coming soon)
|
|
85
|
+
# composer require mediables/livewire-components
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
### 1. Add Traits to Your Model
|
|
91
|
+
|
|
92
|
+
```php
|
|
93
|
+
namespace App\Models;
|
|
94
|
+
|
|
95
|
+
use Illuminate\Database\Eloquent\Model;
|
|
96
|
+
use Mediables\Traits\InteractsWithMediables;
|
|
97
|
+
|
|
98
|
+
class Product extends Model
|
|
99
|
+
{
|
|
100
|
+
use InteractsWithMediables;
|
|
101
|
+
|
|
102
|
+
public function registerMediaCollections(): void
|
|
103
|
+
{
|
|
104
|
+
$this->addMediaCollection('images')
|
|
105
|
+
->acceptsMimeTypes(['image/*'])
|
|
106
|
+
->maxNumberOfFiles(10);
|
|
107
|
+
|
|
108
|
+
$this->addMediaCollection('documents')
|
|
109
|
+
->acceptsMimeTypes(['application/pdf'])
|
|
110
|
+
->singleFile();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 2. Upload and Attach Media
|
|
116
|
+
|
|
117
|
+
```php
|
|
118
|
+
use Mediables\Facades\MediaService;
|
|
119
|
+
|
|
120
|
+
// Option 1: Two-step (store then attach)
|
|
121
|
+
$media = MediaService::storeMediaFile(
|
|
122
|
+
$request->file('image'),
|
|
123
|
+
'public', // disk
|
|
124
|
+
'products', // collection
|
|
125
|
+
['alt' => 'Photo'] // custom properties
|
|
126
|
+
);
|
|
127
|
+
$product->attachMedia($media, 'images');
|
|
128
|
+
|
|
129
|
+
// Option 2: Combined method (recommended)
|
|
130
|
+
$media = MediaService::uploadAndAttachToModel(
|
|
131
|
+
auth()->user(), // actor
|
|
132
|
+
$request->file('image'),
|
|
133
|
+
$product, // target model
|
|
134
|
+
'images', // collection
|
|
135
|
+
['alt' => 'Photo'] // properties
|
|
136
|
+
);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3. Retrieve and Display Media
|
|
140
|
+
|
|
141
|
+
```php
|
|
142
|
+
// Get all media in collection
|
|
143
|
+
$images = $product->getMedia('images');
|
|
144
|
+
|
|
145
|
+
// Get first media
|
|
146
|
+
$mainImage = $product->getFirstMedia('images');
|
|
147
|
+
|
|
148
|
+
// Get URL (original)
|
|
149
|
+
$originalUrl = $mainImage->getUrl();
|
|
150
|
+
|
|
151
|
+
// Get conversion URL (use Media model method)
|
|
152
|
+
$thumbUrl = $mainImage->getUrl('thumb');
|
|
153
|
+
|
|
154
|
+
// Get first media URL directly (NO conversion param)
|
|
155
|
+
$firstUrl = $product->getFirstMediaUrl('images');
|
|
156
|
+
|
|
157
|
+
// Check if has media
|
|
158
|
+
if ($product->hasMedia('images')) {
|
|
159
|
+
// ...
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Count media
|
|
163
|
+
$count = $product->getMedia('images')->count();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Defining Media Collections
|
|
167
|
+
|
|
168
|
+
Define what media your models accept:
|
|
169
|
+
|
|
170
|
+
```php
|
|
171
|
+
public function registerMediaCollections(): void
|
|
172
|
+
{
|
|
173
|
+
// Single avatar image
|
|
174
|
+
$this->addMediaCollection('avatar')
|
|
175
|
+
->singleFile()
|
|
176
|
+
->acceptsMimeTypes(['image/jpeg', 'image/png']);
|
|
177
|
+
|
|
178
|
+
// Product gallery with limits
|
|
179
|
+
$this->addMediaCollection('gallery')
|
|
180
|
+
->acceptsMimeTypes(['image/*'])
|
|
181
|
+
->maxFileSize(5 * 1024 * 1024) // 5MB per file
|
|
182
|
+
->maxNumberOfFiles(20);
|
|
183
|
+
|
|
184
|
+
// Documents with specific types
|
|
185
|
+
$this->addMediaCollection('documents')
|
|
186
|
+
->acceptsMimeTypes(['application/pdf', 'application/msword'])
|
|
187
|
+
->maxNumberOfFiles(10);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Working with Media
|
|
192
|
+
|
|
193
|
+
### Upload Workflow (Service)
|
|
194
|
+
|
|
195
|
+
```php
|
|
196
|
+
use Mediables\Facades\MediaService;
|
|
197
|
+
|
|
198
|
+
// Option 1: Two-step (store then attach)
|
|
199
|
+
$media = MediaService::storeMediaFile(
|
|
200
|
+
$request->file('image'),
|
|
201
|
+
'public', // disk
|
|
202
|
+
'products', // collection
|
|
203
|
+
['alt' => 'Photo'] // custom properties
|
|
204
|
+
);
|
|
205
|
+
$product->attachMedia($media, 'images');
|
|
206
|
+
|
|
207
|
+
// Option 2: Combined method (recommended)
|
|
208
|
+
$media = MediaService::uploadAndAttachToModel(
|
|
209
|
+
auth()->user(), // actor
|
|
210
|
+
$request->file('image'),
|
|
211
|
+
$product, // target model
|
|
212
|
+
'images', // collection
|
|
213
|
+
['alt' => 'Photo'] // properties
|
|
214
|
+
);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Managing Collections
|
|
218
|
+
|
|
219
|
+
```php
|
|
220
|
+
// Clear all media from collection
|
|
221
|
+
$product->clearMediaCollection('images');
|
|
222
|
+
|
|
223
|
+
// Attach existing media
|
|
224
|
+
$product->attachMedia($existingMedia, 'images');
|
|
225
|
+
|
|
226
|
+
// Detach media
|
|
227
|
+
$product->detachMedia($media, 'images');
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Working with Conversions
|
|
231
|
+
|
|
232
|
+
```php
|
|
233
|
+
use Mediables\Facades\MediaService;
|
|
234
|
+
|
|
235
|
+
// Get conversion URL
|
|
236
|
+
$thumbnailUrl = $media->getUrl('thumb');
|
|
237
|
+
|
|
238
|
+
// Get responsive images srcset
|
|
239
|
+
$srcset = $media->getSrcset();
|
|
240
|
+
|
|
241
|
+
// Generate conversions manually
|
|
242
|
+
MediaService::generateConversions($media);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Configuration
|
|
246
|
+
|
|
247
|
+
Simplified configuration in `config/mediables.php`:
|
|
248
|
+
|
|
249
|
+
```php
|
|
250
|
+
return [
|
|
251
|
+
// Storage
|
|
252
|
+
'disk' => env('MEDIA_DISK', 'public'),
|
|
253
|
+
'conversions_disk' => env('MEDIA_CONVERSIONS_DISK', null),
|
|
254
|
+
|
|
255
|
+
// Processing
|
|
256
|
+
'queue_connection' => env('MEDIA_QUEUE_CONNECTION', 'sync'),
|
|
257
|
+
'conversions' => [
|
|
258
|
+
'thumb' => ['width' => 150, 'height' => 150],
|
|
259
|
+
'preview' => ['width' => 500, 'height' => 500],
|
|
260
|
+
],
|
|
261
|
+
|
|
262
|
+
// Video Provider (currently only 'bunny' is supported)
|
|
263
|
+
'video' => [
|
|
264
|
+
'provider' => env('VIDEO_PROVIDER', 'bunny'),
|
|
265
|
+
'bunny' => [/* ... */], // Bunny.net config
|
|
266
|
+
],
|
|
267
|
+
|
|
268
|
+
// Validation
|
|
269
|
+
'max_file_size' => 10 * 1024 * 1024, // 10MB
|
|
270
|
+
'allowed_mimes' => 'jpeg,jpg,png,gif,webp,svg,pdf',
|
|
271
|
+
|
|
272
|
+
// Features
|
|
273
|
+
'generate_responsive_images' => true,
|
|
274
|
+
'versioning' => false,
|
|
275
|
+
'cdn_url' => env('MEDIA_CDN_URL'),
|
|
276
|
+
];
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Environment Variables for Video Providers
|
|
280
|
+
|
|
281
|
+
To configure your video provider, set `VIDEO_PROVIDER` in your `.env` file. Currently only bunny is supported.
|
|
282
|
+
|
|
283
|
+
**Bunny.net Configuration:**
|
|
284
|
+
When `VIDEO_PROVIDER` is set to `bunny`, the following environment variables are required:
|
|
285
|
+
|
|
286
|
+
```env
|
|
287
|
+
BUNNY_VIDEO_LIBRARY_ID="your_bunny_video_library_id"
|
|
288
|
+
BUNNY_VIDEO_API_KEY="your_bunny_video_api_key"
|
|
289
|
+
BUNNY_VIDEO_HOSTNAME="video.bunnycdn.com" # Default is usually sufficient
|
|
290
|
+
BUNNY_VIDEO_CDN_HOSTNAME="your_library_id.b-cdn.net" # or play.bunnycdn.com
|
|
291
|
+
BUNNY_VIDEO_WEBHOOK_SECRET="your_webhook_secret" # Used for verifying webhooks
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
## Image Processing
|
|
296
|
+
|
|
297
|
+
### Automatic Conversions
|
|
298
|
+
|
|
299
|
+
Define conversions that run automatically:
|
|
300
|
+
|
|
301
|
+
```php
|
|
302
|
+
'conversions' => [
|
|
303
|
+
'thumb' => [
|
|
304
|
+
'width' => 150,
|
|
305
|
+
'height' => 150,
|
|
306
|
+
'fit' => 'crop',
|
|
307
|
+
'quality' => 80,
|
|
308
|
+
],
|
|
309
|
+
'preview' => [
|
|
310
|
+
'width' => 500,
|
|
311
|
+
'height' => 500,
|
|
312
|
+
'fit' => 'contain',
|
|
313
|
+
],
|
|
314
|
+
],
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### On-the-fly Manipulation
|
|
318
|
+
|
|
319
|
+
```php
|
|
320
|
+
use Mediables\Services\Core\ImageProcessor;
|
|
321
|
+
|
|
322
|
+
$processor = app(ImageProcessor::class);
|
|
323
|
+
|
|
324
|
+
// Generate specific conversion
|
|
325
|
+
$processor->generateConversion($media, 'thumb', [
|
|
326
|
+
'width' => 150,
|
|
327
|
+
'height' => 150,
|
|
328
|
+
'fit' => 'crop',
|
|
329
|
+
]);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Available Filters
|
|
333
|
+
|
|
334
|
+
The package includes 46 image filters:
|
|
335
|
+
|
|
336
|
+
```php
|
|
337
|
+
use Mediables\Services\MediaManipulationService;
|
|
338
|
+
|
|
339
|
+
$manipulationService = app(MediaManipulationService::class);
|
|
340
|
+
|
|
341
|
+
// Submit a manipulation request (validated data from ManipulateMediaRequest)
|
|
342
|
+
$manipulationService->manipulate([
|
|
343
|
+
'media_uuid' => $media->uuid,
|
|
344
|
+
'manipulations' => [
|
|
345
|
+
'filter' => 'ColorMatrix-1977',
|
|
346
|
+
'brightness' => 10,
|
|
347
|
+
'contrast' => 15,
|
|
348
|
+
],
|
|
349
|
+
]);
|
|
350
|
+
|
|
351
|
+
// Apply server-side filters to a media item
|
|
352
|
+
$processedUrl = $manipulationService->applyFilters($media, [
|
|
353
|
+
['name' => 'ColorMatrix-1977'],
|
|
354
|
+
]);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Responsive Images
|
|
358
|
+
|
|
359
|
+
Responsive image generation is controlled by the `generate_responsive_images` config option. When enabled, responsive variants are generated automatically during upload.
|
|
360
|
+
|
|
361
|
+
```php
|
|
362
|
+
// Enable in configuration
|
|
363
|
+
'responsive_images' => [
|
|
364
|
+
'enabled' => true,
|
|
365
|
+
'widths' => [320, 640, 1024, 1920, 2560],
|
|
366
|
+
],
|
|
367
|
+
|
|
368
|
+
// Get srcset for HTML
|
|
369
|
+
$srcset = $media->getSrcset('preview');
|
|
370
|
+
|
|
371
|
+
// Use in Blade templates
|
|
372
|
+
<img src="{{ $media->getUrl() }}"
|
|
373
|
+
srcset="{{ $media->getSrcset() }}"
|
|
374
|
+
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw">
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Working with Variants
|
|
378
|
+
|
|
379
|
+
Media variants allow you to create processed versions of your media:
|
|
380
|
+
|
|
381
|
+
```php
|
|
382
|
+
// Create a variant with specific processing
|
|
383
|
+
$variant = $originalMedia->createVariant([
|
|
384
|
+
'manipulation' => 'cropped',
|
|
385
|
+
'dimensions' => ['width' => 500, 'height' => 500],
|
|
386
|
+
'filters' => ['brightness' => 10, 'contrast' => 15]
|
|
387
|
+
]);
|
|
388
|
+
|
|
389
|
+
// Check processing status
|
|
390
|
+
if ($variant->isVariantCompleted()) {
|
|
391
|
+
$variantUrl = $variant->getUrl();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Get all variants for media
|
|
395
|
+
$variants = $originalMedia->variants;
|
|
396
|
+
|
|
397
|
+
// Delete specific variant
|
|
398
|
+
$variant->delete();
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Signed URLs for Private Files
|
|
402
|
+
|
|
403
|
+
```php
|
|
404
|
+
// Generate signed URL (expires in 60 minutes by default)
|
|
405
|
+
$signedUrl = $media->getSignedUrl();
|
|
406
|
+
|
|
407
|
+
// Custom expiration time
|
|
408
|
+
$signedUrl = $media->getSignedUrl(120); // 2 hours
|
|
409
|
+
|
|
410
|
+
// Check if URL is still valid
|
|
411
|
+
if ($media->isSignedUrlValid($signedUrl)) {
|
|
412
|
+
// URL is still valid
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Batch Operations
|
|
417
|
+
|
|
418
|
+
For bulk operations, iterate over your media collection using the standard service methods:
|
|
419
|
+
|
|
420
|
+
```php
|
|
421
|
+
use Mediables\Facades\MediaService;
|
|
422
|
+
|
|
423
|
+
// Bulk upload with loop
|
|
424
|
+
$uploadResults = [];
|
|
425
|
+
foreach ($files as $file) {
|
|
426
|
+
$uploadResults[] = MediaService::storeMediaFile($file, 'public', 'products');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Bulk delete with loop
|
|
430
|
+
foreach ($mediaItems as $media) {
|
|
431
|
+
MediaService::deleteMedia($media);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Generate conversions for multiple media
|
|
435
|
+
foreach ($mediaCollection as $media) {
|
|
436
|
+
MediaService::generateConversions($media);
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Event System
|
|
441
|
+
|
|
442
|
+
The package dispatches comprehensive events:
|
|
443
|
+
|
|
444
|
+
```php
|
|
445
|
+
// Listen to events
|
|
446
|
+
Event::listen(\Mediables\Events\MediaUploaded::class, function ($event) {
|
|
447
|
+
Log::info('Media uploaded: ' . $event->media->uuid);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Available events:
|
|
451
|
+
// - MediaUploaded
|
|
452
|
+
// - MediaManipulated
|
|
453
|
+
// - MediaDeleted
|
|
454
|
+
// - MediaAttached
|
|
455
|
+
// - MediaDetached
|
|
456
|
+
// - ConversionGenerated
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Frontend Components
|
|
460
|
+
|
|
461
|
+
Frontend components are now in separate, optional packages:
|
|
462
|
+
|
|
463
|
+
### Vue 3 Components
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
npm install @mediables/vue
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
```vue
|
|
470
|
+
<template>
|
|
471
|
+
<ModelMediaManager
|
|
472
|
+
:model-type="'product'"
|
|
473
|
+
:model-uuid="product.uuid"
|
|
474
|
+
collection="images"
|
|
475
|
+
:max-files="10"
|
|
476
|
+
@upload-complete="handleUpload"
|
|
477
|
+
/>
|
|
478
|
+
</template>
|
|
479
|
+
|
|
480
|
+
<script setup>
|
|
481
|
+
import { ModelMediaManager } from '@mediables/vue';
|
|
482
|
+
</script>
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Blade Components (Coming Soon)
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# composer require mediables/blade-components
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
```blade
|
|
492
|
+
<x-mediables-upload
|
|
493
|
+
:model="$product"
|
|
494
|
+
collection="images"
|
|
495
|
+
/>
|
|
496
|
+
|
|
497
|
+
<x-mediables-gallery
|
|
498
|
+
:model="$product"
|
|
499
|
+
collection="images"
|
|
500
|
+
/>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Livewire Components (Coming Soon)
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
# composer require mediables/livewire-components
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
```php
|
|
510
|
+
<livewire:mediables-manager
|
|
511
|
+
:model="$product"
|
|
512
|
+
collection="images"
|
|
513
|
+
/>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## API Reference
|
|
517
|
+
|
|
518
|
+
The complete REST API is documented using OpenAPI 3.0.3:
|
|
519
|
+
|
|
520
|
+
- **OpenAPI Spec**: [docs/openapi.yaml](docs/openapi.yaml)
|
|
521
|
+
- **Interactive Docs**: [View in Redoc](https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/mediables/mediables/main/docs/openapi.yaml)
|
|
522
|
+
- **Swagger UI**: Import `docs/openapi.yaml` into [Swagger Editor](https://editor.swagger.io/)
|
|
523
|
+
|
|
524
|
+
### Key API Groups
|
|
525
|
+
- **Media Management**: Upload, list, update, delete media
|
|
526
|
+
- **Image Editing**: Apply filters, crop, save variants
|
|
527
|
+
- **Video Processing**: Encode, transcode, apply effects
|
|
528
|
+
- **Albums**: Organize media into hierarchical albums (requires `enable_albums=true`)
|
|
529
|
+
- **Admin**: Administrative endpoints for media management
|
|
530
|
+
|
|
531
|
+
### Authentication
|
|
532
|
+
|
|
533
|
+
All API endpoints require authentication via Laravel Sanctum:
|
|
534
|
+
|
|
535
|
+
```javascript
|
|
536
|
+
// Set authorization header
|
|
537
|
+
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Media Upload
|
|
541
|
+
|
|
542
|
+
```http
|
|
543
|
+
POST /api/v1/media/upload
|
|
544
|
+
Content-Type: multipart/form-data
|
|
545
|
+
|
|
546
|
+
Parameters:
|
|
547
|
+
- file: File (required)
|
|
548
|
+
- collection: string (optional)
|
|
549
|
+
- custom_properties: object (optional)
|
|
550
|
+
- owner_uuid: string (required)
|
|
551
|
+
- owner_type: string (required)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Response:
|
|
555
|
+
```json
|
|
556
|
+
{
|
|
557
|
+
"data": {
|
|
558
|
+
"uuid": "550e8400-e29b-41d4-a716-446655440000",
|
|
559
|
+
"file_name": "image.jpg",
|
|
560
|
+
"mime_type": "image/jpeg",
|
|
561
|
+
"size": 1024000,
|
|
562
|
+
"collection_name": "gallery",
|
|
563
|
+
"original_url": "https://cdn.example.com/media/image.jpg",
|
|
564
|
+
"conversion_urls": {
|
|
565
|
+
"thumb": "https://cdn.example.com/media/conversions/image-thumb.jpg",
|
|
566
|
+
"preview": "https://cdn.example.com/media/conversions/image-preview.jpg"
|
|
567
|
+
},
|
|
568
|
+
"custom_properties": {},
|
|
569
|
+
"created_at": "2025-01-24T10:00:00Z"
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### List Media
|
|
575
|
+
|
|
576
|
+
```http
|
|
577
|
+
GET /api/v1/media?collection=gallery&search=product&page=1&per_page=20
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Apply Image Filters
|
|
581
|
+
|
|
582
|
+
```http
|
|
583
|
+
POST /api/v1/media/manipulate
|
|
584
|
+
Content-Type: application/json
|
|
585
|
+
|
|
586
|
+
{
|
|
587
|
+
"image_data": "data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
588
|
+
"original_media_uuid": "550e8400-e29b-41d4-a716-446655440000",
|
|
589
|
+
"crop_params": {
|
|
590
|
+
"x": 0,
|
|
591
|
+
"y": 0,
|
|
592
|
+
"width": 100,
|
|
593
|
+
"height": 100,
|
|
594
|
+
"scaleX": 1,
|
|
595
|
+
"scaleY": 1
|
|
596
|
+
},
|
|
597
|
+
"filter_params": [
|
|
598
|
+
{"name": "ColorMatrix-1977"}
|
|
599
|
+
],
|
|
600
|
+
"async": true
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Response (Sync, `201`):
|
|
605
|
+
```json
|
|
606
|
+
{
|
|
607
|
+
"data": {
|
|
608
|
+
"uuid": "770e8400-e29b-41d4-a716-446655440000",
|
|
609
|
+
"url": "https://cdn.example.com/media/manipulated.jpg"
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Response (Async, `202`):
|
|
615
|
+
```json
|
|
616
|
+
{
|
|
617
|
+
"id": "job-uuid-123",
|
|
618
|
+
"status": "queued",
|
|
619
|
+
"status_url": "https://api.example.com/api/v1/media/manipulations/job-uuid-123"
|
|
620
|
+
}
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Polling Status:**
|
|
624
|
+
```http
|
|
625
|
+
GET /api/v1/media/manipulations/{job_id}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
**Legacy Aliases (Deprecated):**
|
|
629
|
+
- `media_uuid` (alias for `original_media_uuid`)
|
|
630
|
+
- `manipulations` (stored as metadata only)
|
|
631
|
+
- Usage triggers `X-Mediables-Deprecated` header.
|
|
632
|
+
|
|
633
|
+
### Generate Conversions
|
|
634
|
+
|
|
635
|
+
```http
|
|
636
|
+
POST /api/v1/media/{media_uuid}/conversions
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
For complete API documentation, see [docs/api.md](docs/api.md).
|
|
640
|
+
|
|
641
|
+
## Image Processing
|
|
642
|
+
|
|
643
|
+
### Available Filters
|
|
644
|
+
|
|
645
|
+
#### Adjustment Filters
|
|
646
|
+
- **brightness** (-100 to 100) - Adjust image brightness
|
|
647
|
+
- **contrast** (-100 to 100) - Adjust contrast levels
|
|
648
|
+
- **saturation** (0 to 300) - Control color saturation
|
|
649
|
+
- **hue** (0 to 360) - Rotate hue values
|
|
650
|
+
- **grayscale** (boolean) - Convert to black and white
|
|
651
|
+
|
|
652
|
+
#### Blur Effects
|
|
653
|
+
- **KawaseBlur** - High-quality blur effect
|
|
654
|
+
- **ZoomBlur** - Radial motion blur
|
|
655
|
+
- **MotionBlur** - Directional motion blur
|
|
656
|
+
- **Bloom** - Soft glow effect
|
|
657
|
+
|
|
658
|
+
#### Vintage & Artistic
|
|
659
|
+
- **ColorMatrix-1977** - Instagram-style vintage filter
|
|
660
|
+
- **Sepia** - Classic sepia tone
|
|
661
|
+
- **OldFilm** - Vintage film with grain
|
|
662
|
+
- **Dot** - Halftone newspaper effect
|
|
663
|
+
- **CRT** - Retro monitor effect
|
|
664
|
+
|
|
665
|
+
#### Distortion Effects
|
|
666
|
+
- **Twist** - Spiral distortion
|
|
667
|
+
- **Shockwave** - Ripple effect
|
|
668
|
+
- **BulgePinch** - Lens distortion
|
|
669
|
+
|
|
670
|
+
### Usage Examples
|
|
671
|
+
|
|
672
|
+
```php
|
|
673
|
+
use Mediables\Services\Core\ImageProcessor;
|
|
674
|
+
|
|
675
|
+
$processor = app(ImageProcessor::class);
|
|
676
|
+
|
|
677
|
+
// Generate a conversion with custom config
|
|
678
|
+
$processor->generateConversion($media, 'custom', [
|
|
679
|
+
'width' => 500,
|
|
680
|
+
'height' => 500,
|
|
681
|
+
'fit' => 'contain',
|
|
682
|
+
'quality' => 80,
|
|
683
|
+
'manipulations' => [
|
|
684
|
+
'brightness' => ['level' => 20],
|
|
685
|
+
'contrast' => ['level' => 25],
|
|
686
|
+
],
|
|
687
|
+
]);
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
## Performance
|
|
691
|
+
|
|
692
|
+
### Caching Strategy
|
|
693
|
+
|
|
694
|
+
The package implements multi-layer caching:
|
|
695
|
+
|
|
696
|
+
```php
|
|
697
|
+
// URL caching (reduces database queries)
|
|
698
|
+
'enable_url_cache' => true,
|
|
699
|
+
'url_cache_duration' => 60, // minutes
|
|
700
|
+
|
|
701
|
+
// CDN integration
|
|
702
|
+
'cdn' => [
|
|
703
|
+
'default' => env('MEDIA_CDN_URL'),
|
|
704
|
+
'invalidate_on_change' => true,
|
|
705
|
+
],
|
|
706
|
+
|
|
707
|
+
// Lazy conversion generation
|
|
708
|
+
'lazy_conversions' => true,
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### Queue Configuration
|
|
712
|
+
|
|
713
|
+
Optimize performance with granular queue configuration:
|
|
714
|
+
|
|
715
|
+
```php
|
|
716
|
+
'queues' => [
|
|
717
|
+
'upload' => 'default', // File uploads
|
|
718
|
+
'conversions' => 'default', // Conversion generation
|
|
719
|
+
'conversions_high' => 'high', // Priority conversions
|
|
720
|
+
'conversions_low' => 'low', // Background conversions
|
|
721
|
+
'responsive' => 'default', // Responsive image generation
|
|
722
|
+
'delete' => 'low', // File deletion
|
|
723
|
+
],
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Database Optimization
|
|
727
|
+
|
|
728
|
+
- Uses UUIDs for efficient lookups
|
|
729
|
+
- Polymorphic relationships for flexibility
|
|
730
|
+
- Indexed foreign keys and commonly queried columns
|
|
731
|
+
- Optimized queries with eager loading
|
|
732
|
+
|
|
733
|
+
### Monitoring
|
|
734
|
+
|
|
735
|
+
```php
|
|
736
|
+
// Get performance metrics (debug mode only)
|
|
737
|
+
GET /api/v1/admin/media/performance
|
|
738
|
+
|
|
739
|
+
// Monitor conversion progress
|
|
740
|
+
GET /api/v1/media/{media}/conversions/progress
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
## Artisan Commands
|
|
744
|
+
|
|
745
|
+
```bash
|
|
746
|
+
# Generate missing conversions
|
|
747
|
+
php artisan mediables:generate-conversions
|
|
748
|
+
|
|
749
|
+
# Clean up orphaned media records
|
|
750
|
+
php artisan mediables:cleanup-orphans
|
|
751
|
+
|
|
752
|
+
# Verify media files exist in storage
|
|
753
|
+
php artisan mediables:verify-files
|
|
754
|
+
|
|
755
|
+
# Warm CDN cache
|
|
756
|
+
php artisan mediables:warm-cache
|
|
757
|
+
|
|
758
|
+
# Test media relationships
|
|
759
|
+
php artisan mediables:test-relationships
|
|
760
|
+
|
|
761
|
+
# Test S3 connectivity
|
|
762
|
+
php artisan mediables:test-storage
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### Scheduling Orphan Cleanup
|
|
766
|
+
|
|
767
|
+
Scheduling is the consuming application's responsibility. This command is designed to be safe for cron/schedulers by using chunked processing and an optional runtime bound (`--limit`).
|
|
768
|
+
|
|
769
|
+
```bash
|
|
770
|
+
php artisan mediables:cleanup-orphans --grace-period=7 --chunk=200 --limit=2000
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
## Testing
|
|
774
|
+
|
|
775
|
+
The package includes comprehensive testing support:
|
|
776
|
+
|
|
777
|
+
```php
|
|
778
|
+
use Illuminate\Http\UploadedFile;
|
|
779
|
+
use Illuminate\Support\Facades\Storage;
|
|
780
|
+
use Mediables\Facades\MediaService;
|
|
781
|
+
|
|
782
|
+
// Fake storage for testing
|
|
783
|
+
Storage::fake('public');
|
|
784
|
+
|
|
785
|
+
// Create fake upload
|
|
786
|
+
$file = UploadedFile::fake()->image('product.jpg', 1200, 800);
|
|
787
|
+
|
|
788
|
+
// Test media upload
|
|
789
|
+
$media = MediaService::storeMediaFile($file, 'public', 'products');
|
|
790
|
+
|
|
791
|
+
// Assert file exists
|
|
792
|
+
Storage::disk('public')->assertExists($media->full_path);
|
|
793
|
+
|
|
794
|
+
// Test media attachment
|
|
795
|
+
$product = Product::factory()->create();
|
|
796
|
+
$product->attachMedia($media, 'gallery');
|
|
797
|
+
|
|
798
|
+
$this->assertTrue($product->hasMedia('gallery'));
|
|
799
|
+
$this->assertEquals(1, $product->getMedia('gallery')->count());
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
## Migration from v1.x
|
|
803
|
+
|
|
804
|
+
### Quick Migration
|
|
805
|
+
|
|
806
|
+
Run the migration command:
|
|
807
|
+
|
|
808
|
+
```bash
|
|
809
|
+
php artisan mediables:migrate-v2
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
This will:
|
|
813
|
+
- Analyze your current setup
|
|
814
|
+
- Update trait usage automatically
|
|
815
|
+
- Migrate configuration
|
|
816
|
+
- Provide detailed migration report
|
|
817
|
+
|
|
818
|
+
### Manual Migration
|
|
819
|
+
|
|
820
|
+
1. **Update Traits:**
|
|
821
|
+
|
|
822
|
+
| Old (v1.x) | New (v2.0) |
|
|
823
|
+
|---|---|
|
|
824
|
+
| `Mediables\Traits\HasMediaCollections` | `Mediables\Traits\InteractsWithMediables` |
|
|
825
|
+
|
|
826
|
+
2. **Update Method Calls:**
|
|
827
|
+
|
|
828
|
+
| Old Method | New Method |
|
|
829
|
+
|------------|------------|
|
|
830
|
+
| `getMediaByCollection()` | `getMedia()` |
|
|
831
|
+
| `getFirstMediaByCollection()` | `getFirstMedia()` |
|
|
832
|
+
| `hasMediaCollection()` | `hasMedia()` |
|
|
833
|
+
| `detachMedia()` | `detachMedia()` |
|
|
834
|
+
| `syncMediaCollection()` | `clearMediaCollection()` + `attachMedia()` loop |
|
|
835
|
+
|
|
836
|
+
For detailed migration instructions, see [UPGRADE.md](UPGRADE.md).
|
|
837
|
+
|
|
838
|
+
## Migration from Spatie Media Library
|
|
839
|
+
|
|
840
|
+
Migrating from Spatie Media Library? We've got you covered:
|
|
841
|
+
|
|
842
|
+
```bash
|
|
843
|
+
# The package includes backward compatibility
|
|
844
|
+
# Your existing database schema works with minor adjustments
|
|
845
|
+
# See detailed migration guide: docs/migration-guide.md
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
Key differences:
|
|
849
|
+
- Uses UUIDs instead of auto-incrementing IDs
|
|
850
|
+
- Improved polymorphic relationship structure
|
|
851
|
+
- Enhanced querying capabilities
|
|
852
|
+
- Better performance optimizations
|
|
853
|
+
|
|
854
|
+
For a complete migration guide, see [docs/migration-guide.md](docs/migration-guide.md).
|
|
855
|
+
|
|
856
|
+
## Database Schema
|
|
857
|
+
|
|
858
|
+
### Core Tables
|
|
859
|
+
|
|
860
|
+
- **media** - Stores media file metadata and properties
|
|
861
|
+
- **mediables** - Polymorphic pivot table linking media to models
|
|
862
|
+
- **media_variants** - Tracks processing status of media variants
|
|
863
|
+
- **media_albums** - Optional album organization (if enabled)
|
|
864
|
+
- **media_audits** - Optional audit trail (if enabled)
|
|
865
|
+
|
|
866
|
+
### Key Fields
|
|
867
|
+
|
|
868
|
+
**Media Table:**
|
|
869
|
+
```sql
|
|
870
|
+
uuid (primary key)
|
|
871
|
+
file_name
|
|
872
|
+
original_file_name
|
|
873
|
+
mime_type
|
|
874
|
+
size
|
|
875
|
+
disk
|
|
876
|
+
collection_name
|
|
877
|
+
custom_properties (JSON)
|
|
878
|
+
created_at, updated_at
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
**Mediables Table:**
|
|
882
|
+
```sql
|
|
883
|
+
media_uuid (foreign key)
|
|
884
|
+
mediable_uuid
|
|
885
|
+
mediable_type
|
|
886
|
+
collection_name
|
|
887
|
+
order_column
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
## Requirements
|
|
891
|
+
|
|
892
|
+
- **PHP** 8.1 or higher
|
|
893
|
+
- **Laravel** 11.0 or higher
|
|
894
|
+
- **Intervention Image** 3.11 or higher
|
|
895
|
+
- **Vue** 3.4+ (for Vue components)
|
|
896
|
+
- **Ionic** 8.0+ (for mobile components)
|
|
897
|
+
|
|
898
|
+
## License
|
|
899
|
+
|
|
900
|
+
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
|
901
|
+
|
|
902
|
+
## Contributing
|
|
903
|
+
|
|
904
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
905
|
+
|
|
906
|
+
## Support & Documentation
|
|
907
|
+
|
|
908
|
+
- **Documentation**: [Full Documentation](docs/)
|
|
909
|
+
- **API Reference**: [API Documentation](docs/api.md)
|
|
910
|
+
- **Vue Components**: [Component Guide](docs/vue-components.md)
|
|
911
|
+
- **Migration Guide**: [Migration Guide](docs/migration-guide.md)
|
|
912
|
+
- **Issues**: [GitHub Issues](https://github.com/mediables/mediables/issues)
|
|
913
|
+
|
|
914
|
+
## Credits
|
|
915
|
+
|
|
916
|
+
- Originally extracted from the Marketplace project
|
|
917
|
+
- Built on top of Intervention Image
|
|
918
|
+
- Inspired by Spatie Media Library
|
|
919
|
+
- Vue components designed for Ionic Framework
|
|
920
|
+
|
|
921
|
+
---
|
|
922
|
+
|
|
923
|
+
**Ready to get started?** Check out our [Quick Start Guide](#quick-start) or dive into the [full documentation](docs/).
|