How to Schedule Emails in PHP - Run Automated Scripts Using CRON - Full PHP 8 Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] sending emails could be a heavy process and could take a few seconds we don't want the user to have to wait while we're sending the emails instead we want to queue up the emails and have some sort of php script to run in the background at a given schedule which then can pick up the emails in queue and send them out before we get started please drop a like and subscribe if you enjoy my content it really helps to grow the channel and recommend these videos to more developers like you i've created a new table behind the scenes called emails which simply stores the subject status of the email text and html bodies metadata and some time steps like created and sent dates i've also created an email model with a queue method that inserts the email in the table it accepts the address object for the to and from variables and this address object is from the symfony package it is easier to work with because we can set the name in addition to the email and so on we're also accepting the subject and html and text bodies of the email now of course in a real application we would accept maybe more stuff but for this example this is enough to not over complicate things then we simply prepare the insert query and we're storing the to and from in our metadata array which is a json column in the table we are also using email status enum to set the status of the email when the email record is created it's going to be set to queue by default if we open up the email status enum we see that it's a back denum with three cases cue sent and failed all right so let's open the user controller class and instead of sending email right away we can instead create a record of it in the database to be sent later so we'll take this piece of code and instead we'll do something like new email queue and we'll pass in the necessary arguments so we need to create a new address object here so we'll do new address and pass in the email which is email and currently we don't have the name so we'll leave that blank and then we need the from email so we'll do address and we'll just hard code this for now to support example.com and we'll just set the name to support next we need a subject which was welcome and then we're gonna pass in the html and text bodies uh we're also going to get rid of this here because we're not sending the email anymore we're just queuing it up so that means that we don't need the constructor so we can remove it let's test this out to make sure that the record is created in the database so we'll open the browser enter some name here we'll enter some email some password hit register and as you can see this was instantly did not take few seconds so if we switch over to the mail hug we see that there is no email here because we haven't sent any emails let's open the code let's open the table here refresh and we see that the email record was created we see that the status is set to zero which means that the email is queued what we need to do now is to create some sort of email script that will be responsible for sending emails so we'll need to create a scripts directory and add something like email.phpscript within it now this script will be running in command line right it won't be running in browser so we need to boot up our application to get access to the config and other necessary objects or items we also need to require the autoloader here so we pretty much need to do the same thing that we're doing in public index.php so what i'm going to do is that i'm actually going to copy this whole thing and paste it in the email.php and then remove the unnecessary parts as i mentioned this code will be running in command line so we don't need anything related to routing so we can get rid of the router and get rid of this we do need container we don't really need the storage path or view path in here so we'll remove that and we don't need anything related to request so now we need to work with our app object to make it work with our command line script because right now it won't work because router is a required parameter one option is to make the router dependency nullable so we'll go to the app class so let's import this go to the app class and we'll make this nullable and set it to null by default we'll also rearrange things here a little bit so we'll move the config above because this will be required and we'll set the request to empty array by default because this is also not required for our command line script there are many different ways we could structure this but we're not building an actual framework so it doesn't really matter in this case another thing that i would want to do is that to move this out of here into some kind of method called boot so we'll create a method here public function boot that will boot up our application and we can set our bindings and everything in here and we'll make this method return itself so we'll return this and if we wanted to get fancy instead of accepting config in the constructor we could create the config within the boot method so what we would do is that instead of doing this in both email.php and index.php we could take this out of here and put it in the boot method and then we would initialize the config here this way then we could get rid of the config dependency entirely and simply create the config property here now because we did that we would also need to move this out because it needs access to config so we'll take this and put it right after here and simply access the config and then what we can do is that within our public index.php we can get rid of this and we can get rid of these two lines now as i mentioned before there are many different ways of doing this this is not the ideal way this is not the perfect way we're just kind of playing around with the code so now we need to adjust our public index.php we need to call the boot method before run and then we'll do the same thing in our email.php so we'll instantiate our container and we don't need to pass anything here and instead of calling run method we'll simply call boot method because we don't need to run the app in this case since we're not in the browser next we need to actually send the emails so now instead of writing the code to actually send the emails here we can create some kind of email service class to encapsulate that logic so we'll do something like container get email service and we'll call send cued emails method now email service class as you remember we created before but we don't have send queued emails method in it so if we go in this class we just have this send method which we used in one of the previous lessons as an example so what i'm going to do is get rid of that and instead create send qdmails method which is going to fetch the scheduled or queued emails from the database table and then send them out one by one let's create a constructor method first so we'll add the constructor here and we'll inject the email model because we need to fetch the cued emails right so we'll inject the email model here and we'll also inject the mailer interface because we need the mailer to actually send the emails let's promote these properties and let's fill in the send qdmails method so we need to call a method on the email model called get emails by status and i'll open that method in a second to go over but we need to pass in the status here so we'll pass email status queue enum case and let's assign that to a variable let's open that method and as you can see it's a simple method where we select all the emails where status is equal to the value of the enum case so we go back here and now we need to loop over each email and construct the proper email object so we'll do for each emails as email and as you remember i copied the snippet from before so let's see if we can find it it's right here let's paste that in and instead of calling this email we'll call it email message and now we need to replace these values so let's start from here subject is simply email subject we're not attaching any files we'll do email text body and html is email html body for the to and from these are stored in the metadata column which is a json column so we need to decode that and we'll decode it into an associative array then we can access the from n2 from the meta variable so we'll do meta from and meta 2. next we need to actually send the email message so we'll access the mailer this mailer and call the send method and pass email message now in addition to sending email we also need to update the status of the email record in the database table and we also need to update the sent at timestamp so we'll do this email model mark email sent and pass in the id of the email so we'll do email id if we open the mark email sent this is a method that i created behind the scenes it simply updates the emails table setting the status to the email status sent value and updating the sent at date time we could also wrap this into a try catch block and if something goes wrong while sending email we could update the status of the email record in the table to failed and maybe lock the exception and so on but we won't do that in this lesson you could do that yourself as an exercise if you want to so we're good to go let's test this out to make sure that it's working what i'm going to do is that i'm going to run the email script manually from terminal so we'll open the terminal run php scripts email.php we wait a little bit and no errors means good news let's open the mail hug and sure enough the email is there so everything looks good if we open the table now this status should be updated to send so if we refresh we see that status is updated to one and we also have the send datetime set correctly now you might be asking what about attachments now we're not going to cover attachments in this lesson but i'll give you a hint and you can work on it yourself as an exercise when creating an email record from the user controller if there is an attachment that you want to send with the email you could save the attachment locally somewhere in the file system or if you're using some storage like s3 or something else you would store the attachment there and then you would save the file path in a column in emails table maybe an adjacent column or something to support multiple attachments or even better you would store them in a separate table like email attachments and then when sending emails from the email service you would pull those records from the email attachments table and then based on the file path column it would look up the physical files and then attach them to the email message so we were able to send email by running the email php script the problem with that is that we have to run that script manually how can we automate that so that we don't have to run them manually this is where cron can help cron is a job scheduler tool for unix systems that lets us run scripts or jobs automatically at specific times or specific schedule the schedule of the jobs or scripts are stored in a file called crontab which is short for crown table each entry or the line in the crown table is the job that we want to run and execute an entry consists of six or seven fields depending on the implementation the first five fields or six fields again depending on the implementation define when to actually run the job so it's the schedule while the last field is the actual command that we want to run we're going to assume the typical implementation with six fields so the first one is the minute value 0 through 59 second is the hour value 0 through 23 third is the day of the month 1 through 31st 4th is the month of the year 1 through 12. fifth is the day of the week 0 through 6 where 0 is sunday and in some systems number 7 could also be used as sunday and the 6th field is the command or the script that we want to run so let's see some examples let's say that we want to execute a script every minute as the risk or wildcard character here indicates all possible values for that field so it means every minute of the hour every hour and so on if we wanted to run every hour on the hour we could set the minute field to zero so now it will run every hour like at 1 pm 2 pm 3 pm and so on we could change the minute field value to 30 which will make it run hourly on 30 minute marks so it will run at 1 30 2 30 3 30 and so on we can change it to run on specific month day of the month day of the week and so on you could also specify multiple values for these fields separated by commas so this for example would run only at 2am and 5am range of values can also be specified using dash so we could do 1-5 for the month field value which will run the script only in the month of january february march april and may now we want to run our email script every other minute we can do that by adding a step character which is a slash after the wildcard followed by an integer so in this case that would be wildcard slash 2 for the minute field which would indicate to run the script in every 2 minutes all right so now that we got our expression we need to add it to the crown table or cran tab file if you were working on a native linux distribution you would be able to type in crontab.e and then edit that file and put your crown expression there but we are working in a docker container so we need to run crons within our container there are a few ways of doing this one is to run cron in a separate container and other is to run it within the same container as our app we're going to run it in a separate container so let's open the docker compose file here and i already created a cron container which uses a custom docker file which we'll talk about in a minute we're mapping the source here in the volumes the same way we're doing for our app and then we're also mounting the log directory for the cron now let's open the actual docker file within the chrome directory so if i open the project here and open the docker directory we see that within the chrome directory i have the docker file and crontab so let's open the docker file and as you can see this is pulling from the php 8.1 fpm alpine image we are using alpine because it already has the cron setup so all we really need to do is to copy our local cron tab into the container and that's what we're doing right here we're copying the crontab file into the etc crontabs root and then we're creating a directory for the log files for the cron and then finally we're running the cron command let's open the crontab file and this is where our crown expression is so this is the crown expression that we just saw and then we're simply putting all the output in cron log so i'm going to exit here and i'm going to run docker compose up dash d build and while it's doing its thing let's open the browser and refresh the page we submitted the same form values so that it creates the email record in the database table so that it queues up the email and if we refresh the emails table we have that second record there now the cron's supposed to run every other minute so this will eventually change to one so if i'm going to refresh as you can see now it already changed to one which means the two minutes is up and the cron automatically ran so if we open the mail hug here we see that the email was sent so this is it for this video hope you enjoyed it if you did please give it a thumbs up share and subscribe if you're not already a subscriber and as always i'll see you next time
Info
Channel: Program With Gio
Views: 23,147
Rating: undefined out of 5
Keywords: php8 tutorial, php course, learn php the right way, object oriented php, full php course, php in 2021, advanced php course, php 8.1, cron, scheduled tasks, queue emails, symfony mailer, php send emails, run crons in docker
Id: 9q1Nt6lHXq8
Channel Id: undefined
Length: 16min 4sec (964 seconds)
Published: Tue Mar 01 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.