Laravel Spatie Media Library: 8 Less-Known Features with Demos

Laravel Spatie Media Library: 8 Less-Known Features with Demos
Admin
Thursday, May 11, 2023 7 mins to read
Share
Laravel Spatie Media Library: 8 Less-Known Features with Demos

Spatie Laravel MediaLibrary is a very popular package to store and associate images with models. But it has so many less-known features under the hood! Let's explore some of them.

Here's the list of what features we will show:

  1. Preventing Bad Cropping or Resizing
  2. Using a Default "Fallback" Image
  3. Ordering Images
  4. Adding Custom Properties and Custom Fields
  5. Assigning Media to Multiple Models
  6. Retrieving All Media from Multiple Models
  7. Displaying Responsive Images
  8. Working with API Resources - Unifying the Output

Let's dive in!


Our Setup

Our base project setup can be found in the GitHub Repository. In there, you will find each step of the tutorial as a separate branch, while the base is on the main branch.


1. Preventing Bad Cropping or Resizing

When using this package, you might quickly notice that some images get cropped or resized incorrectly:

This can be caused by incorrect settings while generating a conversion. To fix this, we have to add a fit method to the conversion:

Model

public function registerMediaConversions(Media $media = null): void
{
$this->addMediaConversion('thumbnail')
->fit(Manipulations::FIT_MAX, 200, 200)
->nonQueued();
}

In this example, we've used the Manipulations::FIT_MAX method which will make sure that we respect MAX width and height, and will not crop the image:

There are more manipulations available, such as:

  • Manipulations::FIT_CONTAIN - will make sure that the image fits inside the given dimensions, and will not crop it
  • Manipulations::FIT_MAX - will resize the image while respecting the given dimensions. The image will not be cropped or stretched.
  • Manipulations::FIT_FILL - will resize the image while filling the background with white background by default (can be customized) . The image will not be cropped or stretched.
  • Manupulations::FIT_FILL_MAX - acts like Manipulations::FIT_FILL, but will upscale the image if it's smaller
  • Manipulations::FIT_STRETCH - will stretch the image to fit the given dimensions
  • Manipulations::FIT_CROP - will make sure that the image fits inside the given dimensions, and will crop it if it doesn't

A full explanation of each method can be found in the documentation.

Code in Repository


2. Using a Default "Fallback" Image

What if some of your models don't have an image? How to avoid a broken image icon?

To fix this, we have a couple of options:

  • Adding a default image when creating a resource
  • Adding a default image when rendering the image

To add a default image when creating a resource it's as simple as storing the default image in the storage/defaults folder and adding it to the media collection:

app/Http/Controllers/PostController.php

public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
 
if ($request->hasFile('images')) {
foreach ($request->file('images', []) as $image) {
$post->addMedia($image)->toMediaCollection();
}
} else {
// Adding default image
$post->addMedia(storage_path('defaults/defaultPostImage.png'))->preservingOriginal()->toMediaCollection();
}
 
return redirect()->route('posts.index');
}

But this will only work when creating a new Database entry. If you want to add a default image when rendering the image we can use the fallbackUrl method:

Model

public function registerMediaConversions(Media $media = null): void
{
$this
->addMediaConversion('thumbnail')
->fit(Manipulations::FIT_FILL, 200, 200)
->nonQueued();
}
 
public function registerMediaCollections(): void
{
$this->addMediaCollection('default')
// Url of the default image
->useFallbackUrl(asset('fallback/fallbackPostImage.png'))
// Url of default image thumbnail
->useFallbackUrl(asset('fallback/fallbackPostImage.png'), 'thumbnail')
// Path to the default image
->useFallbackPath(public_path('fallback/fallbackPostImage.png'))
// Path to the default image thumbnail
->useFallbackPath(public_path('fallback/fallbackPostImage.png'), 'thumbnail');
}

And then in our view, we should change how we display images from:

View

<img src="{{ $post->getFirstMedia()?->getUrl('thumbnail') }}"
class="object-contain"
alt="{{ $post->title }}"/>

To:

View

<img src="{{ $post->getFirstMediaUrl('default', 'thumbnail') }}"
class="object-contain"
alt="{{ $post->title }}"/>

That's it! Now if the image is missing, we will see the default image: For illustration, the default image is bigger than others

To find out more, you can read the extensive documentation or watch a video tutorial.

Code in Repository


3. Ordering Images

Media library packages come with an order_column field which is used to sort the images in the order you want. There are two ways to set the order for the images:

  • Mass updating the order
  • Updating the order one by one

With mass updating it's as simple as passing all IDs in the order you want:

Controller

