Skip to main content

File Upload with Spatie Media Library

Premium
8 min read

Now, let's add a file upload field to the Tasks CRUD.


Prepare Back-End: Spatie Media Library

Let's install a well-known package spatie/laravel-medialibrary to handle the file data.

composer require "spatie/laravel-medialibrary"

Then, according to its documentation, we need to publish and run migrations:

php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations"
php artisan migrate

Finally, we add the appropriate Traits to the Task Model file.

app/Models/Task.php:

use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
 
// ...
 
class Task extends Model
class Task extends Model implements HasMedia//[ tl! ++]
{
use HasFactory;
use InteractsWithMedia;
 
// ...

Ok, the package installation/configuration is ready. Let's dive into the forms.


File Upload in Create Form: Front-End

This time, let's start with the front end.

For reference, here's the Inertia documentation on File Uploads.

Let's see how our Create.tsx file needs to change.

  • We add media to the CreateTaskForm and useForm
  • We add a progress indicator from Inertia
  • We add the <Input> for the file picker

We start with the TypeScript changes...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (30 h 41 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

RA
Richard A. Hoyle ✓ Link copied!

Getting error from the resources/js/pages/tasks/create.tsx and the edit.tsx The files both work and I am getting the info on to the web page. When looking over the pages the only thang I can see that is being marked as ban are the <Input id="media" onChange={(e) => setData('media', e.target.files[0])} className="mt-1 block w-full" type="file" /> This is on both pages On the Edit.tsx you mentioned the put problem I am getting this as well.

PS. I am using PhpStorm as my editor.

PK
Povilas Korop ✓ Link copied!

Wait, so the files are working but you're worried about IDE warnings, only?

J
Joe ✓ Link copied!

You can get rid of the linter error by applying a conditional to make sure e.target.files[0] is not null first:

onChange={(e) => {
const files = e.target.files;
if (files && files.length > 0) {
setData('media', files[0]);
}
}}
RA
Richard A. Hoyle ✓ Link copied!

Not able to see File or Categories on the index.tsx page but can see both on the edit.tsx page and in the database; any suggestions?

H
Harti ✓ Link copied!

did you run php artisan storage:link ?

T
TuanTQ ✓ Link copied!

I get error Add fillable property [media] to allow mass assignment on [App\Models\Task]. and if i remove media in form request, it will work. is there anyone like me ?

K
krd313 ✓ Link copied!

in the index and edit pages I do not see any image, I see a box and a small icon, when I click on this I get taken to a fresh web page with "The requested URL was not found on this server." I think it maybe looking at the wrong directory how do I change that ?

PK
Povilas Korop ✓ Link copied!

It probably depends on how you configured your web-server and .env file.

Have you run "php artisan storage:link"?

What is the actual URL it is trying to load?

K
krd313 ✓ Link copied!

I have run the storage link, when I click on the icon it shows 'http://localhost/storage/3/plus.1024x1024.png", if I go into storage/app/public/3/ the file is there. Why does it create a directory for each file?

PK
Povilas Korop ✓ Link copied!

That's how spatie/laravel-medialibrary works, creating a directory with ID for each file. With why this file isn't shown, check two things

  • In .env file: APP_URL=http://localhost
  • And run php artisan storage:link
K
krd313 ✓ Link copied!

I have re-run "php artisan storage:link" and got " ERROR The [public/storage] link already exists. " . in the .env file the setting was "APP_URL=http://localhost". I went through a the exercise with a fresh install and got the same result. I am using vscode on linux mint. I have checked my files against the gitHub commit but have not been able to find the error. I think its saving to the correct place but I don't think its looking in the correct place.

PK
Povilas Korop ✓ Link copied!

Maybe something is different on Linux Mint specifically or your web-server, like some folder/file permissions are different? I don't see any other obvious errors, either, if the URL "http://localhost/storage/3/plus.1024x1024.png" doesn't work in your browser.

K
krd313 ✓ Link copied!

Thanks for the suggestions.

DS
Dmytro Sakharuk ✓ Link copied!

When sending a request via router.post, the errors object will be empty. Therefore, it is recommended to use post and transform

let { data, setData, errors, reset, post, transform, processing, progress } = useForm<Required<EditTaskForm>>({
// ...
});
 
transform((data) => ({
...data,
_method: 'PUT'
}));
 
const editTask: FormEventHandler = (e) => {
e.preventDefault();
 
post(
// ...
AF
Angel Fabian Silvera ✓ Link copied!

I made 2 changes to my code because they are using string in media and thats give me TypeScript error you can only omit it but i prefer to change the Type of Props

type EditTaskForm = {
name: string
is_completed: boolean
due_date: string
media?: File | null;
categories: string[];
};
 
const { data, setData, errors, put, reset, processing, progress } = useForm<Required<EditTaskForm>>({
name: task.name,
is_completed: task.is_completed,
due_date: task.due_date,
media: null,
categories: task.task_categories.map((category) => category.id.toString()),
});

And in the Input

<Input
id="media"
onChange={(e) => setData('media', e.target.files?.[0] ?? null)}
className="mt-1 block w-full"
type="file"
/>

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.