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? (36 h 00 min)

You also get:

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

Already a member? Login here

Richard A. Hoyle avatar

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.

Povilas Korop avatar

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

Joe avatar

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]);
}
}}
Richard A. Hoyle avatar

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?

Harti avatar

did you run php artisan storage:link ?

TuanTQ avatar

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 ?

krd313 avatar

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 ?

Povilas Korop avatar

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?

krd313 avatar

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?

Povilas Korop avatar

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
krd313 avatar

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.

Povilas Korop avatar

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.

krd313 avatar

Thanks for the suggestions.

Dmytro Sakharuk avatar

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(
// ...
Angel Fabian Silvera avatar
Angel Fabian Silvera

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.