use Spatie\MediaLibrary\MediaCollections\Models\Media;
 
// ...
 
public function reorderMedia() {
Media::setNewOrder([3, 2, 1]);
}
 
// ...

This will start at 1 and increment by 1 for each ID.

Another way is to manually update the order for each image. For this, we have a few links and one controller method:

resources/views/posts/show.blade.php

<div class="p-4">
@foreach($post->getMedia() as $media)
<div class="flex flex-rows space-x-4">
<div class="flex flex-col justify-center">
@if($media->order_column !== $post->getMedia()->min('order_column'))
<a href="{{ route('posts.moveMedia', [$post, $media, 'up']) }}"
class="text-blue-500 hover:text-blue-600 underline">Move up</a>
@endif
@if($media->order_column !== $post->getMedia()->max('order_column'))
<a href="{{ route('posts.moveMedia', [$post, $media, 'down']) }}"
class="text-blue-500 hover:text-blue-600 underline">Move down</a>
@endif
</div>
<div class="flex flex-col justify-center">
<span>Current position: {{ $media->order_column }}</span>
</div>
<div class="">
<img src="{{ $media->getUrl('thumbnail') }}" alt="{{ $media->name }}">
</div>
</div>
@endforeach
</div>

routes/web.php

use App\Http\Controllers\PostController;
 
// ...
 
Route::resource('posts', PostController::class);
Route::get('posts/{post}/move/{media}/{direction}', [PostController::class, 'moveMedia'])->name('posts.moveMedia');
 
// ...

app/Http/Controllers/PostController.php

use Spatie\MediaLibrary\MediaCollections\Models\Media;
 
// ...
 
public function moveMedia(Post $post, Media $media, string $direction): RedirectResponse
{
if ($direction === 'up') {
// Find the media in a higher position
$mediaInFinalPosition = $post->media()->where('order_column', '<', $media->order_column)->orderBy('order_column', 'desc')->first();
} else {
// Find any media in a lower position
$mediaInFinalPosition = $post->media()->where('order_column', '>', $media->order_column)->orderBy('order_column', 'asc')->first();
}
 
$currentPosition = $media->order_column;
$media->order_column = $mediaInFinalPosition->order_column;
$media->save();
$mediaInFinalPosition->order_column = $currentPosition;
$mediaInFinalPosition->save();
 
return redirect()->back();
}

Now you should see something like this:

Clicking a link will move the image up or down:

As you can see, the image moved from position 1 to position 3. This is intentional as we are covering a case where images are not in sequence. This is great to cover as deleting an image will leave a gap in the sequence.

Code in Repository


4. Adding Custom Properties and Custom Fields

Sometimes an image needs additional information like alt_text or caption. This can be done in two ways in the database:

  • Adding more columns in the media table
  • Using the existing column for custom properties

For both of these options, an implementation is quite simple. I will show you 2-in-1 example.

Start by adding a migration with the caption column:

Migration

return new class extends Migration {
public function up(): void
{
Schema::table('media', function (Blueprint $table) {
$table->string('caption')->nullable();
});
}
};

Then we will write a Controller method to update the caption and alt_text:

app/Http/Controllers/PostMediaController.php

class PostMediaController extends Controller
{
public function update(Request $request, Post $post, int $mediaID)
{
$this->validate($request, [
'caption' => ['nullable', 'string', 'max:200'],
'alt_text' => ['nullable', 'string', 'max:200'],
]);
 
$media = $post->media()->find($mediaID);
 
// Updating the caption in a separate column
$media->update(['caption' => $request->input('caption')]);
// Updating the alt_text in a custom property
$media->setCustomProperty('alt_text', $request->input('alt_text'));
// Save is needed for both!
$media->save();
 
return redirect()->back();
}
}

Add missing route:

routes/web.php

use App\Http\Controllers\PostController;
use App\Http\Controllers\PostMediaController;
 
Route::resource('posts', PostController::class);
Route::resource('posts.media', PostMediaController::class);
 
// ...

And finally, add a form to the view:

resources/views/posts/show.blade.php

{{-- ... --}}
@foreach($post->getMedia() as $media)
<div class="w-full flex flex-rows space-x-4 justify-between">
{{-- ... --}}
 
<div class="">
<img src="{{ $media->getUrl('thumbnail') }}" alt="{{ $media->name }}">
</div>
<div class="">
<form action="{{ route('posts.media.update', [$post, $media]) }}" method="POST" class="flex flex-col space-y-4">
@csrf
@method('PUT')
 
