Planning S3 file uploads0:00
Okay, as we move on to build our multi-tenant application, one thing we're invariably going to need is the ability to upload things to our application. And it could be images, it could be PDFs, and we're going to do this using Amazon S3. If you're building a multi-tenant application, you're probably trying to build a large application. You want lots of users. That means you might be using Laravel Vapor, or maybe a load balancer in front of multiple servers. In that case, you can't use the server storage for any of the uploads. You're going to have to put it out on something like S3. So we're going to do that, and we're going to use the new Livewire File Uploads feature,
You're going to have to put it out on something like S3. So we're going to do that, and we're going to use the new Livewire File Uploads feature, which is really wonderful. So I'm excited to show you how this works. Now the last thing before we get into it is, every time I build an app like this, I always like to think of two different places to put images. We're going to need a public storage, where whatever I upload to that, anyone can see it across the whole internet. They don't have to be in my app. And we're going to need private storage, where I get to control whether or not you see it.
They don't have to be in my app. And we're going to need private storage, where I get to control whether or not you see it based on what your role is in my app, or something like that. And we're going to start with the public images. So imagine this is all hard-coded right now from Tailwind UI, but imagine we want to upload an image of all of our employees, and maybe my company wants to send emails with a picture of the employee who's going to be helping you with your purchase, or whatever it is. We need that image to be public so that it can come through in that email. So that's how we're going to start right now. But before we put the image on S3, let's just make sure we understand how file uploads work.
Implementing Livewire local uploads1:24
So that's how we're going to start right now. But before we put the image on S3, let's just make sure we understand how file uploads work in Livewire and get something stored locally first. So let's take a look at the Livewire docs. We've got Livewire File Uploads. Let's take a look here. And if we scroll down, it's pretty simple, pretty basic. We need to use this trait. We need this save function. I'm going to copy this and put it into my app.
We need this save function. I'm going to copy this and put it into my app. I've got a save function right here. I need to use the trait UsesFileUploads. And then the last thing I need to do is wire:model a public property. You can see this public property here to Photo. So I'm just going to grab all of this and go to my AddUser. This is where I have my file input, so I'm going to paste it here. And the only thing I want to do is change this from submitting a form to wire:click="save".
And the only thing I want to do is change this from submitting a form to wire:click equals save. And so this should work. We should be ready to upload a file to Livewire through Livewire. So let's go ahead and click Add Team Member. I've got this basic form that's going to get all the information we need. And here I can choose my file. And I'm going to grab my headshot and save the photo. And we didn't get any feedback, but the way we can tell if it's working is taking a look at our storage.
And we didn't get any feedback, but the way we can tell if it's working is taking a look at our storage. So now we have this Livewire temp. And you can see there's my headshot right there. And we also have the photos directory, which if you remember, we are saving the photo into the photos directory. And that worked too. So Livewire uploads are working. And you may be wondering why there's two different files here. And the reason is Livewire uses what's called sideloading.
And you may be wondering why there's two different files here. And the reason is Livewire uses what's called sideloading. So when you're on the UI and you actually choose a file, if I choose a different one here, it's going to load that. And you can see there's a second one. But until I save it, this is just a temporary folder that's going to get deleted with all the stuff that never actually gets saved. This is where you really save it. So this is all working great locally. And just to show you how this sideloading works a little bit better, let's go back to
Previewing temporary uploads3:31
So this is all working great locally. And just to show you how this sideloading works a little bit better, let's go back to our AddUser. And we've got this little, this SVG is this little pretend guy here. So let's go ahead and do an if statement. And if there is a photo, then what we're going to do is let's go to our team table here. And we're going to get where we've got this image here. Let's just take this div. And we want it to look exactly the same. So let's put it in here.
And we want it to look exactly the same. So let's put it in here. But for the source of the image, we're going to do photo, temporary URL. And this is something that Livewire provides. You can see it in the docs here, temporary URL. And so this is going to let us actually see what's been sideloaded before it gets saved. And then this is what we want to show if we don't have a photo here. So let's go back, take a look. So we're showing that here. But if I grab one of these pictures, now we're actually seeing what's been sideloaded.
Connecting Laravel to S35:22
So Team public. And when I do this, I'm not going to block all public access. I can actually block these. And then I'm going to acknowledge that it's going to be public. And I'll create this bucket as well. So now we have a little bit more to make this public. But the first thing I want to do is make sure that we can actually load something to that bucket before we try to get something out of it to read from it. And to do that, we've got to get our Laravel app connected. So before we do any more in S3, we do need to look at the Laravel docs for S3.
And to do that, we've got to get our Laravel app connected. So before we do any more in S3, we do need to look at the Laravel docs for S3. Because there is a composer package that we need to get. So if we scroll down here, you can see for Amazon S3, we need this package. So let's grab this. Let's composer require that. Okay, and I got this allowed memory size is exhausted. You might see this and you might, this might be frustrating for you. There's actually a good article on Laracasts that shows me how to fix this. So allowed memory size by it's exhausted.
So let me just grab this. And I can post that there. And now I think it's going to work. Okay, and I was right. Everything worked here. So we're good. So now let's go back to S3. And in order to give our Laravel app access to both of these buckets, we're going to have to go into what's called IAM or Identity Access Manager or Management. And we just need to create a user that we can have in use in our Laravel app to talk
to go into what's called IAM or identity access manager or management. And we just need to create a User that we can have in use in our Laravel app to talk to S3. So I'm going to add a User. Let's call it Laravel because that's what it is. Give it programmatic access. And let's get a policy from the directory. And this makes it actually really easy. I don't find AWS to be easy usually, but this is easy. Just get the Amazon S3 full access that's read and write access to S3.
I don't find AWS to be easy usually, but this is easy. Just get the Amazon S3 full access that's read and write access to S3. Let's just click through create the user. And we're good. So now I've got some stuff. This is secret. But don't worry, I've already gotten rid of this user. So don't try to use it. But if I go to my .env file, you can see I've got an AWS_ACCESS_KEY_ID here. So let's just paste that in.
But if I go to my .env file, you can see I've got an AWS access key here. So let's just paste that in. I've got a secret key. So let's do that as well. The region was US East two. And then we have a bucket. So I have my teamZPrivate. And I also want an AWS bucket for my public. So I can just do that here. And we'll say teamZPublic.
So I can just do that here. And we'll say teamZ public. And so now I'm going to have to go to my file System config. And so S3 is going to be my private bucket. And let me just copy and paste. And we're just going to call this S3 public. And the only difference is the bucket is public. So now I've got the ability to access both of these different buckets, just based on which disk I use when I use the Storage facade to access a disk on my file system. Okay, so let's see if we can actually load an image to this S3 public.
which disk I use when I use the Storage facade to access a disk on my file system. Okay, so let's see if we can actually load an image to this S3 public. So let's go to our AddUser component. And when we store, you can pass in a second parameter, that's the disk you want to use. So let's just do that. I've got it storing to my S3 public. And let's go back to the app, refresh. And with any luck, this may work. Okay, so if I go to AWS, and let's go back to S3, let's look at my public bucket. And let's see if there's anything in here.
Making S3 bucket public9:33
And the way we're going to do this is with a policy. So I am sorry, this is a policy or permissions on the actual photo, I want to go to the bucket. Here's the permissions. And we've got a bucket policy here. And so I don't know a lot about AWS, but I just do AWS S3, bucket policy. And we've got a link here, that if we scroll down, you can see granting read only permission to an anonymous user. That's exactly what we want. So let me just copy this, go back here, go to my bucket policy, paste it in. And the only thing I need to do is take my value here and replace this.
And if I look at it, it works there. And also, we'll open the link in a new tab. Now you can see it works. So anyone that has this URL is going to be able to see this image, which is exactly what I want. So it may seem like we're almost done. But actually, if we go back to phpstorm and take a look at our livewire temp folder here, we're still side loading into our localStorage, we're not side loading directly to S3. And that's what we want. So we can do that too, with Livewire.
Configuring temp uploads to S311:11
And that's what we want. So we can do that too, with Livewire. So let's go to the Livewire docs again. And we need the config. So let's see how to publish the config file. Let's grab this. And now we've got our Livewire config. And if we go here, you can see I've already scrolled down to it, the Livewire temp file uploads endpoint configuration. And this is going to allow us to say where we want temporary files to be uploaded.
uploads endpoint configuration. And this is going to allow us to say where we want temporary files to be uploaded. So right now it goes to local. But all we have to do is change this to s3. And I actually do want this to be in my s3 private bucket, because I don't want anyone to be able to access those temp, there's no reason anyone should have access to those temp folders that should be kind of local to my app. So we need to see if this works. So let's go back here, refresh the page, and I'm going to try to upload another one. And you can see it didn't work.
Saving user and photo data14:14
the form now. So let's just take this photo. Let's put the validation requirements there. I do still want to store the photo here. One thing I need to do is get the file name. And you saw those crazy file names that were in s3. And that's what I need to store on my User model so that I know how to get that photo. So now that I'm done with this, I can just delete it. And then I need to also, once this is done, I've got to create the User. So I've got that code.
And then I need to also, once this is done, I've got to create the User. So I've got that code. Let me just paste it in. Okay, so this is pretty simple. Let me just import the User class. And I've just got all of this stuff. It's going into the database. And I also have to set a password for the User. Presumably, we would email them, you know, some option to set their own password. I haven't set that up right now.
Presumably, we would email them, you know, some option to set their own password. I haven't set that up right now. Also need to import the string helper. And now the last thing I want to do is maybe give myself some feedback that it actually worked. So let's have a success message in the session. Sorry, that's not what I want. I want to flash a success. We did it. Okay, that looks good.
We did it. Okay, that looks good. And then let's go back to our addUser. And let's put that flash message somewhere. So just just so we know that everything worked, I don't know, right here looks good. Let's say if session has success, let's just spit out the session success. Okay, now let's go back here. Let's refresh the page. Let me grab my headshot here. I don't need that button anymore.
Let me grab my headshot here. I don't need that button anymore. But let's just add team member. We did it. So everything looks like it worked. Let's go ahead and look at our database. If I refresh, I've got this new User, I've got the photo here. And so this gives me the directory and the name of the photo. And so just so you know, if I ever want to get this back out, what I can do is now I'm in Tinkerwell here.
And so just so you know, if I ever want to get this back out, what I can do is now I'm in Tinkerwell here. And I can just do. And then I want the URL for this. And if I do that, then that gives me my URL. And if I take that out to Chrome, throw it in there, it works. So that's how we would actually use that image to show it if we were going to like put it right here. And we'll do that in the next video, along with dealing with some of the private uploads that I want to show as well.
