Build a Task Scheduler Admin Panel with Laravel | Arturo Rojas | Skillshare

Playback Speed

  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Build a Task Scheduler Admin Panel with Laravel

teacher avatar Arturo Rojas

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

11 Lessons (2h 18m)
    • 1. Intro

    • 2. Create project

    • 3. Database migrations

    • 4. Create task

    • 5. Task list

    • 6. Task edit delete

    • 7. Task toggle

    • 8. Task schedule

    • 9. Task results

    • 10. Query caching

    • 11. Events and notifications

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.





About This Class

Improve your Laravel knowledge by creating a real-life project that uses advanced techniques like task scheduling, events and notifications.

By using Laravel, you not only can improve your development speed but you also deliver better looking projects.

Create a task scheduler admin panel using the latest Laravel Framework.

By the end of this course, you will acquire sufficient knowledge on:

  • Task Scheduling

  • Events and Listeners

  • Model notifications

Meet Your Teacher

Teacher Profile Image

Arturo Rojas


Class Ratings

Expectations Met?
  • Exceeded!
  • Yes
  • Somewhat
  • Not really
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.


1. Intro: hello and welcome to this new course in latter Bill. So let's talk about what we're going to create in the next couple of hours. Imagine that you have a bunch of scripts or, ah, tasks. They can be pH. B or any other kind of script, and you want those tasks to be executed on a regular basis or on a schedule, this kind of scripts or task. Generally, I don't have any you I attached to them. They're just a bunch of scripts or task that will do something in the background. For example, send a bunch of emails or SMS, collect aggregate data for a financial report, or scrape information from a certain website. Something like that. Typically the most popular way off creating scheduled tasks is using crown or cranked up. But if you are familiar with quantum, it means that you have to administer or out, or a text file where every line will be a grand expression, which will define the schedule for the task and then a command line that you will typically execute on a on a terminal. Now the problem here is that when the amount of tasks that you are scheduling start to grow , then you will have to edit a file and edit. If I, with so many lines, can be problematic, or if you work in a team just maintaining a text file and making sure that every member of the team is keeping the file clean and without errors, it's also a difficult task. And that is without mentioning the security problems. Because for every user that is allowed to modify the Quanta, you will have to create user credentials on the server so that they can connect and make changes to a grant. That file, which can also create problems in the future. So what we're going to create here is a user interface that will allow us to create, update and delete tasks. Automated tasks the finder schedules in the lead lauraville. Schedule those tasks and execute. That way you don't have to give anyone access to the cerebral where the contact file is. Instead, all you give them is a user in the past four or user credentials to a user interface. So now that we know what we're going to do, let's go ahead and get started in the next video. We're going to create a new project using composer and then set up a new local website for us to be able to see the project on a Web browser. 2. Create project: Let's begin by creating a new composer project. We're going to use composer create project and this is going to be a lateral project. So lot of ill forward slash a lot of it. Then the name of our project will be task admin in for the version off lateral. We're going to use 5.6. As you can see, it just installed a lot of ill person 5.6 point 33 and it went ahead and created a new Dottie and be filed for us. And now it's just loading all of the dependencies. I'm going to make a pass here because this is going to take some time, so I'll come back once all of the dependencies have been installed. All right. All of the dependencies have been installed. Now will have to do in order to see the default. Welcome page for latter Bill in our Web browser is to set up a local development website. To do this, I'm going to exit out off Homestead using exit and within the Homestead folder, I'm going to edit a file cold homestead dot y a m l or, you know, And that was the incorrect name? See Homestead. Homestead? I missed the homestead out. Yeah, all right. Now, within this file inside the sites section, we're going to map a new site using dash map, and we're going to give it a name that say, desk at mean that local, and we're going to map this to home vagrant code task admin public. So basically the public folder with in your main project folder. And in my case, this happens to be inside home vagrant code. Now save enclose a file. And the last step is to edit a hosts file that will contain a reference to our newly created local website. And in my case, this file is located on their forward slash e t c. Forward slash hosts. So I'm going to use pseudo because to edit this file, I need administrative privileges than women again. You can use any editor you want, and then forward slash e t c forward slash hosts. And here we're going to add a new line, and we're going to start by typing the I P address off the homestead box, which by the fault ISS 192.168 got 10 that 10 and next to it goes the name of the website that we chose back in the home said that Yeah, no foul, which is task admin dot local Saving close the file. Finally, all we need to do now is repre vision. The vagrant box. Now I do want to take a moment to briefly mention a problem that I encounter while I was developing this course, and it has to do with a grin. So in order to boot up your homestead box, you would use vagrant up, and after that you used vagrant ssh to use an ssh connection and logging to your homestead box while I was developing the course, I would usually just turn off my machine, go to sleep, and then the next morning I would come back and then reboot my favorite box. But it happened a couple of times where I was trying to boot of the vagrant box. And then there was an error message saying that the box couldn't be booted because of a timer, and I would get stuck for hours after banging my head against the wall. For a long, long time, I discovered that the issue was that I was not using the latest version off vagrant. So to fix the problem, all you have to do is come to this website vagrant up that come forward, slash downloads and download the latest version off vagrant for your operating system. In my case is Marco is I don't know the installer run it and then come back to the terminal . First of all, make sure that the vagrant boxes not running by using vagrant hot and then used a grin up again. And at least for me, that solved the problem every time. So if you face a similar issue, don't bother looking for a solution and looking for lock files or anything. Just go and download the latest version of Vagrant. Run the installer and try again. All right. With that out of the way, we have to re provision the vagrant box because we made changes to the home state Are Thiemo file. So we're going to just vagrant reload. That's tush provision now that the machine has been re provisioned or we have to do is go and test it out in a Web browser right back in our Web browser or we have to do is type in the name of the website that we chose, which is that's Cadman about local. And if everything went well, you will be received with the the fold laudable welcome page, which is this one? All right, that will be all for this video. We have a new project and also we configured our local website. The next thing that we're going to do is work on our database. In the next video, we're going to create a new database to work with, as well as some migration files that will help us create the tables that our project is going to use. 3. Database migrations: let's create a new database that we are going to use for our project. First of all, I'm going to jump back into Homestead using vagrant. This is a church. If that doesn't work, always make sure that your vagrant boxes running first using vagrant up. And now that we're here, I'm going to use my SQL to create a new database. So I'm going to use my SQL minus you for user. And I'm going to use the full user, which is homestead and then dash p for pass work, which is going to ask me to type in the password. And in this case, the default password is secret. Now that we are inside my SQL, I can create a new database using create that a base in Let's name our database desk. I mean, DB and that's it. Our new database has been created. I can exit out of here now. All I have to do is configure my project to use the status and that I'm going to do inside my editor. My editor of choice will be PHP storm, but you can use any other every third that you prefer. So here I created a new project and open my task at mean folder and inside my project folder. I'm going to open the door gmv file in order to set up my new database here. All have to do is look for the DB connection here. This one will remain the same. We're going to use my SQL, but we're going to change the DB database and make that task. Admin Devi. We can leave the user name and password as they are right now, and that'll do the trick. Now we have our project configured to use her new database. Next, we're going to create a couple of migration files that will help us define the tables for a project and talking about tables. Let's discuss what are we going to need? We're going to use only two tables for this project. One table is going to handle or store all over tasks and the task information like description, the command that is going to be executed for the task as well as thes schedule. We're going to use grown expressions in order to schedule our tasks and also we're going to need another table to store the results off the execution of those tasks. Maybe we can call it something like task results or something, and I want to store. For example, when was the last time the task was to run or executed? How long did it take, as well as the output of that execution, if any, so to create our database migrations? Let's go back to the terminal, make sure that you are inside your project folder. So let's create a new migration. And we do that by using PHP. Artisan Make migration. Now, since we are working with in Homestead, we have a short cut where we don't have to type Ph. B. So whenever you see me type artisan, what is being executed is actually PHP artisan, but we're just making it shorter. So to create immigration again, we type artisan make migration in. Let's call this migration, create tasks table, and we're going to use the option create in the name of the table will be asks this willing struggle edible, too. Scaffold some of the code in the migration so that we don't have to type too much. All right, our migration file was created successfully. You can see the lateral pre appends eight timestamp to the file name so that he knows which migrations have to be executed. First, let's go back to the editor and let's open our new migration file, which is located in database migrations. Here we can see a couple of migrations that come already with Lauraville, and they will be used when we scaffold are authentication system. And this is our new lot of immigration here. Create tasks table. Let's open that one up. And as you can see, this is a class definition that extends migration, and it has two methods up and down. Up will be executed to create the table, and down will be executed when we are rolling back the migration and will effectively delete the table or drop it and scenes we used the create option lateral already scaffold. It's some of the code for us, for example. Here he's using schema facade to create it, ask stable and passing down a blueprint to that table that we used to define the table columns. And he already added the I D column, which is an integer and outer increment column, and this is actually the primary key as well. And he added, this time Stamps column, which basically will create two columns to daytime columns in our table that will be handled by level and will contain the date and time when every record is created and updated. Let's start defining all of the columns that we need for the stable. First, I'm going to need a description column so that I can give my tasks meaningful descriptions . So I'm going to use stable string. This will be a string column called Description. Next, I'm going to need a column to store the command that will be executed for my task. So, table, this will be also a string command. Now I'm going to need a column to store the expression or the crown expression that will define the schedule for my task. So again, table string expression in this field, I'm going to define it so that it is NeuLevel and later on, if I find there is a new value for this column, I will simply the fault to five stars, which is the fold crown expression. If you're not familiar with crown expressions, don't worry, I will give you a brief overview and also point you to a great website that you can use to learn everything you need to know about crone expressions. Now the next column I'm going to use to Dennett eight if the task is active or not. So this will be a Boolean column. And let's call it he's active and by default, I want all of my tasks to be active, so I'm going to give this a default value off. True lateral also offers me the option to prevent tasks from overlapping each other. That way, if one given task takes too much to execute, it is not going to overlap the next execution off the same task again. This is an option that is provided by level when I am creating the schedules for the task. So I want to have a field on my database that will give me the option to set this on or off So table bullion and let's call it don't overlap. A lot of ill by default sets these two false, so I'm going to do the same. The folk false lateral also provides one other option that will prevent my tasks to run when I am doing maintenance on my server. In that way, the execution of those tasks will not interfere with any maintenance tasks currently executing in the set of it. So I want to have the same options on my table here. So table bullion and this one, we're going to call run in maintenance level sets this to false by the fault. So let's do the same. The folk false. The last column I'm going to need is a string column that will hold on email address, which will receive a notification every time the task is executed. So let's say table string notification email and this will be a knowable column. So if there is no value for this column, it means that we don't want to be notified. All right, I think that's enough for our task stable. Let's move on to our task. Results stable. Let's go back to the terminal and create a new migration using artisan make migration. Let's call it create task results table and again, let's use the create option in the table. Name is desk results. The migration was created successfully, so let's go back to the editor and open up the new migration file, which is created. That's results stable and let's start defining the columns for our new table. First, let's add a column that will serve as the reference to our task stable So table, this will be an unsigned indigent. And let's call it Task I D. Since this is a small project, I am not going to create any foreign keys. So just know that these columns is actually a foreign key to the task stable. But if you want to be thorough, you can go ahead and create a foreign key. See if you want to, and you can do that using table foreign task idea. And just make sure that if you're going to use foreign first, you have to define the column. So again, foreign Task I. D. And then the column that is referenced by this foreign key. So references I D and the table where this idea is located, which is desks So basically to create a foreign key, use foreign. The foreign key column knee the primary key that references and on which table that key is located. But again, I'm not going to do that, since this is a simple enough project. Next, I would like to have a time stamp column that will hold the time when the task was last executed. So table timestamp and this is time stamp without the S, which is different than time stamps, which will create two different columns. So, time stamp, Let's call this ran at and for the value. We're going to the fault to the current time stamp for when the record was created. And we can do that by using use current. That way we don't have to provide a value for the column. It will just use the current time stamp on the creation of the record. Next I would like to store. How long did it take for the task to execute so table and to keep things simple? I'm going to store these as a string. Let's call it duration. And Leslie, I would like to have a column to store the results or the output of the task execution. Since I don't know exactly how long that text is going to be, I'm going to use table long text and let's call it result. Okay, I think that sina for the stable as well, so now is the time to go ahead and run this migrations so that tables will be created. Let's go back to the terminal and to run our migrations. We simply type artisan, my great. And as you can see, he run the tasks and the desks results. Stable migrations, as well as the user's and password resets migrations. These two will be used later for authentication. Let's go ahead and log into our database to see our new tables. To connect to our database. I'm going to use a program called Sequel Pro. But again, you can use any way you want to connect your database, even the my SQL command line. If you like to here, I'm just going to connect to our Homestead local database. If you want to connect to our homestead local database from outside Homestead, all you have to do is point to the 1 to 7.0 dot zero host. And for user name and password. You can use Homestead in Secret, which are the default, and for the port you have to use 33060 I'm going to choose our newly created database, which is task at Mindy B, and you can see that we already have our tables created. Here's the task. Stable task results, password resets and users in the migration stable that lot of a created in order to keep track of all of the migrations that have bean executed in the next video, we're going to start feeling up this task stable. We're going to scaffold RLF Indication System, which is very simple and that will allow us to add new users. And with those users we will be able to log in, and then we're going to create a form that will allow us to create new tasks. 4. Create task: Now that we have our project in our database created, it's time to start working on the U I elements of our project. First, we're going to start with the authentication. We can generate our entire authentication system by simply using Artisan make South, and that's it. We now have a new authentication system created for us. Let's go check it out when you access the lateral default. Welcome page. Now you have an extra tool Ings up here. One for logging, one for register. Let's check the log in one. Here we can see a log in page that little bill scaffold it for us. And it's not just to you why everything around it works. They forgot your password mechanism and also logging indefinitely using the remember me option. Right now, we don't have any users in the system, but that's no problem, because we also have the option to register new ones. Let's call this one something like, I don't know John Doe for the email address. Let's say John Doe, that test that come for the past wordless. Just used the word secret has confirmed that again Secret in Click on Register. And just like that, we created a new use it in the database in lateral also logged this in automatically and takes us to the home page up in the right corner. You can see the name of the user that is locked in, and also you have the option to look back out. And with all of this now we have a great foundation that we can use to create our own you elements, starting with the form that will allow us to add new tasks. But where do we start? There is not really a rule about where should you start coding. Personally, I like to start with the routes. Then I move on to the controllers and finally to the views. Let's go check out the routes filed. The latter will already created for us. If we go back to the editor, you can find the routes file inside these routes folded over here, which contains a bunch of route files for different middle where but the one that I'm going to uses the web dot PHP. Now, this file already contains a few routes that were created by lateral. The 1st 1 comes by default and is the one that show US deed fault lateral welcome page. The second and 3rd 1 were generated by letter ville. When we scaffold at the authentication system, this one right here will create all of the routes necessary for logging in out, resetting your password and registering new users. And this one right here is the one that takes us to the home page, which, by the way, is protected by authentication. So if you try to access this one without being locked in, it will take you back to the log in page to enter your credentials. Now let's go and see what routes are available to a project with only these few lines of code. You can do that by going back to the terminal and type artisan route Least here you can see a bunch of routes that are used to show you your home page, logging, log out, password reset and user registration. All of these routes were created by a lot of ill, or they came with lateral by the fault, which is the case of the fault. One you can see, for example, the get request for the Logan you Errol. He's named looking, and it's handled in the logging controller at the show, logging for now. What I would like to do is to group all of my tasks related route into one single controller. That is all of my routes for creating updating the leading and show in my tasks. A quick way to do that is by using a resource room. Let's go back to the editor and down here, I'm going to create a resource route using route resource. And all of my routes will be grouped on their tasks and will be handled by a single controller called That's Controller. Now let's see what happens when we tried to lease their routes again in the 10 minutes artisan route. Least again. Lateral tells me that the tasks controller has not been created yet, as you can see in the error message over here, which makes sense. So now that we have a resource route, what we need is a resource controller. We can create one real quick from the terminal using after send make controller and add the dash dash resource option. Oh, I forgot the name of the controller, which goes right here. Artisan May controller. Let's call it tasks controller bash Dash Resource, and now our controller has been created successfully. Now let's try and leads the routes one more time. As you can see now, we have a bunch of new routes, starting with tasks. Get, which will be handled by the task controller at the index method and is going to be named, asks that index. We're going to use this route names later on in our project to generate links. We also have a route for tasks create, which will show us the form for creating your routes. And that's the one that we're going to use right away, as well as some other routes for showing updating, destroying an editing tasks. Now what would happen if I tried to access this route right now from the browser? Let's try and type it in up here. Tasks create. All we get is a blank page, which makes sense because the method that is handling this request in the controller doesn't have any code or logic to it. In fact, let's go ahead and check it out. Let's go back to the editor and we confined our newly created controller Inside the up http folder inside. Controllers down here we can see a class name asks Controller, Just open that way. This glass already have all of the methods that are required for handling all of the resource routes that we saw earlier. We have index, create, store show, edit of date and destroy. Now the one that we're tryingto access by use in the tasks create route is the create method. And as you can see, the method is empty. But what I would like to do is to try and show the view that is going to have the form to add new tasks. So it'll look something like returned view and let's call this view, tasks not create. And let's leave it like that for now. What happens if we go back to the browser and try to refresh with this new life? Now we get a never message saying that the tasks that create view was not found. So let's go ahead and create that view real quick. Now I want to use the home view, which is home. I want to use this as a template to create my new view. So what I'm going to do is go back to the editor and inside the resources these folder I'm going to create a new folder here could tusks. And inside that folder I'm going to create a new PHP file and I'm gonna call it create that blade that PHP. Now I'm going to copy the contents of the home that blade PHP view by simply copying all. And I'm gonna pace that inside my new view. And let's replace this Ah, Tex here with something like bad form here and instead of dashboard lets at a title saying , Create new desk, something like that. Let's go back to the browser and refresh to see our new changes. Let's go back to tasks create. And now we're presented to our new view with the creating task title and the at form here text. Now all we have to do is start mortifying this form and at all of the fields that we want in order to create a new desk. So let's start with a form and the action of this warm. It's going to be route, and for the name of that route, let's go back quickly to the terminal and see which route is the one that handles the post method toe the task throughout in that name will be tasks about store and that will be handled in the tasks controller in the store method. So route tasks that store and we have to make the method to be post and for the content of this form. First, I want to make sure that we have a couple of hidden fields that lot of always needs whenever we're making post requests to the set of the 1st 1 is called the C S. R F Field, which stands for cross site request forgery field. This is because lateral comes by default within middle, where that will make sure that our side is protected against cross side request forgery. All this does is to render a heat and field with a talking that level has in session. And that token is used to validate that Whoever requested this form is the same person making the post method back to the server. OK, now it's time to add a few form fields to create our new task. I'm going to be using bootstrap four classes throughout the whole project. If you're not familiar with bootstrap, for you can read all about it in its documentation, which is available in this webpage here. Get bootstrap dot com. You can go into the documentation and read about all of the classes, components and everything that is available to bootstrap. The good thing is that lateral already comes packed with bootstrap, so you can start using it out of the box. So let's start with a DIF class form group here. I'm going to need a label, and this will be the label for the Description field, and now we need the input, which is going to be type text class for control. Then we need a name, which is going to be the same name off the column in our task stable, which is description. This will make it a lot easier year to create a new task when we're handling the request in the controller. OK, let's see how that looks back to the browser and let's refresh All right, it's starting to look good. Now we need another feel for the command, so I'm just going to copy this. Dif faced it down here, change the label too, command and also change the name of the field to command. And since we're here. Let's keep adding fields. For example. We need another one for the grand expression. Soul has changed the label to grand expression, and the name will be expression. And I'm going to add a D fold value to this field, which will be the default Cron expression, which is five stars. Next, we're going to need a couple of check boxes for our bullion fields so it's adding you Deve class form check then at an input class form check and put. And this will be type checkbooks. The name will be. Don't overlap with a value off one which Dennett. It's true in the case that the check box is selected and right below the input, we're going to add a label with class form check label in the label text is don't overlap. Finally, we need another checkbooks for running in maintenance so that input name he is run in maintenance. And let's change the label text to run in maintenance. All right, let's see how this looks. Refresh and there you have it. It's starting to look very good. We have all of the fields that we need and all we need. Now it's a button to submit this form so right below here just before the form in tactless Addy button class B TN b t n primary This button will be off type submit and the text will be save or actually create desk. I would also like to have a cancel button just in case I changed my mind and I don't want to create a task anymore. And instead of a bottom, I'm just going to use a link So a and I'm going to give it the same class as the button b TN But instead of Bt and primary, I'm going to use Bt and default which will make it look gray and the link route or the Ural for the link will be route desks which will take me to the tasks view that will show me a list of all of the tasks that are created in the database in for the text. Let's say cancel. Okay, let's go ahead and see how this looks. Refresh. I think I put the name wrong. Let me go back to the terminal in Let's see. Oh yeah, right. It is called tasks in this so back to the editor Let's fix that. Tasks that index. Okay, refresh. All right, there you go. I have a button, and this one doesn't look great. And that's because class that I was looking for, it's actually Bt and secondary. So DTN secondary. There you go. Let's refresh again Now is looking better, except that I really don't like to have my buttons aligned to the left is more of a common practice to have them aligned to the right over here. I think the simplest way to do that is to add another class heared both buttons, which is called Float Right. I'll add that to both buttons and let's see Well, that worked. Kinda seems like it's just sticking the buttons together, and I really don't like that. So let's try something different. Instead of flowing both to the right. Let's try and add another Dave here, Class ro and then another dif class coal MD for medium this place and let's make it 12 so that it will extend from here to here. It will take all of the space down here in thes card over here into these class and going to another class gold text, right? And I'm going to take both my buttons and put them inside that day. If and this removed this float right class and let's see how that looks all right, much better now. What would happen if I tried to send this asset is right now. If I click the create task button again, I would be presented with a blank screen because the method that is supposed to handle this request, it's empty. So let's go ahead and change that. Let's go back to the editor and open the tasks, control it and look for the storm effort here. What we want to do here is to use the model class that we haven't created yet by the way to create a new task. And it's a simple as saying task create and we simply call request. Oh, again, this task model has not been created yet. We have our tables, but there is no models that are associated with those stables. But this is what I would like to have right now. So what happens if I go back to the browser and we try that store request? I'm going to re submit the form, and it's telling me that the pass class has not been found. So now what we need to do is to create a model class. Luckily, that is very simple. Well, you have to do is go back to the terminal and use partisan make model in Let's call it desk . This will create a new task model inside the APP folder right here. Now let's go back to the browser and re try one more time resubmitted form. It's giving me the same ever. But this time is because I haven't included this task class inside my control it. So let's go back to the editor and up at the top. Make sure you use up desk. All right, let's go back to the browser and resubmit. Now. This error is kind of interesting. It's telling me to. Ari underscored talking to the fee level property to allow mass assignment on the task class. So this is basically a mass assignment issue. Now, remember that CSR airfield that I mentioned before that is coming with the request, but that is not a field in our database table. So what we have to do is to tell lateral what are the fields that are table except and that goes inside this fee level properly. So let's go back to the editor and open the task model. And inside the class, let's define a new protected fee level property, which is simply an array off field names that are accepted for mass assign. So here we have description, command expression, don't overlap and run in maintenance. And I'm noticing that we forgot to add the notification email field. So this added here first notification email and let's quickly go back to the form view and add a new text field over here, maybe below the Koran expression. I'm going to copy this basted below email notifications or rather email address. The name will be notification email and ah, we can get rid of the value. Okay, let's go back to the browser. But instead of resubmitting, I'd like to go back, refresh and start actually feeling in the data for my new task. So for the description, let's pretend that we have a script or a task that is going to send a bunch of emails. So let's call it send emails in for the command part. This is what you usually will do in order to execute that command or that task in the terminal. So for the sake of the test, let's just pretend it's an artisan command. So be HB our descend email sent like this, and we're actually going to create these artists and command later in our project. I'm not going to change the crown expression, and I will not have any email address on this task. Everything else remains the same. So if I click on the Create Basque, I am greeted again with a blank page. But in this case, something different happened because we executed this'll line right here, so we know there is a new record in the database. We simply didn't return. Anything in these storm is. So let's do something real quick. Let's go back to our database, and here I have my tasks table. I'm going to refresh the contents and there you go. We have one record that we just create it, but I'm going to delete it real quick because I want to fix that controller method in order to return me back to the least off all the tasks in my database. So I deleted that record. Let's go back to the controller and once we have created this task, I want to return a redirect and we're going to redirect again to a named route, which is going to be Tasks Index. And I would like to send a quick message saying that my route was created and if you notice in the template or the view up here, we have this if statement saying that if there is a status key in the session, he's going to render these alert here and is going to be a green alert with whatever value there is inside that status key in the session. So we're going to make use of this in the controller by simply saying with and this will quickly flash of value to the session that will be used next interview. Whenever the value is used, it will be removed from the session so the key will be status and for the value we can say something like Basque created. Now let's go back to the browser and from here I can simply reserve meet the form and it seems like it did the same thing. But in reality, what he did was to redirect me to this tasks round, and to prove it, we can try and refresh. And as you can see, it didn't try to re submit the form because we are no longer in the form post request. We're now in the get request for these route in. The reason that is empty again is because the method that handles this route he's empty. But if we check the database again, you can see that the record was successfully created. So what we need now it's a view that will allow us to see all of the creative tasks in our database, and that's what we're going to work on in the next video. 5. Task list: now that we have a way to create new tasks in the database. I would like to have a place where I can see all those tasks listed in my user interface, and that's what we are going to work on right now. Just head back to the editor. And if you remember from the last video, every time we create a task, we immediately redirect to this route Tasks Index and we send a status message Task created . So let's work on this view. Once again, I'm going to use the home view as a template. So first of all, on the resource is views Tasks is create a new PHP file in Let's call it index that blade that PHP Now let's open the home that blade of PHP. Copy everything and paste it here under index dot blade of PHP For the heather, let's use something like tests, and let's remove this for now. Now that we have review, let's go back to the control it tasks controller and look for the index method, which is up here here. I'm going to return view tasks index. Now let's head back to the browser, and if we navigate to us can mean the local forward slash tasks. We should be able to see your new desk. But if he noticed from these two links over here, we're not locked in. Which means that everyone can access the task list and also the that's creation form. So let's take this opportunity to protect our controller behind authentication. Let's go back to the editor. So all I have to do is to create a constructor function, public function, construct and inside it. All I have to do is say this made aware out. Now if we go back to the palace said and refresh this, we should be redirected to the log in page. All right, let's slogan using our test user. And now we are taken back to the task least, but this time we see they used her name and we are locked in. We're going to display the task information on a list here, so let's create a table inside this card over here. Let's go back to the view and right over here let's start by creating a table class table, and this is going to have a table heather and a table. But on the head there were going to have, first of all, the description of the task. And what other information could we display? That it's useful? Well, for example, we could have the last time that the task was executed. So something like last run as well as How long did it take in average for the test to execute? So something like have a dish run thing? This will be calculated using all of the task results later in this project. I would also like to know when is the next time that the task is scheduled to execute. So next run, and I believe that should be enough for now. Now let's move on to the body of the table, and I'm going to reiterate over a list of tasks. So for each asks US task, and this is going to come from the controller and for each and here I'm going to need a row , and for each row I'm going to display the task description and also the last time that task was executed. Now there is no column in the task stable that holds this information. So for now, let's just pretend now we have something in our task model that is called Last Run. And in just a moment we will take care of at in this custom attributes to the model. And in fact, we can do the same for the other two columns for average runtime. Let's pretend we have a custom attributes cold average run things and also one more custom attributes. Coat. Next run. Now let's go back to the controller so that we can provide this asks, least into the view back to the index method. Let's extract all of the records from the task stable, using task Oh, and then past that into the view in a viable cold stuff. Now let's go back to the browser and see how it looks. As you can see, we got a description out of the database and scenes. There are no custom attributes for last run, average, run time or next run. It's simply returns a new body, which is fine for now. But let's start working on some of these custom attributes. For example, let's try creating the custom attributes for the last run physical back to the editor and open the Basque model the way you create custom attributes for your model. It's simply by creating a public function. And if, for example, we want to create a custom attribute for last run, all you have to do is name this public function, get last run hatchery. So basically all you have to do is at these get work at the beginning and the word attributes at the end in just Camel case the whole thing. Since we don't have any records for tests, results were simply going to return, not applicable. If we go back to the browser, can refresh you see that now instead of a no value we can the return value from the custom attribute. Now there is a custom attribute we can actually work on, and that is the next run, because we do have the crown expression for our task. So let's go back to our task model and let's create a new public function. Get next run Patrick Butte. Now how do we parse the crown expression and transform it into a date object that we can then form it into a human readable string? Well, it's easier than you think. A lot of ill comes with the package cold Cron expression and we can use it out of the box like this Cron expression. And don't forget to include that class up here with use grown back slash crown expression. In these crown expression for sad has a function called factory to which we only have to pass a crown expression string. So to this we can pass this Cron expression or actually expression. But since the field is knowable in the database, let's make sure that if there is no value, we simply default to five stars it. Actually, instead of having that in here, let's create a private function down here, maybe private or no, let's make it public, and this will be something like Get krung Expression and here I can simply return these very same thing from here. So instead of this, let's just call this get growing expression. So this will create a crown expression object to which we can use the get next run date function. And one thing I know is that the resulting data object will be in UTC for so to apply a time song to the date, all we have to do is failing some of the parameters here. For example, the current date in time is just default to now this other parameter called ends. Let's just leave it at zero, which is the default the next one called Allow Current Date. Let's just leave that it's false and finally, we can pass a string with the time zone that we want. So for Central Time Zone, let's use something like America backslash. She can go. If you don't know which string to use for your time zone, you can refer to the PHP Documentation page on their manual forward slash English forward slash time sounds dot PHP And here you will find a least of all of the accepted time zones for the different regions like Africa, America, Arctic, Asia, etcetera, for example. In America, I know that the central time zone is Chicago, because down here in one of the comments, you have a handy reference for all of the U. S. Time zones like Eastern Central Mountain Mountain without daylight savings time, etcetera. And actually, I just noticed that I'm doing it wrong because on my editor, I am using a backslash and it's actually a forward slash now that I have a daytime object I can use format, and I would like to have these in a year, month they our in a 12 hour format minute and then a AM or PM, and for minutes it's actually I know him. Okay, and let's make sure we return all of this. Some. Let's go back to the browser and refresh to see this in action and there you have it. Now we have a date in time for the next run of this task. In all of that, based on Lee on the crown expression from the database in the next video, we're going to create a form that will allow us to modify the information off our tasks. 6. Task edit delete: So, in order to edit the information for our tasks, the first thing I want to do is to turn this description into link that will take us to the new form that we're going to create. So let's go back to the editor real quick and open the index that blade of PHP file. And right over here I'm going to add a new link in that link will take us to route, asks Edit. Let's take this and put it inside a link back to her brother and refresh. So we haven't never here saying that there is a missing required parameter for the route tasks at it. Let's see what that's about. Back in the terminal, let's use artisan route least one more time. And if you look at the task added route, which is this one, the route is expecting this parameter called task, which is supposed to be the task I D that we're trying to edit. In fact, if we go back to the control of the and It method, which is down here, is also expecting an I d parameter. So the round function is expecting a perimeter with the task I D So let's send that with task. I d back to the browser and refresh. Okay, now it's working, and if you click on it, it's taken us to these You around. Where to is the idea of the record that we're trying to edit again? Since D function is empty in the controller, we only get a blank page. So let's go back to the controller real quick. And the first thing I want to change is instead of receiving a number as a parameter, which will be a to In this case, I want to use laterals Implicit route model binding in order to receive the actual model record from the database. All I have to do is type hint the past model and let's fix the comments over here. Swell. And instead of calling it I d. I have to call it exactly as the route parameter, which in this case, is task. So with round model binding instead of receiving the I D off the record label will take the idea that it's passed as a parameter fetched the record from the database in return, an instance off the task model. In that instance, it's already field with all the information from that record. So here all they have to do is then return, have you tasks, Eddie, and then passed the task down to the view. And now, of course, we need a view. Luckily, this form is very similar to the one we used for creating record. So I'm just going to use that as a template and modify a few things. So under resource is views, tasks, knits copy the create not blade of th being based it again here and rename it Edit that blade that PHP this opened that one. And for the heather, instead of creating a task, let's say headed task now moving down The action of the form is actually desks update. And if we check the route list again, the update method here is expecting a food request or a patch request. A post request will not be identified and will actually return an error. Luckily for us, a lot of offers a very simple way to mimic a food or patch requests within a police request . All you have to do is at something called Method Field, and for this case, we're going to use a sport request and all this is doing is just rendering a heat and feel the lateral can use to identify and mimic a put request when it's trying to handle the route. Now all we have to do is feeling all of the inputs with the information from our task. So for the description input that said the value off task description Same for the command Value Task Command. Now the value for the crown expression is going to be either task expression and if there is none available with their fault to five stars, which is the default current expression Now we moved to the email notification. So value Basque E mail or actually notification E mail? No for the two check boxes. What I can do is right over here at the end. I can say task. Don't overlap. If that is true, I will render the string checked. Other wife empty string in the same four run in maintenance task run in maintenance. If that is true, then mark the checkbooks has checked otherwise empty streets and finally, for the labels of her buttons instead of create desk, let's say update task and the Council one can say the same. So let's go back to the browser in. Let's check this out. I'm going to refresh again and missing a required parameter. But this time is for the tasks update, and I know where the problem is. Let's go back to the view. Open the action for the route, the tasks up their route. It's also expecting the task I d. So ask Heidi Refresh again. And as you can see, our form is rendered properly with all the information available from our record. Now let's make a few changes, for example, has changed description to something like updated. And let's change the current expression so that instead of running every minute, let's run it every third minute, which is basically saying around the task every three minutes. Now, if you want to familiarize with all the different expressions of a Level two chron, you can access a very handy page called Cron tab dot guru. This page has a very big input where you can practice in, analyze different expressions, for example, that one I just used. I'm going to change these two star and here can say star forward slash three, and it's telling me here that the task will be executed every third minute. It also tells me when is supposed to run next. It has a reference to all of the values that can be used in every star or every field of the expression. Some very nice examples down here in the examples page, where you can see different examples off grown expressions like every minute, every day, every Sunday or every day of the week. Every 20 minutes you can check all of those. If you click on one, for example, every 10 minutes, it's going to give you the Koran expression of here so you can use that expression for your scheduled tasks. So I recommend you have these side handy if you're constantly dealing with crown expressions but back to our browser or to our website, let's say that this task I don't want it toe overlap anymore. And again, if I click on the of the task, all I'm going to kid is a blank page because the method on the controller doesn't have any logic toe. So let's go ahead and fix that. There's open the tasks controller in its head over to the update method down here again. I want to use Route Mortal binding. So ask task and let's update a comment as well. In a simple way to update our task is by simply saying task, feel, request all and then se. So all I'm saying here is feel my task with all the information available from the request and thanks to the mass assignment available property, it's only going to take the fields that are relevant to my task and then save that record back to the database. Once this is done, I want to be redirected back to my task, least so return redirect to route tasks index in its past. A status message. So with status Dusk updated. All right, let's give that a go back to the Roeser since I'm already here. If I resubmit or refresh the page, I'm going to resubmit form. And as you can see, the description and all the information that we provided West successfully saved back to the database and we have a nice notification and stop that says Ask updated. Now, since we're here, let's add real quickly away for me to the lead a task. Let's head back to the view. Let's add another column in the Heather. Let's call it the elite and down here, let's have the corresponding column in what I'm going to do Here is at another form. Tasks destroy, which is the route for the leading tasks. Now this form needs to be a post form, so method post and also I'm going to give it a ni de. So I d Let's call it the elite form. And in order to make this I d unique, so I can reference it later. I'm going to append the task I d. So task Heidi. Now inside this form, I have to make sure to pass the CS out of field and also a method field to simulate a the lead request. If we check the route least again, you can see that we have the tasks destroy route. Here it is expecting a parameter. So let's not forget about that, and it only accept the lead requests. So the route destroying is expecting a parameter, which is the task I d. So task I d. Now the next thing I'm going to do that are a lot of ways you can do it even better, but in order to keep it simple. I'm just not going to use any J query libraries or anything. I'm going to just simply JavaScript. I'm going to create a button off class bot N b TN s m to make it a little bit smaller and BT and danger to make it red. This button is a type submit or actually not submit button because the submission of the form will not depend on the button but on a JavaScript event on this button which is on click. And when that happens, I'm going to say if and this is JavaScript only confirm and I'm going to ask, Are you sure? And if the user clicks on Okay, then I'm going to say get element by i d. And passed the idea of the form in this case, the lead form and the task i d. And on that form, I'm going to call suddenly and for the label of the button, let's call it the league. So basically what I'm doing here is using plain javascript. I'm presenting the user with choice to cancel the delete by saying, Are you sure you want to delete this record any of the user clicks on. Okay, then I execute this line here which will gather the form identified by the lead form in the task I D. And then submit that for now. Before we test this, let's go to the controller. Find the destroy method. Let's updated as well with round model binding and here I can simply say task delete in. Once that's done, I want to be redirected. So return redirect route tasks index with the status. Ask the leader. All right, let's give this a try. We go back to the browser, there's refresh. Now we have a nice delete button over here, and if we click on it, you get this confirmation message so you can either confirm or cancel. Let's go ahead and confirmed by clicking on OK, and as you can see, the task has been deleted from our database. There are a couple of things that I still want to do in this view. For example, now that I have no tasks in my database, I realize I have no other way to create a new task order than typing the euro myself for the create form. So I would like to have a button maybe here in order to access that for also, I would like a way for me to. Tuggle asks on enough as well a scene in the table if a task is enabled or disabled, maybe with a row color or something. So I'm going to go ahead and create a couple of records in my database, a couple of tasks, and in the next video, we're going to finalize his view. 7. Task toggle: in the last video. We ended up with this view, but we still need to do some work on it. I previously said that I was going to create a couple of records, but I think we can do that after the first part of this video, which is adding a button in this corner right here that will allow us to create in your records or to access the form that creates new tasks. So but to our index view for the tests up here in the cart Heather, I'm going to do some modification. First, I would like to have an Age Street act to wrap around this title here. I'm going to cut it first, then at an H three. Put it in there and inside these h three, I'm going to add a button class B, TN BT and primary, and I'm going to float this button to the right. And for the label. It's a type create task, and actually this shouldn't be a button. On second thought, there should be a link that looks like about So instead of button, I'm gonna use an attack, same class. But the link will take me to route tasks create. Okay, let's see how that looks. I'm gonna refresh in our router, and there you have it. A nice looking create task button. And if you click on it, we end up here in the create task. For now, let's go ahead and create a couple of tasks here. 1st 1 let's create the same one as before, which was send emails. The command is going to be PHP artisan email. Send brown expression. I'm gonna leave it like that email address. I don't need one, and everything else stays the same. Next, I'm going to add another one. This one I'm going to call run report and the command is going to be a little bit different . We're not constrained to just use PHP artisan commands. We can use any type of command we want. For example, what if we first CD into a folder which is called scripts and then execute ph. B report dot PHP? I could go ahead and execute these on the command line if I wanted to, but I am trying to schedule it as a task. Now this stars I would like to send an email to Let's use an imaginary email, for example, reports there's dot com it doesn't really matter, because when we test this, I'm going to use mail trip. And with male trip, I can use any email I want. All right, let's click on Create. The next thing I want is a way for me to disable the tasks in my list so that I don't have to delete a task to prevent it from running. I just have to disable it. So the first thing I'm going to do is work on the controller side, and I'm going to create a method that will simply struggle the active field on a task. So let's go back to our tasks controller and down here below destroyed. I'm going to create a public function called Tuggle. I would expect to receive a task I d inthe eu around, so I'm going to turn that into a task using route mortal binding and here Can you say task is active? Equals two, not task is active. This will effectively trouble the value. If it is true, it will become force. And if it is false, it will become true. After that, I just have to say the task with tasks safe. And then I redirect back to the task. Least redirect to route tasks. Index with He started off Task updated. Now let's work on the U I. Let's go back to the index view and right after these next run column let's add a new column. Let's just call it on off. And now down here after next run, it's at the Tedy Tak, and here I'm going to use a similar method that we used in the lead for now. Granted, this could be done with an Ajax school in J Query, but I'm just trying to keep everything as simple as possible. But just use impure JavaScript. So I'm going to create a form. The action of this form will be route tasks. Toggle Now. This route has not been created yet, but we'll created in just a second. And I know by now that I'm going to need the task ideas, a parameter so task I d. I will need to give this form and ideas well, let's call it form or actually, Duggal form and let's upend the I D so that it is unique task Heidi and the method will be post. Now here I'm going to include again the CSR airfield, and also I'm going to mimic a food request. So Method Field. For now, all I need is an input type checkbooks, and I'm going to make sure the boxes checked based on the value off the task. So if task is active, then I put check other ways, an empty string. And then I am going to use the on change even. And I'm going to get element by i d. And I'm going to pass Tuggle form and the task idea. And then I submit that for All right, let's go and see how this looks open. Refresh. We get this error that the task struggle route does not exist. So let's go ahead and define it back to the editor. Let's open the Routes file, which is routes web dot PHP. And here we're faced with a question. How do I add more routes to a resource route? Because this is basically going to be around on their tasks. Well, all you have to do is at your custom routes right before you define the resource route here . So route, foot and for the, um well let's use tasks and then the task i d forward slash trouble. This will be handled inside tasks controller in the Tuggle method. And then we named this road tasks. Let's go back to the routes. Had a refresh. So, yeah, we have a check box here, but in my opinion, this check box looks a little bit boring. So why don't we spice it up with the little package that are like cold bootstrap tuggle, which will transform any checkbooks in this version right here, which is a nice struggle, but a couple of choices here. You could download that the javascript file if you wanted to, but in this case, I'm going to keep it simple again by simply using this references to ah Cdn version of the script. So I'm going to take this link from here for the style. Copy it. And now I opened the layout file, which is in resource is views layouts up. Not later Ph. B. And down here in styles, I faced that link over here and now for the script. I take this script back here, opiate, and then baste it in the script section. And according to the documentation All we have to do is at these data struggle property to our checkbooks. So I'm gonna coffee that head back to our index cream and at that property to our check books here. And let's see how it looks Now. There is a problem here because when I refresh the button or the checkbooks stays the same , there is no visible change. Now the solution is very easy here, and I spend a couple of hours trying to figure out this. If you open the JavaScript console for the development panel in chrome, you're going to see this error. Jay Query. It's not define which at first doesn't make any sense, because if we go back to the editor, this Abdel Js here will contain a reference to a J query package, which is bundled up in this file. Now the solution here is this word, the for which is not in our script back. And what that does is to prevent the script from being executed until the page has been loaded and it appears that the script again, we just had it. It's running before that. So what we have to do is at the de faire attack over here and back, in our view, which is simply refresh. And there you go. Well, almost. If you notice this doesn't look like the example here, that should be like a dial on top, this white bar here and it's not showing in this place. And as it turns out, and after a few hours of investigation, the bootstrap Tuggle package has a little problem or incompatibility with bootstrap for so to solve that all you have to do is go back to the editor back to the layout file, and right after this style shit here, I'm going to include a snippet with a couple of custom classes that will help solve the problem. Basically, you have to override the Tuggle dash off class with this value box that shadow in sit zero , three pixels, five pixels, rgb a C 0.125 Then there is the tuggle dot off with this value here and finally the double dash handle with a background color white in a border scene, black solid in that should essentially sold the problem. If we go back to the browser and refresh one with time now, everything looks as advertised. So what happens if we double one? The task is essentially not active anymore. We get the message up here and now it says off. I don't like the white background, though, to solve that, let's go back to the editor back to the index view and here in our checkbooks, right after the data. That struggle or we have to do is at another customer. Attribute called data. Dash off style equals danger. You can use any of bootstraps styles here like primary, secondary warning, etcetera. But I'm going to use danger for Red. Let's go back to the browser and refresh. I think that looks a lot better. One last thing is that I would like the whole row for the task to reflect the status of it . For example, this one should have a slightly red background and the active one. I think I would like a slightly green background. Let's go back to the editor ring. Make that change. Well, we have to do is over here in our T r attack, which to imitates the row, which is Ari class table dash and here with the site which color danger will be read and success will be green. But we have to make that decision based on the task. So ask is active. Then it will be green or success. Otherwise danger. Let's see how that looks back to the ground set and refresh. Now we have finalized the index view. So it's time for us to actually schedule this tasks. And in the process, we're actually going to create a couple of scripts, one for the report, which is going to be a simple PHP script, nothing to do with laudable and also a Ph B artists in common that we can create right here in our product. 8. Task schedule: Okay, so we have these two tasks in our database. The objective here is to let Lateral scheduled them, and we're going to do that in just a second. But first, I would like to actually create the scripts that are being executed by these desks. Let's start with this simple one run report. If we look at it, the actual command that is going to be executed on schedule, it's a combination of a CD or getting to a folder called Scripts and executing a PHP file called Report about BH being. I've gone ahead and created this file in this folder, and I'm going to show you the contents of it. So as you can see, there is a reporter PHP file in here and inside. I'm just using three lines of code that are simply out putting some messages like running report script. Then I'm causing the process for about 3 to 5 seconds and then output a done message. Granted, this could be any kind of script, and that script can do a lot of complicated things that you want done in the background. But for this example, this will do now if I go outside and for a moment, let's move away from this folder. If I bring this command from here, copy it and paste it in the terminal, you see that the process starts with this message, pauses for a second and then says, Done. And that's all we're going to do now for the other one, which is send emails. We're actually executing on artists and comment and notice how I'm not doing any CD or moving into another dietary, because I know this command belongs to the same project. If that wasn't the case, you can actually execute artisan commands from other projects by simply moving into those project folders and then executing their artisan comments. But I'm going to keep things simple, and I'm going to create these command in this project to create this command. I can go to the tear Amina that's moving to our project folder and then execute Artisan may commend, and I'm going to call it send emails. Now let's move back to the editor and you can find this new command inside APP council commands and here's thes send emails coming. If you open it, you will find the command class which extends from command the signature, which is the name of the command that we're going to execute, which in this case is email send. So I'm going to copy this from here. Didn't based it in here. A short description of the command and two functions. The only one I'm going to worry about right now is the handle function, which is the one that executes the command or performs the tasks in this comment. And I'm going to do something similar to that order script that I created. I'm just going to output a first line with this line, which will throw a line or a message to the output and let's say something like sending emails. Then I'm going to pause the process for a couple of seconds. Let's say sleep random int and let's posit again between three and five seconds. And finally, I'm just going to say done. Now here comes the fun part. How do we tell letter built to take all of the tasks that are in our database and run them according to their schedule. For this, we have to take a quick look to the lateral documentation pages in the schedule in section here you can see that in order for lateral to start the scheduler, all we need to do is to add this line to our crown top file, which means that every minute Lauraville is going to go into your project directory and execute these artisan comment schedule run and the worry too much about this last part is just throwing away the output of these schedule running commence. You know, the worst is just running it silently. Now, with this in mind, we are going to tap into lateral service container, and we're going to trigger a callback function right when Larible is trying to discover the schedule class. What this means is that when level is bootstrapping and getting ready to run this schedule command is going to try and resolve this schedule class using the service container. And once the class has been result, it's going to check if that class has any schedules to run. So before the classes discovered were actually going to schedule recommends from the database and then let a lot of run them according to their schedule. So to do that, let's go back to the editor and open up provide this AP service provider here is where we bootstrap any application services for a project. So here in the boot function, we can say something like this up resolving schedule class and as the second parameter, these receives a callback function, and that function is actually going to receive an instance off the schedule class, which means that we can interact with the schedule class right at the time it's being result by the service container. Now, let's not forget to include this class using use, and I believe this one is located in Illuminate Council scheduling schedule. Now what we want to do here inside this cold, dark function. First, we want to Fitch all the active tests and then schedule those tests. Let's take care of this first part. You might be tempted to use something like tasks and then run aware he's active, equals true, and then get This is how we generally fetch records from a database using the Moto class. The problem is that we are at the point where we are resolving all over classes for a project, and I don't want to take the chance that this task Assad has not been result. So what we're going to do instead is to resolve the model class on the spot using APP and then the class name up task. And on that we're going to call the wear function where we filter all of the active tasks and we treat them from the database. Now let's store these into tasks and to schedule them, I'm going to use for each tasks as task and then call schedule, except which lets me input any type of command. And in this case, what we want is to pass the task command. Now, the way this works, according to Laval documentation, is that right after we call exact and to find the command that we want to execute, we defined the frequency. This is because these coal will return on event class. In that event, class can be scheduled using several methods, and the one that stands out is this one where we can use a crown expression to define the frequency of the task. But there are a couple of things I want to do first, like, for example, decide if I want to run this task in maintenance mouth, depending on the options of the task and also, if I want this task to prevent overlapping, so I'm going to call this as it is and then store the results in event and on this event, first of all, I'm going to define the schedule using crunch into that, I'm going to pass the task expression. Then I would like to be able to know how long did this task took in order to execute? We can do that by using an event callback called Before, which takes a callback function, and I want to use this event object inside dysfunction. Let's say use event and in here I'm simply going to the final custom property called Stark . So event start, which is equal to micro time, and I'm going to get that as a float. So let's pass me true here. Then we can use another callback event called After, which again takes a callback function that is going to again make use of the event object. So use event. And here we confined how much time has elapsed, so elapsed time. It's equal to micro time as a float minus the event start time. Another thing that I would like to do once the task has finished is to collect the results and create a new result record in the database. So what I'm going to do is first, I'm going to send the output of this task to a text file. And we can do that by saying sent Output two. And here we can provide a file name. In this case, I'm going to put it in our storage folder inside the same product so we can use storage path. And for the name of the file, maybe we can create something unique, like maybe task bash. And then let's append to this maybe an encrypted string with S H A one and that can be based on the task information, maybe the task command and also the task expression. But in order to use the task inside this callback function, we need to use it as well. So use event and task. And this is the task from this for each statement. So we know that the output will be put into this file. Now we can make use of that file after the task has finished executing. So we can say if a file exists and let's copy the file name from here. If they file exist, we get the contents of the file and let's store that inside out. But which is equal to file? Get contents and we based the file name again. And with that, I think we can create a new task resold record. We can do that easily, using the task itself, then calling the results which I believe we already have in our model. Let me double check. Actually, we don't, but we're going to create that relationship in just a second. So let's pretend the relationship exists already and it's call it results. And on that relationship, we can create a new record in this. Create receives an array of data and the tasks resold table. If I'm not mistaken, we only need to passing the duration and the results. The run at column has a default value, and the task I d will be automatically putting bilateral scenes. We are creating the record. The our relationship created Adan updated add are also managed bilateral. So for the duration and the result, let's go back to the editor. We know the duration already, which is the elapsed time, but we have to multiply this by 1000 because we are getting it as a float and also in microseconds. By multiplying it by 1000 we are effectively storing milliseconds which we will later transform in two seconds. And for the output. We simply passed the contents of the file which is stored in out. Once we've created thes task resold record. I would like to delete the text file because I don't need it anymore. So unlinked and let's again copy this file name and I'm already seen some call reputation. I'm sure I'm going to do some reworking on this file later on. But for now, let's just do it like this. So, to sum up, we are getting all of our tasks. Former database, the active ones. And for each one, we are scheduling the task command based on the task expression before the task is executed . We are collecting the start time and after the task is executed, we collect how many time has left. We're sending the output of this task to a file which we then check for existence. Once task has finished, collect the contents of that file and create a new task result record and finally we delete the output file now. One last thing I want to do here is to make sure that we apply the options for running in maintenance and not overlapping. So all we have to do is say, if desk don't overlap, then on the event we call without overlapping. Also, if desk run in maintenance. Then we said that up using event, even in maintenance mode, and I think that should be it. But before we contest decide we have to take care of these relationship here. First of all, we're going to need a new model for the task resold stable. So let's go back to the Tear Amina and create a new model with partisan make model. And let's call it just result back to the editor. Let's open the new model class resold things. The glass name is not the same as the table. We have to define a table property, which is protected, so protected table equals two. Ask results to avoid mice assignment problems. We need to define a protected the level property and for the fields we are going to use duration and result. And I think I used the wrong column name in the service provider. Yeah, that's right. There's no doubt it's result. Sorry about that. Okay. Back to the model. There is one other property that I want to define here it is protected dates. And this is so that lateral knows what other columns in our table has to be treated as dates. Which means that any columns that I define here, for example, the ran at column will be returned to me as a date object more specifically as a carbon date. Instance. If you want to know more about carbon, go to carbon that nest, but dot com or were slash stocks. Larible already knows that he has to do this for they created at an updated at columns. But if we want to have this also for any other column in our table, we have to put it in the dates property. Okay, Now let's go to the task model. And down here, we're going to define a relationship. So public function results and this is going to return with These has many result class, and I believe that should be everything. Now, in order to test this out without having to modify the crown top file. All you have to do is go back to the Terra Meaner and use artisan schedule run, which is the command that is being executed when we get to added the contact file. But first, let me just clear the screen real quick again. Descend schedule. Right. And you can see that the schedule run actually executed this command and store the results in this file Y just one. Well, because only one of them was active Internet service. Now, this file, if everything went well, should not exist anymore. Let's go to the editor and find out. And as you can see, the story, Shoulder doesn't have any extra files in it. And if we go to our database, we should see one task resold record, and they have it. We have the duration of the task, how many milliseconds it took to run as well as the output of the execution. So now that we know its working, all we have to do is go back to the lateral documentation, scroll up to the top and copy this line. Of course, we're going to modify the path of a project, go back to the Terra meaner and to modify your contact, Simply type grunt up dash E for edit. And in here I'm going to face that line from the documentation and modify the path to our project, which in this case is home vagrant. Cool, Fast, Cadman. And the rest stays the same. Save in close the file and there you have it. Now, Thescore Edgell Run Command is going to run every minute and every minute is going to grab all of the tasks from our batteries and run them according to the schedule based on the front of expression off every task. Now that we have available results for each of our asks, we can finally, at these two pieces of data to our test least, which is when was the task last executed in the average time that it takes to run that desk . But we're going to leave that for the next video 9. Task results: in the last video. We ended up with tasks running on schedule in feeling up test results in the database. And now we're going to finish off this view by adding the remaining pieces of information that are missing. That'll be the time that the task last executed. Right now, we're just out putting this string. And also how long does it ask takes to execute in average? I let the tasks running schedule for a few minutes since the last video, which means that I have plenty of records inside it as results stable. So let's go back to the editor so that we can add this last run column if we open the index view for the tasks we are using this custom attributes in the model to display the information right now, if we open the model, the get last run attributes function is just returning any. So let's change that. And before we return in a let's say, if last equal this results. This is the task and results will be the relationship that we set up in the last video right here. When we reference a relationship as a function, we can use any of the query builder functions like, for example, I can order by i d. In descending order and off that I only need the first record, and this will effectively give me the last record or the last time the task was executed. So if the result off this assignment is other than false, I'm going to return last, which will be an instance of the result model ran at which is a column in the table. And since that column was flagged as a date, this will be a date object for carbon date object that I conform it into year month. They our in a 12 hour format, minutes and AM or PM. Now, if the result of this assignment is false, meaning that there was no last record, then I will return any. Let's go back to the browser and let's see this in action. Three. Fresh And as you can see, the Last Run column actually shows the last date and time the task was executed. I don't really like the way this is wrapping around, and I think it's because the card that contains the table it's too small, so let's expand it a little bit more back to the editor. Let's open the index view. And if we scroll to the top, we can see that there is a diva that is actually wrapping around the card in that the ISS Only eight columns white. Let's actually take it up to 12. That should do the trick. Refresh And yeah, that's better. Now we should fix the time zone on this state because if you remember, the next run date was in UTC Time zone, and we transformed that interest central time zone. If we go back to the editor, you can see here that we use America forwards last Chicago, which is actually central time. So let's do the same with this one here before formatting the output. This school set time zone can pass the same string. And actually this Z over here. It's not capital. So set Times don't like that. Back to browser and refresh. Okay, that's much better. Now we move on to the average Stein. So back to the editor, let's are the new custom attribute with public function. And let's remember, what was the name of the attribute that we used back in the index view we use average runtime, so the name of the function will be get average run time attribute. Luckily for us, lateral makes extremely easy to make an average of a column by simply saying this. And actually, I have to return this return. These results, which again is the relationship that I'm referencing as a function to make use of the query builder functions. And there is a handy query builder function called average into that I only have to pass the column name that I went to average, which will be duration. Now we know that we store this value as many seconds. So to get the value in seconds, just divide that by 1000. Let's see how this looks. Go back to the well said and refresh. We did get the output, but the precision is way too high, so let's narrow that down to maybe two floating points back to the editor. Let's wrap this around a number format with two decimal points. All right, let's see how that looks. Now let's add a label next to it so that we know that this isn't seconds back to the editor and open the index view and here right next to average runtime. That's putting seconds. And with that, I think we're done with this index view in the next video, we're going to take care of the little under lane problem off code optimization if we go back to the APP provider's APP service provider, which is where we are taking care of scheduling all the tasks you have to take into consideration that the schedule class were the schedule wrong command that we set up in the crown tap. It's running every minute, so every minute Lauraville is going to invoke this event Colback resolving in every minute , we are going to query the database for all the active tasks. The creation of test results doesn't necessarily happen every minute, because tasks will not necessarily run every minute. They can have different schedules, like every 15 minutes or maybe once a day. But we do know for sure that this query over here we'll be executed every minute. So what I'm going to do in the next video is to optimize my code by adding query cashing. So if the information hasn't changed into that of its, we will get the active tests, not from the database but from cash. And we will probably end up using this query cash method on the index view of our tasks. Controller, over here. That way, not only the scheduling of the test will be optimized, but we will also improve the US well. 10. Query caching: So the objective of this video is to optimize our code by adding query cashing so we don't have to execute this query every single minute. But before we do that, I would like to introduce a very handy tool that will help us D. Buck, if the query cashing is actually working or not in are you I in that too? It's called Letter Bill de Book. Far these two, among other things, will give us information about the queries that we are executing to our database. So to at the store project or we have to do is follow the installation process, which is just this line over here. I'm going to copy it, go back to ketamine in and then based it here. The installation process is going to take a couple of minutes, so I'm going to make a pass here and come back when it starts. Okay, the package has been installed in. Luckily, there is nothing much to it. The bar will be automatically enable if the configuration variable Abdi buck, he said to truth. So if this is true, you will see the bar in your browser. Let's go and check it out simply refresh. And now you have the D book bar down here at the browser. And if you want to see what queries are hitting the database, simply click on the query stuff, and here you can see a list of all of the queries that are going into the batteries. The objective here will be to cash this specific query right here because we know that asks will not change that often. So instead of getting the least of tests from the database, we will get them from cash. So let's go back to the editor and open up the APP service provider, which is in APP provider's service provider. So what I'd like to have instead of this wear over here, it's something like up desk. But instead of filtering like this and then fetching the results from the batteries, let's use something like get active. So let's uncommon this'll incoming. The one belong, and this should be stored being tasks. So let's go ahead and create dysfunction inside the task model. Let's go to the end, and right here I'm going to add a public function called Get Active. I'd like to have thes same query from here. So I'm gonna cope in that section, go back to the model and without cash in what I would do is simply return this and then filter by active infection from the database. But if the information has already been returned and has not changed, I don't want it from the database I wanted from cash. And we can do that by using a lot of else cash, facade like this. So instead of returning this, I'm going to return. Gosh, remember forever. And then we pass in a key which will be tasks active and then a callback function. And inside that callback function, we're going to return the actual query. So what this means is that latter Bill will check the cash for these key tasks active. If there is none, he will then execute this function, return the query in this case, and also add the results of this query into cash. So the next time I call get active, a lot of ill will check for the key again. And if it finds it, we'll return the value from cash. Now let's do the same. But this time with the u I. If we go back to the tasks controller instead of calling all to get all the records directly from the database, we're going to do something like asks equals two. And here I cannot use a facade because I am going to create a public function just like I did for the active records. So I'm going to quickly instance a new task model. And on that I'm going to call get Oh, now, let's create this Get all method in our test model right here. Let's at a public function cold get oh, again, I'm going to return. Gosh, remember forever The key will be tasks and then he callback function that will basically return this. And by looking at this, we can probably clean up the code a little bit by calling the get all method here and then feel third those results because when we call this Oh, what we get in return is a collection class in the collection class has a filter function which receives a callback function in based on the return of these function, the corresponding item which I forgot to include here in this cold a function. And since we're talking about tasks, this will be actually, a desk based on the task is active column. That particular test will be filtered. If the value off is active is false. Just go to the browser and let's see it in action. We should see these particular query disappear. Let's go ahead and refresh. As you can see, we are still making a query to the database. But that is because it was the first time we were checking for the cash value, which didn't exist it. So in theory, you flee. Refresh again. The query has gone, and we are no longer returning the list of tasks from the database. Instead, we're returning them from cash. And as you can see, the results aren't saying. But if you think about it, Lauraville is going to remember that cash forever. So what happens if we create a new task or edit the information of one of the task or simply tuggle one, for example? It struggled this one off. As you can see, we get the message that the task was updated and we check the database. The task is no longer active, but the U I says that it is because we're not getting the results from the database were getting it from cash in lateral. We remember that cash value forever One is we clear the cache, in which case we will have to go to the database again together. New records. So we need a way to tell Lauraville that the records have changed or the tasks have changed so that he knows that he has to clear the cache and get the records fresh from the database . To do that, we're going to use a model up server. The function of an observer class is to observe changes in the model and act accordingly. To create a new observer class, simply go back to the terminal in tight artisan makeup set of her. In this case, we're going to call this Basque upset of her and then passed the cash stash model equals desk, which is the model class that we're going to observe. Now let's go back to the editor and you confined in Europe Server class, inside up, up servers, Basque Up server. If you open that one, you will see that a lot of already scaffold. It's some of the code for you. For example, he created a few functions like created off dated delete it restored in fours, deleted. This all correspond to events that can happen to a task, since we're not going to force, lead or restore any of our tasks because we're not doing self deleting. I'm going to get rid of these two functions here, which leaves me only with deleted, updated and creative Let's start with created. In fact, the same thing is going to happen in each of it. What I want here is to clear the cache or forget about the cash if we create a new task. So all we have to do is say, Cash forget. And here we pass a key, which was tasks, and in fact, we need to forget also about the that's active cash. Don't forget to include the cash facade at the top of your class, so we take care of the created event. Now let's copy these two lines and do the same when we update a task, and also when we delete a task. Finally, we have to register this observer in our service containers. To do that, let's open the APP service provider file within up providers, which is the place we are using for scheduling our tests and in the same boat function. Let's add one more line right at the beginning that goes like task. Observe into these. We passed the observer class, which is task up server class. Don't forget to include this class up here into use statements, so use task upset of it. And with this line, every time we update create or the leader task, the cash will be cleared in the latter, Bill will fetch new results from the database. Let's see that in action. Let's go back to the browser and let's try and refresh this just to make sure that cash is working. And oops, I forgot to include the task class. It's going back to the editor and fix that right here. APP Task. Okay, that should do it back to the browser. Refresh again. Okay, let's examine the queries. And yet there is no query to select the task list. But which what happens if we double one of these? Asks, Let's turn it off. The changes reflected, and if we examine the queries again, let ever went and fetched the tasks results directly from the database. If we refresh again, results will come from cash instead, and I believe that will be all for this lesson. All that remains now is to send an email notification every time a task is completed. But before I moved to that, I'd like to spend some time cleaning up some of the code that we have so far. Let's go back to the editor. And if we examined, for example, this boot function over here, this whole section of code looks a little bit too crowded. There is too many things going on in one place. I'm going to try and clean this up by introducing two key elements events in notification classes, which, by the way, will enable us to send notifications. The email. 11. Events and notifications: in this lesson, we're going to take some time to clean up our code. Let's start by removing this unused line of code right here this comment. And second, I'd like to take this part off code and move it elsewhere so that it will look cleaner in the section here. For that, I'm going to use events and event listeners. So instead of executing everything here, I'd like to have something like Event, which is a helper function that lateral provides to dispatch events. This would receive a new instance of the event class, So let's call it, maybe ask Executed event into this new class. I can pass any information that I require when handling the event, for example, I'm going to need the task, and I would probably need the elapsed time as well. So let's move this line just below the elapsed time like this. So in theory, all of the code right here will be handled by the list inner of this event. But how do we create this event and its corresponding listener? Lateral provides an easy way to scaffold both the event and the event listener by simply modifying the line of code inside the event service provider file. This file is located inside the APP Providers folder. If we open that one, you will notice that there is a protected listing property in the even service provided class. This is simply an array of events and their corresponding event listeners. So let's change the names here. So the event we called task Executed event and for the least, inner, Let's just call it. Ask executed listening. Once you have the names of the events and the event listeners, you just have to go back to the terminal in type artisan event. Generate. As you can see, events and listeners were generated successfully. If we go back to the editor, we can find the new classes inside the events and listeners folders. Let's start with the event. This is where we set up our event before it's handled by the listener. If you remember, we passed two variables for the constructor, which were the desk in the elapsed time. Don't forget to include this class up here in the use statements, and here I will simply assign these two variables as properties in our class. So this task equals two task in this elapsed time. Equal student elapsed time. We have to remember to include this two properties as public properties of our class in order for them to be available to the listener. So up here I'm going to use public ask in public elapsed time, and that will be all for the event itself. Now let's move on to the event. Listening inside APP listeners. That's open task, executed listener. And in this class I'm going to feel the handle function, which is the one that is going toe execute after the even has been dispatched, and it will take an instance off the event itself. So let's quickly go back to the APP service provider file and let's take all of these lines of code from here and then faced them inside the handle function. And let's start getting rid of these errors. For example, the task is actually part of the event, so event task Samos here event task. If you remember, we set up the task as a public property off the event back in the event Constructor over here. Let's do the same here. Event I ask. Same here can finally here there is one more down here and this one, too. And the elapsed time is also part of the event class. So event elapsed time. So now, instead of doing all of this inside the at service provided file, we have a more clean look by simply dispatching the task executed event. And don't forget to include this file. They used statements up here. We could even go one step further and take all of this logic into its own function. For example, let's add a new public function right here. Let's call it schedule. And now we take all of the logic that is inside the resulting callback function, which is from here all the way down to here. It's got that based it down here in the schedule function, and it looks like the only thing that we're going to need to be passed on to the schedule function is thes schedule itself. So let's add that parameter schedule schedule and up here inside the resulting called a Conscience. We simply call this schedule and then passed the schedule class, and in my opinion, that looks a lot cleaner and a lot better in the final part of this project will be to send an email notification every time a task is completed. To do that. First, we're going to create a notification class. Let's go back to the editor in type artisan. Make notification and let's call it desk completed. The notification was created successfully, so let's go back to the editor and you can find your notification class inside Up Notifications folder. And the important function in this class is the one called via here you can define. Where are you going to send your notifications to, or where are you going to route those notifications? By default, Lauraville will route them via mail. We're going to modify this because we want to the side to send identification on Lee. If we have an email, have a level in our task. Also, if we keep scrolling down, we have another function called to mail. Here is where we construct the contents of the email notification. It already has an example with one line one action in another night. We're also going to modify these to include the details of our task. So let's start with this one. Let's say something like Channel, which will be the channel we went to route are identification to equals two. And here is where we make our decision. No, this notifiable perimeter is actually an instance of our task. More field with the information off the Korean task. And which task is that? Well, that does. That was just executed. So based on the information of the notifiable, which is a task, we can decide if we have to send an email or not based on the content off notification E mail ive the wrist one. Then we returned this here, which is an array with just one item mail. And if there is not, we simply return an empty array. Now, instead of returning this, we return channel. Now let's construct our email notification. I'd like to have an email subject which will be the past description so we can use subject . And again, this notifiable perimeter is the desk so we can use that and simply passed the task description. We can modify this line to say something like the jazz description again so that that was just or actually has finished running. We're not gonna need this action here, which will render a button or a link in our email, but we don't need that one. And actually, I think just the subject and that line will be enough. You could even throw in the output. But I know there will be problems with new lines and carriage returns, so let's not make it too complicated. I think this will be enough now. In order for this to work, we have to do to modifications. First, we have to make our task model notifiable. So let's open our task model. And at the top, we are going to use a trait called Notifiable. And if we use that trade, we also have to include one function called route notification for Male. So which is the field that contains the email address for the notification? That will be this notification email, and in fact, we have to return this. So, in other words, this is simply a way to identify the route for the mail notification, and the route for the main notification will be the value off, not the vacation email. So how do we test this? Well, to make things simple, I'm going to use a service gold mail truck. This is a free service that you can use to test your application email deliveries. So go ahead and create your own account so that you have your own credentials. I already have one. So I'm going to Logan, and here you will be given a demo in books, which is where all of your test emails will be delivered to. If you click on that one, you have your in books and one side and also your credentials stated here. I'm going to use my user name and password, but I suggest you use your own credentials. Otherwise, you won't see the results of your test. So to set this up in lateral, let's go back to the editor and opened the dot ian V file in the root of your project. If you scroll down, you can see there is a section for male configuration and, as it turns out, by the fourth, lateral has everything set up for you except the credentials. So let's fill this in with the credentials from mail, truck, user name and password, and that's it. You're ready to start sending emails, so let's go ahead and make a test. Just go back to the terminal and first let me clean this up a little bit. Now let's run the schedule manually to see the results. Just type artisan schedule Run evidently didn't work because we don't have an email in our inbox, So let's go ahead and see what happened. Go back to the editor and let's go back to now. Of course, I forgot one very important step. Let's open the task Executed listener. Here is where we should notify our past model that the task was executed and we didn't include that light. So in order to send a notification to our task, we simply access the task from the event. So event task in scenes were using that notifiable trade. In our task model, we can make use of the not if I function into this notify function. We have to pass an instance off the notification class. So new task completed again. Don't forget to include this class in the use statements up here. Back to the terminal. Let's around the schedule one more time. Now let's go back to the browser and into male trip to make sure that we got a notification and here you go. We have that as description of the subject and also we have a nicely formatted HTML message with the task description, which is Run Report has finished running. By the way, we can change the lateral text over here and down here by changing the name of your application in the configuration file in the dot he and defile. In fact, let's go ahead and do that right now. Back to the editor opened the dot DMG file and at the top. That's change this into task had mean and just to make sure everything works as expected. Oh, I made a mistake back to the dotty and be file. And when you have spaces in your values, you have to in close that in double quotes. All right, refresh. And there you go. Now your application name changes. I'm going to enable this task again, but I expect only one email notification, because this task does not have an email address for notifications. So back to the arena, let's run the schedule manually one more time. The two tasks were executed. Now let's check mail trip, and we have only one notification as expected. Another email has the right application name, and with that, I think we're done with the lesson and also with the course. So without anything further to add, thank you very much for taking the time to watch the scores, and I'll see you on the next one.