<div class="flex flex-col">
<label for="caption" class="text-gray-700">Caption</label>
<input type="text" name="caption" id="caption"
value="{{ old('caption', $media->caption) }}"
class="border rounded-md p-2 mt-2 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent">
</div>
 
<div class="flex flex-col">
<label for="alt_text" class="text-gray-700">Alt Text</label>
<input type="text" name="alt_text" id="alt_text"
value="{{ old('alt_text', $media->getCustomProperty('alt_text')) }}"
class="border rounded-md p-2 mt-2 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent">
</div>
 
<button type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
{{ __('Submit') }}
</button>
</form>
</div>
</div>
@endforeach
{{-- ... --}}

Now you should see something like this:

Filling them will trigger our Controller method and save the data.

For the caption field we are using a separate column in the media table and the update is done using the update method:

$media = $post->media()->find($mediaID);
 
$media->update(['caption' => $request->input('caption')]);
$media->save();

For the alt_text column we are using a custom property and the update is done using the setCustomProperty method:

$media = $post->media()->find($mediaID);
 
$media->setCustomProperty('alt_text', $request->input('alt_text'));
$media->save();

That's it! Now you can store additional information about your images:

To display this information in the view, you can use the getCustomProperty method:

resources/views/posts/index.blade.php

{{-- ... --}}
@foreach($posts as $post)
<tr class="bg-white">
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
@foreach($post->getMedia() as $media)
<img src="{{ $media->getUrl('thumbnail') }}"
class="object-contain"
alt="{{ $media->getCustomProperty('alt_text', $post->title) }}"/>
<span class="text-center w-full block">{{ $media->caption }}</span>
@endforeach
</td>
{{-- ... --}}
</tr>
@endforeach
{{-- ... --}}

In the code above you'll see that caption is directly accessed on the Model, while alt_text is accessed using the getCustomProperty method. This method has a second parameter - a fallback value in case the property is not set:

$media->getCustomProperty('alt_text', $post->title)

In our case, we will display the title of the post if the alt_text is not set.

Code in Repository


5. Assigning Media to Multiple Models

Media Library is great at assigning an image to a single model, it's not that great if you have a single image and need it to display on different models. With this, you have to get more creative.

Quick overview of what we will do:

  • Create a new model called MediaHolder and store the image only once using the default Media Library workflow
  • Create a many-to-many relationship between Post and MediaHolder and assign the same image to multiple posts
  • Modify how we are displaying images in the view

Let's look at the code:

Migration

return new class extends Migration {
public function up(): void
{
Schema::create('media_holders', function (Blueprint $table) {
$table->id();
$table->softDeletes();
$table->timestamps();
});
 
// Creating a many-to-many table
Schema::create('media_holder_post', function (Blueprint $table) {
$table->foreignId('media_holder_id')->constrained()->cascadeOnDelete();
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
});
}
};

Our MediaHolder model will be a simple Eloquent model with a many-to-many relationship to the Post model:

app/Models/MediaHolder.php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
 
class MediaHolder extends Model implements HasMedia
{
use SoftDeletes;
use InteractsWithMedia;
 
public function registerMediaConversions(Media $media = null): void
{
$this->addMediaConversion('thumbnail')
->fit(Manipulations::FIT_MAX, 200, 200)
->nonQueued();
}
 
public function posts(): BelongsToMany
{
return $this->belongsToMany(Post::class);
}
}

Extending this: you can simply add more relationships to this model, for example, if you have a Product model, you can add a relationship to it as well.

And now we need our Post model to have a relationship to the MediaHolder model:

app/Models/Post.php

use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class Post extends Model
{
// ...
public function media(): BelongsToMany
{
return $this->belongsToMany(MediaHolder::class);
}
}

That's it, our database is ready and relationships are in place. Now to make this function work, we need to add a new Controller with a route:

app/Http/Controllers/PostMediaController.php

use App\Models\MediaHolder;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class PostMediaController extends Controller
{
public function create()
{
$posts = Post::pluck('title', 'id');
 
return view('postMedia.create', compact('posts'));
}
 
public function store(Request $request): RedirectResponse
{
$this->validate($request, [
'image' => ['required', 'image'],
'posts' => ['required', 'array'],
'posts.*' => ['required', 'exists:posts,id']
]);
 
$media = MediaHolder::create();
$media->addMedia($request->file('image'))->toMediaCollection();
 
$media->posts()->sync($request->input('posts'));
 
return redirect()->route('posts.index');
}
}

routes/web.php

use App\Http\Controllers\PostController;
use App\Http\Controllers\PostMediaController;
 
// ...
 
Route::resource('posts', PostController::class);
Route::resource('media', PostMediaController::class)->only(['create', 'store']);
 
