Add Photo Path Migration0:00
I want to add the ability to upload an image for our articles and there's a little bit, well there's actually quite a bit of setup that we have to do to make this work. To start, we need to make a migration because we need to store the path to the image. So we'll make a migration, we'll call this addPhotoPathToArticles. Let's open up that file so that inside of the up method we will create a new column. It's going to be a string, it's called photo_path, and it does need to be nullable because not every article needs an image. But then inside of the down method we will drop that column and everything should be good. So we should be able to run php artisan migrate and everything should work okay. Looks like it did. So let's close that out. Now the next thing we
Enable File Upload Trait0:50
be good. So we should be able to run php artisan migrate and everything should work okay. Looks like it did. So let's close that out. Now the next thing we need to do is add the WithFileUploads trait to both the CreateArticle and the EditArticle components. We cannot do this inside of our ArticleForm class, although that would be ideal, but that's not going to work. The component has to have that trait. But with that in place, that's all that we really need to do for the component classes themselves. Everything else is going to be done inside of our Form class as well as the views. So let's start with the Form class because we do need to add that photo_path. Yes, I know that we could do this in a little bit different way, but this is what we're going to do just
Add File Input Field1:34
because we do need to add that photo_path. Yes, I know that we could do this in a little bit different way, but this is what we're going to do just because this is straightforward. So we're going to say photo_path is equal to the articles.photo_path that is sent to our setArticle method. And then we are going to get the path to whatever image is uploaded inside of the store method and inside of update. But we'll get to that. Let's focus on the edit article view because we need to add the input element with a type of file so that we can actually select the file and upload it. And I want this to go in between where we have the content field and then where we start to have the published and other options. So I'm just going to take the content stuff and I'm going to paste
we have the content field and then where we start to have the published and other options. So I'm just going to take the content stuff and I'm going to paste that in. Now unfortunately we don't have the ability to track dirty state for our uploaded image. I'm not sure if it's a limitation of Livewire or if it's an HTML thing. I do know that I've tried a lot of things and I couldn't get it to work. So in order to save us a lot of time I'm just going to strip out all of the dirty state tracking stuff for this field. The label is going to be simply photo. We'll get rid of this span to denote that this value has changed. It looks like we have an extra quote. If we have that there then where did these extra quotes come from? Because they're all over the place. I don't know. Okay
looks like we have an extra quote. If we have that there then where did these extra quotes come from? Because they're all over the place. I don't know. Okay well I'm getting sidetracked. So we have our photo label. We don't have a textarea. Instead we have an input element where the type is equal to file. And then just like any other form control we would use the model for whatever property. Now we don't want to set this to photoPath because photoPath is just a string. It's the path to the image that we are going to store in the database. Whenever we upload a file we get an object that we can work with. So we need another property inside of our form object so that we can work with that photo object. So we need a different property name here. I'm just going to
another property inside of our form object so that we can work with that photo object. So we need a different property name here. I'm just going to call it photo. But do remember that this is on the form object. Don't make a mistake that I have made and not include that form object. So our model is form.photo. And then we do have some validation. So if a validation rule fails we want to display that message. Everything else should be okay. Let's go ahead and scroll down to our submit button. Since we can't track dirty states for our upload I'm just going to get rid of where we disable the button and then enable it when the form is dirty. It's just going to save us some time. Otherwise we would select a file but then we would have to modify something in the form in order to
Validate and Store Upload4:30
when the form is dirty. It's just going to save us some time. Otherwise we would select a file but then we would have to modify something in the form in order to save it. So as far as our form is concerned this could look better but this is going to work. Now we just need to do something whenever we have that photo. So we'll have an if statement. If our photo is truthy then we want to do something with that photo. But we do need to define that photo property don't we? So we'll have public $photo and we can go ahead and add our validation rules. So really what we want is the image validator and we want to set a max size of let's say 1 meg. So now whenever we attempt to update this article if we have a photo then we will get the photo path by calling a store method on the
of let's say 1 meg. So now whenever we attempt to update this article if we have a photo then we will get the photo path by calling a store method on the photo object. So we can call store and then we can specify the directory where we want to store it. So let's say article_photos but we do want to be sure to store this in a place where we can publicly show the article. So I'm going to call this method called storePublicly and then we will specify the disk of where we want to store which will be the public disk. And since we essentially need to do the same thing inside of the store method whenever we create an Article let's just go ahead and copy and paste that code there. However we do need to include the photo path whenever we create the article as
Display Stored Image5:55
create an Article let's just go ahead and copy and paste that code there. However we do need to include the photoPath whenever we create the Article as well as when we update the Article. But we also need to visit the Article model so that we can add photoPath to the fillable array. And then I think we have everything ready to go. So we should be able to select a file. Let's pick that. We'll save it and we are redirected. It looked like everything worked but of course we don't really show that photo anywhere. So let's do this. Back inside of the editArticle view we'll have our label. Then we will have a div element that'll have flexbox and items-center. And the first thing we will have is the input element there. But then we'll have another div element that will contain
that'll have flexbox and items-center. And the first thing we will have is the input element there. But then we'll have another div element that will contain our photo. So we'll add an if. Our form has a photoPath. Then we want to display that image. And we'll do that with an img element. Let's set a class of width-1/2 because we want to control the size of this. And then for the src we'll use our Storage facade to get the URL for our photoPath. And that should be okay except that this div element needs to go up here inside of this div element with flexbox. Although I used this here. Let's change that to form. So now we should be able to go to the browser and we see our image. Except I selected the wrong image. This is upside down. This needs to be right side up. And
Add Temporary Preview7:29
now we should be able to go to the browser and we see our image. Except I selected the wrong image. This is upside down. This needs to be right side up. And it would have been really helpful if we had some kind of preview image that we could show when a user selects a file to upload. So back inside of our view we're gonna change this up just a little bit. Well first of all check if we have a photo. Because if we do we want to display the temporary image that we get. Otherwise we will display whatever we have for the photo path. And the source for this temporary photo comes from the photo object itself. We have a method called temporaryUrl. That way the user can preview the image that they upload. And if they need to change it before they actually save it then they can do.
called temporary URL. That way the User can preview the image that they upload. And if they need to change it before they actually save it then they can do that. But once again notice that I used this instead of form. So let's change that. So that now we can select a new file and we can see the preview. Yes that is indeed the image that I wanted to use. So I can save that. And if we take a look at that article once again we can see we successfully saved the new image. Well now we need to take essentially all of this markup for our form and go to the create article view. And then after where we have our content we'll paste that in. Except that in this case we don't really need to display an image if we have a photoPath. Because if we are creating an article then well we don't have anything.
Except that in this case we don't really need to display an image if we have a photo path. Because if we are creating an article then well we don't have anything to display. But we do want to continue to display the temporary URL for the photo. So that the user can be sure that they are uploading the correct file. Now if you wanted to support multiple file uploads then for the input element you would need to include the multiple attribute. The photo property inside of our form class would now need to become an array. Ideally we would change the name of this property to photos. But that would mean we would have to go through everything else and make that change. And I'm lazy. So for the validate attribute we would need to change this to an array. To where the key would be
everything else and make that change. And I'm lazy. So for the validate attribute we would need to change this to an array. To where the key would be the name of our property dot and then *. The value would still be our validator that we wanted to apply here. And then when it comes to working with those photos we would do so by iterating over that array. Which we could use a foreach. This photo as photo. And then we would store those individually. But let's not do that. We have a single photo. We'll leave it as a single photo. And that's gonna make it work just fine. So if you want to support file uploads you first of all need to add the WithFileUpload trait to your component. It cannot be done to the form class. If you're using a form class it has to be the
first of all need to add the WithFileUpload trait to your component. it cannot be done to the form class. If you're using a form class it has to be the component. Then from there it's almost just like using any other property. You create a public property. You set it as the model to the file input element. Then you can store that upload by calling the store method or the storePublicly method. And get the path of that file so that you can store that where wherever you need to store it. If you allow the user to upload an image be sure to use the temporaryUrl method. Just so that you can display a preview image for the user. That way if they upload the wrong image they can select the correct image and then upload it.
user. That way if they upload the wrong image they can select the correct image and then upload it.
