مرور کامند حذف تصاویر استفاده نشده0:00
There's one more thing I want to do before we move on. Now it's not quite exactly a refactoring, but more of a convenience thing. All right, so this one might be a little bit of a side quest. And to be perfectly honest with you, I'm not sure if this is the best way to do this. I found a way that works really good for me and feel free to correct me in the comments. So let me set the stage here.
Problem: orphaned images0:35
and feel free to correct me in the comments. So let me set the stage here. Every single time we create a new Puppy, we upload an image and that image gets stored in storage/app/public/puppies. As a result, this puppies folder has a lot of images in there, but there's one problem. Whenever we delete a Puppy from the database, the image uploaded does not get deleted, and that's a problem. Our app doesn't have CRUD operation at this stage.
and that's a problem. Our app doesn't have CRUD operation at this stage. It's only a create and read app. We do not update or delete puppies. So the delete action is where you would probably delete the image, but for now I wanted to just manually or semi-manually go in the folder and figure out what images are there but are not attached to any puppy.
Plan: cleanup command1:16
and figure out what images are there but are not attached to any Puppy because the Puppy was deleted and get rid of that image. And so I thought a cool way to do this would be to create a command. And then on this command I scan the folder for images, I compare it to the Puppys in the database, and then whatever is not matching, we get rid of. Let me show you what I've came up with. So again, not saying this is the best way,
Create artisan command1:36
Let me show you what I've came up with. So again, not saying this is the best way, but it's for sure been super useful to me. So I'm going to create a new command with php artisan make:command, and let's call this command deleteUnusedPuppyImages that sort of makes sense as a name. All right? And this is our command scaffold. So like I said, instead of writing this command line by line, I'm going to paste my solution and then we are going to talk through it.
by line, I'm going to paste my solution and then we are going to talk through it. But as you can see, this scaffold has a signature, which is the name to call the command, uh, a description and then a handle function. So I'm going to delete all of this and paste my command and we'll go through it together. So you can see I'm using the Puppy model and the Storage facade. Uh, but then we have a signature called delete
Compare files to database2:26
and the Storage facade. Uh, but then we have a signature called deleteUnusedPuppyImages. And the description is cleanup uploaded images that are no longer referenced in the database. Pretty straightforward to this point. So let's move to the handle function. Alright, so the first thing I do here is go and look at all the files, all the image files inside the puppy directory.
and look at all the files, all the image files inside the puppy directory. So these stored images are all the files in the storage/app/public/puppies directory. And then what I want to do is compare these stored images with what images are referenced in the database. So I go look at all the Puppys, I pluck just the image_url field, and then to make sure it matches with the stored image path, I just replace the storage.
and then to make sure it matches with the stored image path, I just replace the storage. I remove the storage segments in the URL. And so at this point I have an array of stored images from the file system and then an array of used images from the database. And I'm going to compare them with array_diff. So this is going to find the differences between the two arrays. And I'm going to store these differences in unusedImages
between the two arrays. And I'm going to store these differences in unusedImages because whatever is not matching means it's not being used. Alright, so let's scroll down to see the next step. I thought it might be convenient to have a number of unusedImages. So we can tell the user that runs that command, Hey, we found 12 unusedImages, would you like to delete them? And so we save that in totalCount. And if the totalCount is 0, uh,
And so we save that in $totalCount. And if the $totalCount is zero, uh, it means there are no unused images. So we just stop the script and say, Hey, no unused images were found. You are all good to go. If we do get past this point, however, it means that we have more than one unused image. So we are going to tell the user, Hey, we have found 12 unused images. Now this next bit is a little extra,
we have found 12 unused images. Now this next bit is a little extra, but I found that when I had 30, 40 unused images, if I listed all of them, it would completely drown my terminal. And so just for uh, visibility, I thought I'd show the first three if there's more than three and then said and more because the person gets the ID and it doesn't drown the rest of the prompt. So I grab the first three images with array_slice,
and it doesn't drown the rest of the prompt. So I grab the first three images with array_slice, and then for each I output a new line with the name of the image. Now, if you only have one or two unused images here, slice will take this first one or two and then uh, we don't really need to show more, but if there is more than three, if the total count is more than three, then for the remaining we count the total minus the three.
if the total count is more than three, then for the remaining we count the total minus the three that we've sliced out. And then we say plus remaining more. So if we have 12 images, it's going to show the first three with the file name and then say plus nine more. So like I said, this is not completely necessary, but it's a nice convenience for the consumer of this command. So if we keep scrolling down,
Confirm and delete images5:04
for the consumer of this command. So if we keep scrolling down, we actually now do the deletion that we want to do. I thought it would be nice instead of having a --dry mode that kind of pretend runs the command and then you can run it for real to have a prompt that say, Hey, would you like to delete these images that we found? And then if the user says yes, we delete them. And if they say no, we just abort the command.
And then if the user says yes, we delete them. And if they say no, we just abort the command. So here I ask, do you want to delete these unused images? And basically this confirm method here, if the user says yes, it's going to run this code, if they say no, it's going to just cancel the operation. Pretty simple. So if the yes for each unused image, we delete that image from the storage and then we say, Hey, this image was deleted. And once we're done with the foreach loop,
and then we say, Hey, this image was deleted. And once we're done with the four foreach loop, which means we've deleted all the images, we say, Hey, unused image were deleted successfully. Pretty cool, huh? So let's do a quick recap. We get the images from the file system, we get the images from the database, and we compare both with array_diff. Based on that, we are able to figure out how many images are unused.
Based on that, we are able to figure out how many images are unused and then tell the user, Hey, we found X amount of images, would you like to delete them? Yes. And if yes, we delete them. If no, we say operation cancel. I think that's a pretty nice way to handle it. I have no idea if there's a much better way to do this. Maybe there's a Laravel function that just clears up unused images out of the box.
Run command in terminal6:27
Maybe there's a Laravel function that just clears up unused images out of the box. I wouldn't be surprised, but it was a nice little side quest for me to try create my first command in Laravel and do something useful with it. So let's try go and run it and see the user experience that you get out of it. All right, so let me make the terminal window a little bit bigger. And I have just enough space up here to figure out the name.
window a little bit bigger. And I have just enough space up here to figure out the name of the command, delete unused puppy images. So I'll copy that and I will run php artisan and the name of my command. Now I've created a bunch of puppies that are later deleted throughout this course, so we should find quite a few press enter and it has found 398 images. So it's showing us the first three and then 395 more.
and it has found 398 images. So it's showing us the first three and then 395 more. Would you like to delete these? Let's start with no and it's going to cancel the operation. And so let's run it one more time. But this time we are going to say yes and it's deleted all the images, which sounds like a great service for us. Let's verify this by running the task one more time and it should tell us that it hasn't found
Let's verify this by running the task one more time and it should tell us that it hasn't found any unused images. How cool is that? And now to really drive the point home that I'm not lying to you, I'm going to go in the database and delete these three puppies that we've created in the last lesson. So let's go in puppies. And the last three should be, uh, three test entries. So I'll delete these and save the database changes.
And the last three should be, uh, three test entries. So I'll delete these and save the database changes. As a result. If I refresh the app, they're gone. And now I want you to take a second to tell me exactly what's going to happen when I run this comment. We should have three unused images. It should list the top three and then plus zero. But actually it's not going to say plus zero because we only have three images.
But actually it's not going to say plus zero because we only have three images. So the if more than three is not right. So we should just see the three images and say, would you like to delete? That's what I'm hoping for. Let's try it. Alright, php artisan delete unused puppy images, fund three unused images. Would you like to delete them? Yes please. Thank you very much. Oh yeah, as you can tell,
Would you like to delete them? Yes please. Thank you very much. Oh yeah, as you can tell, I'm pretty proud of my little command script. Uh, it's really useful to me. Of course, in the real app where you have delete operation, this is where you would handle the deletion, but there is still the possibility of someone deleting puppies from the database like I've been doing so far. So it's kind of nice to have a manual cleanup function.
like I've been doing so far. So it's kind of nice to have a manual cleanup function. And by manual, I mean automated, which is the opposite of manual. Alright, I will see you in the next lesson. We are done with the refactoring and we are going to keep pushing forward. The next step is going to be the toast notifications. See you there.
See you there.