// ...

This Controller will take care of the creation of a new MediaHolder model and assign its images to multiple posts. Here's the UI we will build:

resources/views/postMedia/create.blade.php

{{-- ... --}}
<form action="{{ route('media.store') }}" method="post" enctype="multipart/form-data">
@csrf
 
<div class="p-4">
<div class="flex flex-col">
<label for="image" class="text-gray-700">Image</label>
<input type="file" name="image" id="image"
class="border rounded-md p-2 mt-2 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent">
</div>
@error('image')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
 
<div class="p-4 flex flex-row justify-around space-x-4">
@foreach($posts as $id => $title)
<div class="">
<label for="post-{{ $id }}">
<input type="checkbox" name="posts[]" id="post-{{ $id }}"
value="{{ $id }}">
{{ $title }}
</label>
</div>
@endforeach
</div>
 
<div class="p-4">
<button type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
{{ __('Submit') }}
</button>
</div>
</form>
{{-- ... --}}

As you can see, we take a single image upload and display a selection of posts that we can choose from (a very simplified display!).

To display the related images, we have to modify our code a little bit:

resources/views/posts/index.blade.php

{{-- ... --}}
@foreach($posts as $post)
<tr class="bg-white">
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
@foreach($post->media as $media)
<img src="{{ $media->getFirstMedia()?->getUrl('thumbnail') }}"
class="object-contain"
alt="{{ $post->title }}"/>
@endforeach
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $post->title }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $post->post_text }}
</td>
</tr>
@endforeach
{{-- ... --}}

In this case, we are taking all related MediaHolder models and displaying their first image.

To illustrate this, let's dump some IDs from our database:

Here we can see that we have a single image (ID 1) shared on both posts. It uses the same MediaHolder model (ID 1) and is assigned to both posts (IDs 1 and 2).

Remember: This might cause an N+1 queries issue, so make sure to use eager loading when displaying this data:

app/Http/Controllers/PostController.php

public function index()
{
$posts = Post::with(['media.media'])->paginate(10);
 
return view('posts.index', compact('posts'));
}

We have double media here because in Post Model we have a media relationship to load assigned images and in the MediaHolder model we have a media relationship to load the images themselves from the Media Library package.

Code in Repository


6. Retrieving All Media from Multiple Models

If you ever need to retrieve all media from multiple models, you can easily do that with the Media model that's in the package:

To do this we need a controller, route, and view:

app/Http/Controllers/AllMediaController.php

use App\Models\Post;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
 
class PostImagesController extends Controller
{
public function __invoke()
{
$images = Media::query()
->with('model')
->where('model_type', Post::class)
->orderBy('order_column')
->orderBy('name')
->get();
 
return view('postImages.index', compact('images'));
}
}

routes/web.php

use App\Http\Controllers\PostImagesController;
// ...
 
Route::get('postImages', PostImagesController::class)->name('postImages');
 
// ...

resources/views/postImages/index.blade.php

{{-- ... --}}
<div class="flex flex-row justify-between">
@foreach($images as $image)
<div>
<h2 class="w-full text-center text-xl font-bold">{{ $image->model->title }}</h2>
<img src="{{ $image->getUrl() }}" alt="{{ $image->name }}" width="200"/>
<span class="w-full text-center block">{{ $image->name }} / {{ $image->order_column }}</span>
</div>
@endforeach
</div>
{{-- ... --}}

As you can see, our Controller uses Media just like any other model, so we can apply filters, eager loading, and order by clauses.

Displaying the images is pretty straightforward, as you loop through the collection and display the image using the getUrl() method.

Code in Repository


7. Displaying Responsive Images

We all know about responsive designs but have you looked into Responsive Images? It allows you to scale your image based on the device's screen resolution! Cool, right? But it's even cooler to know that you can do this with the Laravel Media Library package!

This can be done by using the responsiveImages() method while adding an image to the model:

app/Http/Controllers/PostController.php

// ...
 
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
 
foreach ($request->file('images', []) as $image) {
$post->addMedia($image)
->withResponsiveImages() // <- This function generates responsive images
->toMediaCollection();
}
 
return redirect()->route('posts.index');
}

And that's it! You will not get a full set of different-resolution images for each image you upload. But how do you display them? Well, it's pretty simple:

resources/views/posts/index.blade.php

{{-- ... --}}
<img src="{{ $post->getFirstMedia()?->getUrl('thumbnail') }}"
srcset="{{ implode(', ', $post->getFirstMedia()?->getResponsiveImageUrls()) }}"
class="object-contain"
alt="{{ $post->title }}"/>
{{-- ... --}}

