Playing
01. Scheduling Actions on Models in Laravel

Episodes

0%
Your progress
  • Total: 14m
  • Played: 0m
  • Remaining: 14m
Join or sign in to track your progress

Transcript

00:00
In this video we're going to cover scheduling actions on models in Laravel. So what do we mean by that? Well basically we want some sort of action, it could be absolutely anything, to be performed on a model at a certain date and time and we want this to be very very accurate
00:15
right down to the second that we want this to be performed at. So what we're going to do in this video is look at an example of scheduling users to be deleted at a certain date and time. So I'm going to go ahead and give you a demo of this. I'm going to mark one of
00:30
these accounts here to be deleted now. Now just before I do that I'm going to go ahead and start up our short schedule which we're going to look at implementing. We're also going to go ahead and run our queue because we're also going to make sure that we queue
00:43
these so we're not processing too much in one go. Okay so now that they're running let's go over and mark one of these accounts to be deleted and of course we're doing this now but this could be at some point in the future or it could be a past date in which case it will always be
00:57
actioned. So let's go ahead and save this out give this a refresh and in a couple of seconds this should be deleted and there we go it's gone. So this is super useful for setting things like blog posts to go live at a certain date obviously deleting users it could be
01:12
absolutely anything but I'm going to guide you through everything you need to get set up to make this work. So now that we've seen how this works let's clear everything out start from a fresh Laravel installation and get everything set up that we need to make this work.
01:24
Okay so I've just freshly created a new Laravel project the only thing I've done is adjust the database settings here and I've also run the default migrations which has given us these tables and of course our users table which we're going to be using
01:38
to demo this. The first thing that we're going to do is just add a column in here a date time which is going to allow us to schedule a particular date and time. So the first thing that we'll do is go ahead and make that migration so we're going to make that out and let's say add
01:54
deletes at to users table and let's head over to that add deletes out to users table so this could be anything it could be when like I said when a blog post goes live anything like that and then of course you'd have another column in your blog post that you would
02:11
set to true based on this date and time. Okay so this is going to be a just a time stamp and let's call that deletes at and really importantly let's make this nullable because that shouldn't always be filled. Let's really quickly fill in the down
02:24
migration as well by dropping that deletes that column and we're good to go. So we can run the migration here like so and that's given us that additional column over here. So let's generate out some fake users so we can test around with this we'll just go ahead and run phpr and
02:41
tinker and we'll use the default factory on the user model to create these out so let's go ahead and say maybe 10 or 20 users and create these out there we go and we now have 20 users in here that we can start to play around with
02:56
so next step is to create the actual console command that we're going to be running to get this working so let's go and run php artisan make command and we'll call this delete users now if we head over to the delete users command here
03:13
let's go ahead and give this a name first of all so we'll put this under a user's namespace and just call this delete and you can fill in the command description if you want to so how are we going to get this working well we're first of all going to go
03:24
ahead and pluck out from the users and let's just go ahead and pull the namespace in here and we're going to say where deletes at is less than or equal to the current date and time now the less than here is really
03:40
important because if for any reason this is scheduled and hasn't been run and the action should have been performed in the past we also want this to work what we don't want here is a really strict comparison by saying equals if your scheduler stops for any reason
03:54
or something else happens you're going to want prior records to be included in this query now what we're going to do is once we get these back we're going to go ahead and iterate through these and perform some action now we're going to be moving this over to a job later but let's just implement
04:09
this here now so we know this is working so into here for each of these we're going to go ahead and grab a user and all we're going to do is just say user delete or in here you could update a blog post like we've already discussed or do pretty much anything you
04:22
want okay so now that we've got our query we can go ahead and run this so let's run php artisan users and delete and of course at the moment nothing should have happened so let's go ahead and mark a few of these or just a couple of these
04:36
to be deleted save them changes and let's go back over rerun this command and sure enough them two users have been deleted so pretty straightforward in the way this works now but what we want is this to happen automatically of course we want to
04:51
schedule this command to happen so we don't have to manually run this all the time so what we're going to do is we're going to go over to the kernel and the app and console and we're going to go ahead and schedule this in here now there's a slight issue
05:06
with this because laravel by default does not allow you to schedule things with any frequency less than a minute so we can say in here every minute we want to schedule the users delete command and that will work now this will work as expected but if you
05:22
needed a little bit more accuracy for example you wanted to check to be run a very specific time frame every minute may not be enough so what we're going to do is we're going to pull in an additional package which is going to allow us to do this so i'm actually going to delete this
05:37
from here and we're going to hop over to the laravel short schedule package let's go ahead and install this and see how we can get this working so let's go down here to the composer command let's come over and pull this in and all we need to do here is run short schedule run
05:54
this will hang and it will just run this at the frequency we've defined now if we come down here we can see how we define this out so we're going to basically just copy and paste this entire method over and we're going to put this in our console kernel here
06:08
so let's go over and just indent this let's make sure we pull in the correct namespace here and we should once this is re-indexed be able to pull that in and we can run commands now every second so let's get rid of this command here this here
06:25
and let's pull in users delete so now this command is going to be run every single second so it will go through and it will be more accurate at performing these actions when we need them of course if you don't need this accuracy there's no point in doing this
06:43
just use the standard every minute command but in our case i want this to be super accurate so the first job is just to run this short schedule so let's go ahead and say php artisan short schedule and run and that's just going to hang there so we can keep that
06:58
running and see what happens so now that we've got that out the way let's go over and just test this out so let's go ahead and fill in the delete column here give that a refresh and it's already gone so obviously
07:10
the now is going to give us the exact date and time now so it would have gone straight away but of course we could do that into the future hang around and it will be deleted as well okay so there's a couple of problems with this of course what we're doing is
07:23
running this command every single second so if you did want to go down this route or to be honest even if you didn't it would be a really good idea to queue this here so we want to queue what is happening inside of here you might be sending out emails in here you might be doing something a little bit
07:38
more data intensive than just deleting a user so it makes sense to implement a job here and queue this so let's go ahead and do that now so let's go over to our console here and say php artisan make job and we're going to call this
07:52
delete user so the singular version of this that's just going to handle deleting our user so what we're going to do is swap this function out here for a short closure and for each of them we will still get the user in and let's just go ahead and actually give the function keyword there
08:08
so we're going to say delete user and dispatch and we're going to pass that user into there now of course you could use a standard closure in here if you needed to do something else at the same time but in for our case this can all go in
08:23
one line okay so delete user the job should queue so we want to make sure this queues and in here of course we're going to go ahead and accept in the user itself like so and let's just make sure we pull the namespace in here and then the action here to handle this
08:38
is just going to be this user and delete and of course we could do other tasks in here we're queuing this now so it doesn't really matter too much okay so now that we've done this let's go ahead and set up queues really quickly i'm going to do this just at the database level
08:53
but of course if you have another queue driver that you want to use that will probably work a little bit better and be a bit quicker so we're going to generate out a queue table let's do that first of all that's going to go ahead and create out a migration for us so we can just run
09:07
phpr to migrate and that's giving us a jobs table in here which we can use to keep track of all the jobs that are running and we're going to go ahead and run phpr to queue work in just a second over in env we need to switch over our queue connection
09:23
and we're going to set that to database that's pretty much what we need to do for our database queuing so now the queue is working what we're actually doing now and we'll just restart our schedule just in case is scheduling these instead of running them straight away
09:37
now the reason that this is really helpful or can be really helpful if we had say thousands of users that all needed to be deleted within the same time frame it makes sense to queue these so we don't overload things you could also make use of
09:53
chunking if that were to be the case so over in your command just here you could go ahead and chunk these by a certain amount we may look at that another time so let's go ahead and see how this performs we've got our scheduler running we've got our queues running and let's go over and schedule
10:09
one of these to be deleted so save that out this is going to take a little bit more time because of course this is now being put into our jobs table and then being processed so it's going to take a little bit longer and it won't have that same accuracy as if we were doing this
10:23
immediately within the closure inside of our console command but it does the job you can see that's being run and it's worked nicely now we do actually have a fail here so let's go ahead and actually check what's happened here
10:37
yeah so this is a really interesting problem that we get when we're using scheduling every single second now as you can see that user that i scheduled for deletion was actually deleted but what's happening is because we're running
10:52
the scheduler every single second we have a slight overlap in jobs so two jobs here to delete the same user were actually created because by the time the scheduler had gone on to the second second so the first second it would have picked that up and created the job
11:10
or dispatched the job the second second it would have done the same because the user was already in the database still in the database so basically we have an overlap where we have duplicate jobs which is not what we want so laravel queuing has a really good
11:24
feature where we can give each of our jobs a unique id what that means is we can first of all implement the should be unique interface and then we can come down here and define out a unique id for each of these jobs or for this
11:42
particular job that's running now what this will do is if a job with this particular id whatever we give this so if we return one this will not be run if two jobs are currently being put into the queue with the same id
11:55
now if we were to just return the number one here of course nothing would ever be duplicated because one is just a hard-coded value but what we want to do is make this unique to a certain thing so in this case the user id so we only want one delete user job in
12:10
the queue at one time with one user id that will prevent that duplicate issue from happening because we're scheduling jobs every single second now it's not advisable to just use a single user id on its own because of course if you had
12:24
another command which were deleting or updating other things and they matched the user's id you could have a blog post with an id of one and you could have a user with an id of one this isn't going to work out so what we're going to do is just prefix
12:38
this just with something that makes it a little bit more unique so the action name and then we'll just append on the user id okay so now that we've done this we shouldn't see any more failed jobs because we shouldn't have
12:51
that overlap and that overlap existed because the user had already been deleted but a duplicate job had been put in trying to delete the same user you can see we've got no query results for model so i'm going to delete that out of the failed jobs table and we're going to try
13:05
again now really importantly when we're making adjustments like this we need to make sure we close off and restart our queue worker we don't need to do the same for the scheduler but i like to do it anyway so let's go over and just try this again
13:16
so let's say now save that out give it just a couple of seconds to push that into the queue and then be run and we shouldn't see a failed job in here because only one job was pushed onto the queue so that is pretty much it of course we've only looked at one example of
13:32
this but if you ever find yourself needing to perform an action on a model at a particular date whether that is set by you or the user or any other process then you can use this pattern to get this to work just to recap we of course created out the
13:47
console command that actually goes through finds the particular query and iterates over and performs the action on each thing but what we then did over in the kernel is implemented the short schedule package to run this every second
14:02
to get a bit more accuracy of course if you're happy with this running every minute and sort of saving up things to batch then you can just move this over to the standard schedule and just run this every minute of course we also went ahead and implemented a
14:16
job for this just so we can push this to the background and not slow things down particularly if we are scheduling things every second and then finally we implemented a unique id because we are running these at such high frequency we don't want any duplicate jobs
14:33
for these particular models being put into the queue at the same time so you can expand on this add to it tweak it but that's the general pattern of how we schedule actions to be performed on models in Laravel
1 episode 14 mins

Overview

Need to perform actions on models at a future date and time? This snippet covers scheduling a command to monitor when an action needs to happen, then dispatching a queued job to handle your action.

This allows you to do things like set a blog post live at a given date and time, or whatever functionality you need similar to this.

Plus, it's actually pretty easy to set up!

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!