[31/38] - Taking out the Trash
3K views
Feb 14, 2023
When we delete an entity mostly things are behaving. However, the Wallpaper image file is not being deleted. Let's fix this using an event listener. https://codereviewvideos.com/course/let-s-build-a-wallpaper-website-in-symfony-3/video/delete-should-remove-the-wallpaper-image-file
View Video Transcript
0:00
Hi, I'm Chris from codereviewvideos.com and in this video we're going to wrap up our untested
0:08
approach by implementing wallpaper delete and you can see that we've already got the
0:13
delete functionality available, this comes with easy admin bundle but our problem is
0:17
when we delete the image file itself is not getting deleted so just to quickly cover off
0:21
what I mean by this, we're going to upload Solana Beach, give it any old slug it doesn't
0:26
really matter and so that comes up we get our preview and what not and if we look inside
0:29
our images folder solana beach is added it's in red there because git doesn't know about it so
0:35
the project is under git so it thinks it's a new file that's just not been tracked anyway if we go
0:40
to delete that it should delete but what happens is the underlying image file itself remains on disk
0:47
which is a problem because when we next go to upload a solana beach say we made a mistake with
0:52
it in some whatever way or if we come back in the future and we found a new picture of solana beach
0:57
but we've got exactly the same file name we're going to hit up on a problem that the file already
1:00
exists so to begin with i'll just delete that file and then we'll crack on with solving this problem
1:05
so we already actually know the solution to this problem if we look inside our app in the config
1:11
on the services we have our wallpaper listener and we listen for pre-persist events pre-update
1:16
events and these are dispatched by doctrine well there's also a pre-remove event so it's quite
1:21
handy and as you might expect this gets called whenever an entity is about to go through removal
1:26
phase now we don't actually control that because as you saw when you click this delete button easy
1:32
admin bundle is going to take care behind the scenes of removing the entity and so because we
1:37
use an easy admin bundle that actually sort of masks where this is going to get triggered normally
1:43
in your own applications you would call that yourself inside your controller action or similar
1:47
so much like we got pre persist and pre-update we go to our wallpaper listener what we're going to
1:52
need to do is add in a new public function which we're going to call pre-remove and the pre-remove
1:58
method is going to get a life cycle event args instance passed in as its only argument and again
2:04
we can do entity equals event args get entity and critically we also need to check if we're dealing
2:11
with a wallpaper instance now we could look at extracting this out into its own private method
2:16
but to be honest with you if i do that it's not really going to make the code any better sometimes
2:21
you just can't avoid the duplication so i'm also going to take this little dot block here pop that
2:26
in and that's just going to make it a little bit easier as we work through it so as we see or as we
2:31
have seen should i say as we've been through pre-update pre-persist if we're not dealing with
2:35
a wallpaper then we would have returned by now so we know at this stage we are actually dealing with
2:40
a wallpaper entity so the first thing i going to do is i just going to set the file to be null so it not actually going to delete the file in any way It just going to do a little bit of tidy up on our wallpaper So that the easy part out of the way The next step is a little bit more tricky and that the process of actually
2:56
deleting the image. So we've been a little bit considerate and we've thought about situations
3:00
where we might move all our images from local disk maybe up to Amazon S3 or similar at a future
3:07
point. So what we don't want to do now is hard code our image file deletion process to just
3:12
deleting from disk instead what we're going to do is abstract this concept out into its own service
3:16
which we'll just call the file deleter service or similar and then we'll hand over to the file
3:21
deleter service to do the specifics of the file delete so let's jump inside our source directory
3:27
at bundle under services we'll create ourselves a new service which i'm going to call the local
3:32
file system file deleter okay i'll just add in the basics here so we're going to need access to
3:39
the file system so i'll just inject that we use symphony's file system component and that's just
3:45
alt return there to initialize the fields saves me a bit of typing and then we'll have a public
3:50
function here called delete which we're going to need to take a path to a file that we want to
3:54
delete and then we're going to call on this file system remove and then as it says we can either
4:00
pass in an array or a string or something that's traversable or we'll just pass in a string as that
4:05
makes the most sense so we'll just say path to file and we'll see where we get with that i think
4:11
there's already going to be a problem as if we think this through path to file is already going
4:14
to need to know that it's on local disk so perhaps we'd need another service to figure out the path
4:19
to the image file and then we'd pass that on to the local file system deleter but we'll come back
4:23
to that in a sec now that we actually have a local file system deleter perhaps we could add that into
4:29
our wallpaper listener but again this is flagging up a problem here because if i now do and i'm only
4:34
tidying up like that there just putting it on new lines because we're running out of space a little
4:38
bit you don't need to do that but what if i pop that in there and then pop in my variable name
4:44
then immediately we've tied our wallpaper listener to a very specific implementation of how files are
4:50
going to be deleted so it's not really what we want to do instead it would be nicer here to use
4:56
an interface and then we can just say actually this is just going to be a file deleter how those
5:00
files get deleted is really at this stage or from the perspective of a wallpaper listener
5:05
it's not really any of our concern so we'll start off and we'll say file deleter.php and we'll just
5:13
add in the basics and again what I'll do is let's take a copy of that and get rid of the use statement
5:18
remove all that local file system stuff and just set this to be an interface get rid of that extra
5:23
space as well don't need any of the construct and that should be good enough so that's our interface
5:29
we just going to say we have one public function on this interface It going to take a string which is our path to a file and that really should be good enough at this stage Okay and then inside our local file system file deleter
5:41
we're going to implement file deleter. We've got a little bit of an error here
5:45
because we expect this to be set to a string, but aside from that, things are looking okay
5:50
So now we can jump back into our wallpaper listener, and we can get rid of the local file system file deleter
5:55
and just set it to be the interface of file deleter, which makes it a lot more generic set that to be a file deleter alt and return on it set up the
6:04
property and we should be good to go and so now inside pre-remove what we're going to do is we're
6:09
going to say this file deleter delete and then we need to pass in some path to the file now this is
6:15
what i was saying a little bit earlier our local file system file deleter is expecting to be given
6:20
the full path on disk and we don't want that concept spilling out into our wallpaper listener
6:26
so for the moment we'll just say we're going to get from the entity just the file name and then
6:32
inside our file deleter inside our local file system file deleter should i say what we'll also
6:37
do is we'll inject a string which will be the file path on disk so again i'm just going to alt and
6:44
return on that set these fields up and so inside the delete function we're going to concatenate
6:49
this file path and then a slash and then the path to the file now of course there are nicer ways of
6:56
doing this we might have a service that we inject here to figure out the path and so on i don't want
7:01
to go too sort of enterprisey and just make this like super abstract crazy system sometimes it's
7:07
easier just to go a little bit hard-coded and come back at a later date and make it a little bit more
7:12
abstract and configurable as needed like don't go overkill at least that's my approach to it only
7:18
make it as complex as it needs to be because as you can probably tell already things can get quite
7:23
complex even when doing fairly basic stuff so we'll add in here a new service definition we'll
7:29
say app service local file system file deleter which is going to have the class app bundle
7:35
service local file system file deleter that takes two arguments it takes the app file system which
7:44
is the file system component provided by symphony or the file system service should i say and then
7:49
we're going to pass in a parameter here and so let's have a look at what parameters we've got
7:52
configured. Well we've got nothing in our parameters yaml dist nor in our parameters
7:58
So how have we done it for our other services then? Let's take a look. So we've just passed in
8:03
a path to web images. That might be sufficient. It really depends I suppose. The parameter is
8:08
quite nice to use. So just looking at that we've got this repeated once, twice. Yeah I guess we
8:15
could extract that out to be its own parameter So we take a copy of that and cut that out and we pass this in as our wallpaper file path or similar We need to come back and do that one in a sec
8:28
So we'll go into parameters.yaml.dist, and we'll say wallpaper file path. So this is the default one that comes with a new installation of Symfony
8:35
So if other developers on your team or whatever spin up an instance of this project, they would get that set
8:40
And then in our actual parameters.yaml.dist, we will do exactly the same
8:45
Now, whether this will work into the future, say if we switch to Amazon S3 or whatever
8:50
again, it would really be a point that we'd need to consider at that point
8:53
But for the moment, it should be good enough to get us to where we need to be. So one sort of crazy thing that we're doing there is our wallpaper file path is a parameter
9:02
And if we look inside our parameters, inside our parameter, we have another parameter
9:07
That's the one that Symfony sort of provides by default. But yeah, parameters inside parameters, all good
9:12
anyway let's get rid of that and get rid of that and then inside our wallpaper listener we also
9:18
have this file deleter that we've injected but we've not told our service definition about
9:22
so our wallpaper listener needs to be given this service let's drop it down there command
9:28
the d to duplicate the line paste that in and let's take a look at our project now so let's
9:34
give that a refresh and i'm going to add a wallpaper in so that we can see this a little
9:37
bit more easily because these ones are named a little bit nice we'll use space one whack in any
9:42
old nonsense as the slug. So let's just check inside our images directory. We've got our space
9:48
one. I want to make sure that when we delete this now, it's gone. Delete. Yep, that's fairly awesome
9:55
So wallpaper set file expects to only ever be given an instance of uploaded file, but we're
9:59
passing in null. So what we need to do potentially is either not null it or go into our entity
10:05
wallpaper. I'm going to set file wherever we are. Set file, uploaded file equals null. And that just
10:13
allows us to pass in a null. And we can say that parameter actually is either an uploaded file or
10:19
a null. And let's just check this. Yeah, so file itself isn't saved off to the database. If it was
10:24
then we would also need to set this to eg nullable equals true, just to make sure that the
10:29
database column for this particular property could accept a null as well. But we don't need to worry
10:34
about that in this instance let's just refresh that and make sure it's not done anything on
10:38
to ward so try again and now we've deleted which looks good let's check yeah it's just gone so there
10:46
we go that's file delete and that pretty much wraps up the crud side of a wallpaper in an
10:52
untested approach all that really remains now is we're going to go through and repeat this process
10:57
in a tested fashion and then finally just adding a little bit of security at the end to wrap us up
11:02
for the first phase of our wallpaper upload system