Let's see what we are doing here:

  • src attribute is used to display the image on devices that don't support the srcset attribute (older browsers).
  • srcset attribute is used to display the image on devices that support the srcset attribute (newer browsers). This is the part that will list all the different resolution images that we have generated.
  • getResponsiveImageUrls() method returns an array of all the different resolution images that we have generated.
  • implode(', ', $post->getFirstMedia()?->getResponsiveImageUrls()) makes sure that we format it into a string that is required by the srcset attribute.

And you might think that this is complicated, right? Well, there's also the ability to just do this:

resources/views/posts/index.blade.php

{{-- ... --}}
{{ $post->getFirstMedia() }}
{{-- ... --}}

Which will automatically generate the whole <img .../> tag for you! But I don't like that method as it's a tiny bit harder to customize (for example, different ALT text or styling). But it's up to you to decide which method you prefer.

Warning: You might want to configure your Queue to process the images in the background as this might take a while to process.

Code in Repository


8. Working with API Resources - Unifying the Output

Working with API means setting clear expectations on what's going to be returned. And while the documentation really helps - you should set up your media to be returned consistently across your application. To do this, we can use API Resources:

app/Http/Resources/Media/MediaCollection.php

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
 
/** @see \Spatie\MediaLibrary\MediaCollections\Models\Media */
class MediaCollection extends ResourceCollection
{
public $collects = MediaResource::class;
 
public function toArray(Request $request): array
{
return $this->collection->toArray();
}
}

And individual resource:

app/Http/Resources/Media/MediaResource.php

 
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
 
/** @mixin Media */
class MediaResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
// Fields that I use
'uuid' => $this->uuid,
'humanReadableSize' => $this->humanReadableSize,
'name' => $this->name,
'file_name' => $this->file_name,
'mime_type' => $this->mime_type,
'order_column' => $this->order_column,
'urls' => [
// Full URL to the image
'full' => $this->getFullUrl(),
// Each conversion with Full URL to it
'conversions' => collect($this->generated_conversions)->map(function ($value, $key) {
return $this->getFullUrl($key);
}),
// Responsive images with Full URLs to each of them
'responsiveImages' => $this->getResponsiveImageUrls()
],
'custom_properties' => $this->custom_properties,
 
// Other fields you might want to add
// 'previewUrl' => $this->previewUrl,
// 'id' => $this->id,
// 'type' => $this->type,
// 'extension' => $this->extension,
// 'originalUrl' => $this->originalUrl,
// 'model_id' => $this->model_id,
// 'model_type' => $this->model_type,
// 'collection_name' => $this->collection_name,
// 'disk' => $this->disk,
// 'conversions_disk' => $this->conversions_disk,
// 'size' => $this->size,
// 'manipulations' => $this->manipulations,
// 'created_at' => $this->created_at,
// 'updated_at' => $this->updated_at,
];
}
}

With this setup, we'll constantly return the same fields and structure for our media. Including the URLs to the images in both conversions and responsive images. For example:

{
"media": [
{
"uuid": "c301304d-5e6a-4dc2-b70b-a8fbd25bbd66",
"humanReadableSize": "493.12 KB",
"name": "Untitled-1 (1)",
"file_name": "Untitled-1-(1).png",
"mime_type": "image\/png",
"order_column": 1,
"urls": {
"full": "http:\/\/spatie-medialibrary-demo.test\/storage\/5\/Untitled-1-(1).png",
"conversions": {
"thumbnail": "http:\/\/spatie-medialibrary-demo.test\/storage\/5\/conversions\/Untitled-1-(1)-thumbnail.jpg"
},
"responsiveImages": [
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_1857_1704.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_1553_1425.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_1299_1192.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_1087_997.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_909_834.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_761_698.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_636_584.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_532_488.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_445_408.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_373_342.png",
"http:\/\/spatie-medialibrary-demo.test\/storage\/5\/responsive-images\/Untitled-1-%281%29___media_library_original_312_286.png"
]
},
"custom_properties": []
}
]
}

This will make it easier to work with the API and will make sure that we don't forget to return some important fields.

To use this, all you have to do is load the MediaCollection in your PostResource:

app/Http/Resources/PostResource.php

use App\Http\Resources\Media\MediaCollection;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
 
/** @mixin Post */
class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'post_text' => $this->post_text,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'media_count' => $this->media_count,
'media' => $this->whenLoaded('media', new MediaCollection($this->media)),
];
}
}

That is it!

Code in Repository


Did you know about all these features? Or maybe we missed something amazing? Feel free to add comments below!