Let's Build Task Tracker App With Laravel | Part 1: Architecture & Auth
Chapters16
Introduces building a Laravel-based daily task tracker with emphasis on architecture, planning, and secure design. Outlines features (auth, tasks, categories, recurring tasks, dashboard) and topics like ER diagrams, hybrid ID strategy, policies, form requests, AI-generated boilerplate, and deployment considerations.
Gio walks through architecting and scaffolding a secure, Laravel-based Task Tracker, emphasizing planning, ER diagrams, and robust auth before coding.
Summary
Program With Gio’s deep-dive organizes a Laravel task-tracker project from the ground up. He starts by stressing architectural planning, ER diagrams, and user flows before touching code, and then shows how to translate those plans into migrations and models for users, tasks, and categories. Gio explores advanced authentication concepts—guards, providers, middleware, CSRF protection, and session security—before implementing routes, controllers, and blade views. He demonstrates how to build a secure login with rate limiting, timing-attack protection, and a form-request-based validation flow, then expands to registration, email verification, password resets, and user password changes. The video also covers modern tooling (Laravel Sail, Docker, Von/Vite with Blade, Tailwind forms, and AI-generated boilerplate), domain modeling (ER diagrams, foreign keys, and relationships), and developer ergonomics (IDE helpers, Blade components, Alpine.js, and route grouping). Toward the end Gio previews part two, promising business logic for tasks, categories, and AI-assisted boilerplate generation. Expect a rigorous tour of security-conscious Laravel essentials alongside practical DB design and UI scaffolding.
Key Takeaways
- Design first: Gio insists on ER diagrams (users, tasks, categories) and flowcharts before writing code to prevent brittle architecture.
- Hybrid ID strategy discussed: internal integers for performance with UUIDs in public URLs to balance safety and usability.
- Robust authentication flow: guards, providers, session-based web guard, and middleware guard access to routes.
- Security-focused auth: rate limiting, timing-attack protections via timebox, CSRF, and careful session regeneration.
- Migrations and models are created with artisan (make:model -m), and relationships are defined (user hasMany tasks; category belongsTo user).
- Form requests used to separate validation from controllers, improving readability and testability.
- Blade components, Tailwind forms, Alpine.js, and IDE helpers (Laravel ID Helper) accelerate UI and developer productivity.
Who Is This For?
Essential viewing for Laravel developers who want to level up from boilerplate authentication to a secure, architecture-first app. Great for teams or solo builders who want concrete patterns for ER modeling, robust auth, and maintainable Laravel code structure.
Notable Quotes
"Hey everyone, let's build a daily task tracker app from the ground up using Laravel."
—Opening line sets the ambitious scope and tone for the project.
"We will actually start by designing our entity relationship diagrams and user flows before we write single line of code."
—Gio emphasizes planning and architecture before coding.
"The elegance of simplicity often masks the underlying complexity."
—A reminder that beginner tasks can hide deep design considerations.
"Guardrails: this plan and these simple diagrams act as our guardrails."
—Planning as a safeguard against messy, brittle code.
"Timing attacks… the timebox technique keeps response times uniform for failed attempts."
—Security-focused defense against password brute-forcing.
Questions This Video Answers
- How do I design ER diagrams for a Laravel app with users, tasks, and categories?
- What’s the best way to implement Laravel authentication without a starter kit?
- How can I add rate limiting to Laravel login to prevent brute force attacks?
- What are form requests in Laravel and why should I use them for validation?
- How does Laravel implement email verification and password reset securely?
LaravelLaravel SailDockerBladeTailwind CSSEloquent ORMMigrationsArtisanForm RequestsAuthentication (guards, providers, middleware)
Full Transcript
Hey everyone, let's build a daily task tracker app from the ground up using Laravel. We're not just going to be coding. We are also going to focus heavily on the architectural decisions, planning, and execution. We will actually start by designing our entity relationship diagrams and user flows before we write single line of code. Now, you might be thinking, is this just another todo app? My answer to that is no. A lot of the topics that we cover in this project are almost never covered in simple todo app tutorials. By the end of this project, you should have a foundational secure system where users can register, manage tasks, categories, and recurring tasks, and even view their progress on a dynamic dashboard.
We'll start with a full authentication implementation, but we're going to go deeper than the basics. We're going to implement rate limiting and learn how to prevent timing attacks in your login logic. We'll talk about password resets and authentication security. We'll also cover architectural concepts like using a hybrid ID strategy where we use integers internally for performance but UU IDs for public-f facing URLs. We'll cover eloquent policies and form requests and we'll even look at modern workflows using AI tools to generate boilerplate code so that we can focus on the complex logic. Now as far as the prerequisites goes, it is assumed that you have basic knowledge of Laravel.
You should know how to set up an environment and know your way around blade routing and controllers. A little bit of service container and so on. It's also assumed that you know basic SQL and how to write standard JavaScript. If you're completely new to Laravel, I suggest watching one of my beginner oriented videos first. At first glance, you might think building a daily task tracker app is straightforward, something you could probably knock out in an hour or two. And you would be partially right. However, the elegance of simplicity often masks the underlying complexity. and this project is a perfect example of that.
So without further ado, let's dive right in. [music] [bell] [music] The first step that I always like to take when working on a project is to carefully read through the project requirements if available or identify the project requirements and then plan the entire thing or at least most of it at a high level. I like to visualize things and diagram them at a high level like flowchart or user flow diagram, sequence diagram, entity relationship diagrams and sometimes even user journey diagrams. They let me stay on course and focused on project features and not diverge too much while building.
So let's read the project details here. Build a practical daily task tracker using Laravel 12, Blade and Tailwind CSS version 4. This project guides us through the creating a web application where users can manage their daily tasks, organize them into categories and track their progress. We will implement core features like user authentication, crowd operations for tasks and categories and dynamic dashboard elements. We'll learn to structure a laral application, work with databases using eloquent render views with blade and style interfaces with tel and CSS. Now, as you can imagine, some of these parts are AI generated because this project was AI generated and then I went through and modified some parts of it to make sure that the tasks actually make sense.
Now, I'm not going to follow this step by step all the way. I want to give this project a little bit of educational form. I'm not just going to go ahead and build these out, assuming that you know everything. I'm going to build these out and explain each topic that I'm going to touch in Laravel in a bit more detail so that you have a better understanding how to do that on your own in future. So let's read through some of the tasks here. So the first task of course is the research task and this is the important part for any project that you'll be working on.
A lot of people take this for granted and they just dive straight into coding and I personally don't like that and don't do it. I'd rather spend initial few hours planning out the whole thing, sculping out the project, ironing out all the details that are needed, requirements, and then get started on the actual coding part. So first task here is asking us to review the project description goals text stack understand the core functionalities project requirements core features are understood and identified potential challenges or ambiguities are noted and a highle plan for feature implementation is uh outlined.
So before we do that I'm just going to go through some of these tasks. So we need to set up the laral project that's obvious. We need to implement the user authentication. Research eloquent factory receivers. Research lot of authorization and policies. Design category database model and migration. Implement category controllers and routes. Create category management views using blade and tailwind. Refactor using form requests. We'll do some keyway. Design task database model and migration. Implement task controller. Create task management. Another key way research artisan commands for recurring tasks. Um this is a research for the Laravel task scheduler.
Implement task filtering. Implement dashboard UI and other QA and other research to research the testing. Then implement some feature tests. Research basic deployment options and write project readmi documentation. Now as you notice there is no task to actually deploy this project. That's because this project's uh difficulty level is two out of five which is sort of beginner level and you're not expected to deploy this project entirely. We are going to deploy that when I'm going to build it because I want to show you how this can be deployed but that is not a requirement. If you complete this project and you just read about how to deploy this and what the deployment options are that's good enough.
you can consider this project completed. If you want to take it a step further and deploy it, that's great. You can do that, but it's not required. So, let's get started. I typically use draw io or mermaidjs to draw diagrams and it is up to you which tool you want to use. I'll be using draw io for this project. So, let's first identify the main entities or the pillars of our application. We obviously have user entity because somebody needs to log into our application. Right? We also have task entity or tasks because user will be managing tasks.
Now since we have the task entity, we should also have a category entity because tasks can belong to categories and user should be able to manage categories as well. Now, as you work on projects and do these kind of plannings, you will notice that these almost always translate directly into the main database tables. We will obviously have the users table, the tasks table, and the categories table. Now, let's think about the users journey or a flow because this doesn't really give us any information. If you give this to someone that doesn't know what the application is about, they're going to get pretty basic idea that okay, you have an user, you have task category, but it doesn't really give them a picture of what's going on in here.
So, we kind of need to zoom out uh of this a little bit and zoom in into the user flow. So, how do we get user to the task or the category management? To do that, we first need to authenticate the user, right? So user needs to be able to register maybe have an account already and if they do they should be able to authenticate through login page and then go to the dashboard and then from there they can manage their tasks and categories. In fact let me rename this uh for now to something like manage tasks and we'll just call this crowd operations.
That way we don't need to create multiple uh rectangles here for individual actions. Uh we'll keep it cleaner that way. And this will be manage categories also crowd. And we're going to move the user up there somewhere. Let's remove these lines for now and we'll connect them in a bit. I'm just going to move this uh a little bit away. All right. So let's build out our authentication steps. So when a user opens up our website or web page, we need to check if the user is authenticated or not. Right? So we need some kind of decision or conditional uh block shape in here.
Uh now we can scroll down to flowchart here and we have some more diagrams here that are specific to flowcharts. We have this uh diamond shape here that is a decision shape. So we can put that in here and we can say is user authenticated. Let's make this a little bit bigger. We'll put it here and we'll connect this. Let me move this up a little bit and zoom out a little bit. Move this away so we have a bit more space to work with. All right. So what do we do if the user is authenticated?
Well, if user is authenticated, we'll take them probably to a dashboard page, which means that we need a dashboard page or process. So, we'll create that in here. Let's call this dashboard. And if the user is authenticated, we'll send them to dashboard. However, if the user is not authenticated, we need to send them to the login page. So, let's create another process or page here called login. and we'll connect that in here. Next, we need to ask ourselves, does the user have an account? Because if not, then we would want to allow them to register a new account.
So, we'll need a decision block here as well that basically determines if we need to send them to the registration page if they don't have an account or have them enter the credentials. So, let's move this down a bit more to make some space. I'm going to adjust this a little bit. Let me move this this way. And we'll add another decision block in here called does user have an account. If user doesn't have an account, then we need to send them to the registration process or the page. So we'll go in here, say registration, and call this no.
And if they do have an account, then we need to have them enter the credentials, which is going to be another process. So, let's move this down a bit more. And we're going to add another process here called enter credentials. And this is going to connect here. Let's connect it this way. Now, what happens if they fill out the registration and registration fails? That means that we need another decision block here that checks was the registration successful. So let's add another decision block here. Call it registration successful. If it's not successful, we will probably redirect them back to the registration page with maybe error messages so that they could resubmit the form.
So if the registration is not successful, send them back to the registration process or the page. However, if the registration is successful, then we can automatically log them in and redirect them to the dashboard. So this technically can go right here. But let's clean it up a little bit so it looks nicer. Now let's continue on the enter credentials process. when they enter the credentials. We also need another decision block to check if the login was successful. Right? So we'll duplicate this and we can actually put it right here and say was login successful. Again if the login was successful we will redirect them to the dashboard the same way.
It it's kind of follows the same line as the registration. And if the login was not successful, we need to send them back to the login page. So we'll take this and send them here this way. And now we have some basic flow here. So if we go up here and we start at the user, when user visits our application, we need to check if a user is authenticated. If the user is authenticated, we redirect them to the dashboard. If they're not authenticated, we take them to the login page. Then on the login page, we need to make a decision.
Does the user have an account? If yes, we send them to enter the credentials. Uh then when they enter the credentials, we need to check if their login action was successful. So that's another decision block. If it was successful, we send them to dashboard. And if the login was not successful, we send them back to the login page. If however user does not have an account we go through the registration flow and on the registration page when they submit the form we check again in the decision block was the registration successful if yes we take them to the dashboard if no we redirect them back to the registration page.
Dashboard in this case is kind of like a command center. It's going to give the user a quick summary of what's on their plate and then they can take it from there and do some of the actions like manage tasks, manage categories and so on. So essentially what we can do is that we can now connect our uh dashboard to these two pages or processes. Now we can drill further into the manage tasks and manage categories uh crowd operations uh individually in more detail if we wanted to and sort of expand these into its own flows but I don't think that's that important uh at this stage we're trying to look at it at a high level the first most important thing is the user authentication uh and the user flow once we get that ironed out the rest is sort of the easy part to implement the rest is just page to manage the tasks and a page to manage the categories and maybe uh some of the actions on the dashboard.
Now since on top here we described the O flow I think it makes sense to put this into its own sort of like a background so that we can separate this diagram section from the categories and tasks and once the user is already authenticated section because we may want to expand this a little bit more and I want to have a separation between this section here once the user is already authenticated and this section here when user user is going through the authentication flow. So what I'm going to do is I'm going to take a square.
Actually, instead of square, let's use a rectangle. So let's add the rectangle here. Make it bigger. Let's set the opacity to 20%. And set the color blue. Let's actually decrease the opacity to maybe 10%. Yeah, I think that's good enough. And we want to include everything up until the dashboard because that's when the user becomes authenticated. All right. And we also need to add some kind of title. So I'm going to add the text block and call it O flow like this. And let's duplicate this and put it in here. And we'll duplicate this and call this task.
and category management flow. All right. Then once we have this, we can have another flow starting from the bottom uh from the perspective of authenticated user. In addition to the authentication that's the core of our uh project, we also will have authorization which is another core part of our application to decide whether user can access certain tasks and categories because we don't want one user to manage another user's tasks, right? We want to keep it isolated for each user. So let's copy that and put it here outside. And we'll call this authenticated user. And when this user visits our application who's already authenticated, in order for them to be able to manage the tasks and categories, we need to have a decision or a check.
Does this user actually have the right permission or access to manage a specific task or a category or even see these tasks and categories on the dashboard? So this decision block is going to have does task or category belong to the user and we'll connect this here. Now, if the task or the category does not belong to the user, we want to show them or send them uh to some kind of error page that says, "Hey, you don't have access to this," which could be 403 access denied page. Or if it's an API request, we could simply just respond with 403 access denied status code.
So, let's put an action here called 403 access denied. And we'll connect this here. So if user does not have the necessary access, we'll take them to 403. Otherwise, if they do have access, we will let them manage these tasks and we will let them manage these categories. I cannot connect it for some reason. I think because this needs to be sent to the back. And now we should be able to. Let me try again. Let's do the same thing here. Let's send that to back. And yeah, that looks much better. Now, as I mentioned, we could get more detailed and expand more on the individual manage tasks and manage categories, but our application as a whole is not that complicated.
We just have a dashboard page. We need to implement ability to manage tasks and ability to manage categories and a way to associate tasks with categories. So we could technically connect this to this but those kind of relationships and details I'm going to save it for the erd which is the entity relationship diagram for this one which just shows the highle flow of what's going on in our application is good enough now you can represent this in 100 many different ways but I'm pretty happy about this and I think I'll stick with this. If this were a little bit more complicated project, I would probably expand these a bit more.
But for this project, I think this is good enough. So the next step that I want to work on are the ER diagrams or the entity relationship diagrams. There are multiple ways that you can draw this. You can use tables or entities, but it is up to you how you want to draw or what tool you want to use. So, as I mentioned before, we are going to have three main entities. User, category, and task. So, we're going to create a table here. Let's zoom in a bit. We will call this user. And notice that I'm naming my entity as a singular noun rather than plural users.
Some may argue and say that you should name entities plural like your table names so they match but I prefer a singular term because it reads better and then it also maps better to my entities or models once we get into eloquent. Now you might be saying why are you saying these are entities but then you're creating table why not just create entity. I just prefer the table UI in here and treat these as entities. It's a personal preference. You're free to do it your own way. I also look at database tables as collection of entities.
They hold multiple records or multiple entities. But in the diagram, we're displaying a relationship between singular entities. So let's fill in the user entity first before creating the task entity. For user, we'll have the primary key ID. We will also have the name and we will have email which should be unique. So we'll put unique key here and we'll have the password. Let's add another column for created at which is a timestamp and another updated at. We will probably have more columns later on but these are the most important ones that I would want to show ahead of time.
Next, let's duplicate this and create the task entity. Task will also have ID. It will have title, description. Uh let's remove this unique key index. It will maybe have a completed at time stamp and the task date so that we can see for which day this task is meant for in case we want to add tasks for future. We also want to be able to mark tasks as recurring because that was one of the requirements when we read through uh the tasks and the requirements and we want to add some foreign keys here right because task should belong to a user which means we need a user ID and this is going to be foreign key and we need to connect this user ID to the user's ID column.
task will also belong to a category. So we need a category ID for key as well. So let's add that here and call it category ID and category will have the name. It is not going to have description. We don't need this either. It will have user ID because categories need to belong to the user. We don't need that. And we can have the created at and updated at. Let's get rid of this. And here actually we need created at as well. All right. So once we have all these entities set up now let's draw relationships between these three entities.
So a user can have zero or more tasks but a task can belong to one and only one user. We don't want task to belong to multiple users. Uh we don't want task to belong to no user because it could cause some confusion and security or privacy concerns if a task can belong to multiple users. If we wanted to share tasks, they would be a separate concern, but that's not the requirement for our project. Now, if you're new to ER diagrams, I'd suggest to do a quick Google or AI search on ER diagrams and how to set up different kinds of relationships.
Also depending on the tool that you use to diagram there might be some slight differences. In here we can see the relationship lines and as I mentioned user can have zero or more tasks and a task can belong to one and only one user. So the relationship that we need is the many optional on the task side and one mandatory on the user side. So if we read through the different type of relationship lines here, we see that this is the one that we need. One mandatory to many optional. So let's bring it here and we'll put the one mandatory on the user side and we'll put many optional to the task entity on the user ID.
This crow food and zero or circle here denotes zero or many. And this double line here on the user side denotes one and only one. Now sometimes if you don't want to be too specific or have limited information during the planning, you could just omit the zero or the circle in here and this double line and keep it simple where it would just be straight line with the crow food and that is one of these in here. So it would be one too many and that is what you will see in many diagrams as well.
But if you have the details and like more clarity you can be a bit more specific like in here. Now let's draw the same type of relationship to the category because user can have zero or more categories and a category can belong to one and only one user. Now the reason we are putting this kind of relationship here is because we're not building a global categories list that are available to all users. Each user can have their own categories and are responsible to manage their own categories. And finally now we can draw the relationship between the task and the category.
So we have this category ID and we need to connect that to this category entity. Now category can have zero or many tasks and task can belong to a category optionally meaning that we can have tasks that don't belong to any category. Maybe user doesn't want to categorize certain tasks and that is perfectly acceptable for that. We want to use another relationship line with one optional to many optional. So we need to find that kind of line in here. And this is the one one optional to many optional. So let's take that and we'll connect this side to the category and we'll connect this side to the task.
So now we're saying that category can have zero or many tasks and the task can belong to zero or one category. And I think this is it. Our app is not that complicated. So I think this illustrates what we really need. So we have the user entity, task entity, category entity, and all the relationships between them. Now I want to pause here for a second because you might be thinking, Gio, let's just get to the code. Why are we wasting time with this? In my opinion, this planning phase is the most important habit that separates hobby projects from professional maintainable applications.
The reason I go through the trouble of creating a plan like this is to prevent chaos. Code written without a plan gets complicated and brittle very fast. This plan and these simple diagrams act as our guardrails. They force us to think about the user's journey and how the different parts of our system will connect before we write single line of code. And with the advancements of the AI, planning, I believe, will become even more important because then you can take that plan, feed it to an LM, and have uh AI build the features and projects for you.
So now that we've got our plan, we can move on to setting up the Laravel project and local environment. I'll be using Laravel sale and Docker to set up Laravel, but you're free to use any other method of getting Laravel installed. refer to the documentation for other ways to get Laravel up and running on your local environment and pick the one that you prefer. So, let's open the terminal. I'm going to run the following curl command to download Laravel, install it, and call it daily task tracker Laravel. Once that's done, let's cd into the daily task tracker and then run vendor bin sale app-d so that we start up the containers and open the project in code editor.
In my case, that's PHP Storm. All right, the first thing that I would like to work on is create migrations and models for our entities because we've already got the ER diagram and we should be able to translate that into Laravel's migrations and models easily. Laravel migrations offer a version control system for defining and evolving your database schema. They act as kind of blueprints for your schema and live under database migrations directory. This ensures that your entire team and production or staging environment share the same database structure. Before we set up our migrations, let's talk about database settings because we would need to ensure that the database settings are configured properly before we run migrations.
Your application's database settings are managed in two key files. One is thev file which is your environment variables. This file stores environment specific credentials like the application name, application environment, whether it's local, staging, production, application key, whether the debug mode is enabled or disabled, application URL, log and database related environment variables, session and so on. The second file is the database.php file under the config directory. So if we open that up here, you can basically define your database connections, set the default connection and so on. And as you can see, some of the values are being read from the ENV file using this ENV helper function.
So it reads this DB connection if it exists as the environment variable. If it does not, then it will set SQLite as the default database connection. If we scroll down here, we have the list of connections. And since our environment variable is set to MySQL, this is the connection that uh our application will be using by default. You can add multiple connections for different purposes here if you wanted to like uh separate reporting database for example. If we scroll down here, we see that there is a section for migrations and we see that the table for migrations is called migrations.
Now in future if you wanted to improve performance you could configure separate read and write database connections. That way it would distribute the load by sending write operations like insert, update, delete to a primary database and then read operations like select to one or more replicas. Now moving on to models. In Laravel, a model is a PHP class that represents a specific table in your database. These models live within the app models directory. Models use Laravel's Eloquent OM which is based on the active record pattern and provides a simple beautiful syntax for database interactions. As you can see, this Laravel application does come with the default user model and the migration that comes with it, which kind of gives you a nice head start.
Let's dig into the user model so that I can quickly explain what some of these attributes do before we create our own models and migrations. So first up class user extends authenticatable. The authenticatable class if we inspect is just a class that extends model and has some additional traits that provide some of the authentication specific functionality. Use factory and notifiable traits here. Uh these are the traits which are like prepackaged bundles of methods that you can drop into a class. Has factory trait connects the model to a factory which is a class used to generate fake model data for testing or seating your database and the notifiable trait deals with the notifications depending on the configuration.
Next I want to mention this hidden attribute right here. This is an important security feature because any attribute listed in this uh array will be excluded when the model is converted into an array or JSON such as maybe for an API response. This prevents you from accidentally exposing sensitive data like password hashes or account numbers. Then going down here we have protected function casts which basically is a method for Laravel that does attribute casting. It automatically converts database values to specific PHP or custom types. For example, emailver verify at column which is a database column on the users table will be converted to a datetime object which laravel uses carbon behind the scenes that is a wrapper around the daytime object and it provides more functionality and easy to use methods.
Then we have the password that is set to hashed. And this basically is a security convenience that whenever you set the password attribute on a user model, Laravel will automatically and securely hash the value before saving it to the database, ensuring that you never store plain text passwords. Now, it's important to note that this is just a glimpse into the power of eloquent models. The casts method supports numerous other data types and there are many other attributes or methods that you can define in your models such as the fillable attribute right here which we skipped over.
That is basically to manage the mass assignment and it's a security feature for um your Laravel applications. We'll come back to this a bit later. You could also define the table property to specify the custom table name if it doesn't follow the Laravel standard conventions like uh user model gets translated into the users table. If your table was called something else, you could specify that using the table property and set this to your custom table name. But since we're following Laravel's conventions, we know that the user's table exists and the user model nicely translates to the users table.
Models are also where you define relationships to other models using methods like has many belongs to and so on. For a comprehensive list of all available properties and features, uh, I'd highly encourage you to check the Laravel's documentation because there is really a lot that you can customize and work with in Eloquent. All right, so now let's create models for tasks and categories before we move on to the migrations. We can actually generate models by using an artisan command. So we don't have to manually create them. First, I'm going to open up the shell to my uh Docker container so that we can run artisan commands in there.
We'll do PHP artisan make model task and we can also generate some of the other files along with models. For example, if we wanted to generate a migration for the task model, we could add dash m and that's going to create the task model and the migration for the tasks table. You can check the documentation again for list of available options here as it allows you to generate some other files as well along with your models like controllers, resources, requests, and so on. But for now, we'll just generate the migrations with our models and other files like controllers and uh so on, we'll generate them once we get to those parts.
So let's run this and let's also run it for the category. And if we look at the models directory now, we see that we have the category and we have the task model. If we open them up, we see that they extend the base eloquent model. Now before we write the tasks and categories migrations, let's dig into the existing users migration to see what it's doing and how it lines up with our initial ER diagram. Migration classes are anonymous classes as you can see in here that extend the base migration class and they define up and down methods.
The up method is executed when you run the PHP artisan migrate command. This migration file contains multiple table definitions, which while it's most common to have one migration file per table, you could also group related tables together for convenience. A migration file doesn't just have to create a table. It can also be used to alter existing tables by adding, modifying, or removing columns and so on. Within migrations, you will typically use Laravel's schema builder to manage tables and columns or to check for their existence. You are then given a blueprint instance within the closure which is used to define the specifics of that table like its columns, indexes and keys.
So here for the users table for example we have the table id which is a shortcut for creating an or incrementing unsigned big integer primary key column called id. If we inspect this we see that it simply just calls big increments method passing in the column ID as the default and then big increments simply calls unsigned big integer with auto increment argument set to true. Next we are defining the name column as string which string translates to warcar basically for my SQL to store the user's name. Then we have another string column called email which is marked as unique.
This column will store the email address of the user and the unique method adds a unique index to this column basically to prevent duplicate email entries. Then we have email verified at column which is the timestamp column. A time stamp basically records when certain action happened and in this case uh that records when the user confirmed their email address. It is set to nullable because it will be empty for unverified users and that will be the default value as well. Then we have the password which is the string and this is going to be the hashed users password.
Then we have this remember token method which is a special string column of 100 characters that is used to store the token for the remember me functionality uh which is for the authentication that allows users to stay logged in. So if we inspect that we see that it's just a string called remember token with length 100 and it is nullable as well. Then it's calling the timestamps method which is another helper method that basically adds two columns to the database table called created at and updated at and they're both timestamp columns. If we inspect this we see that it basically just adds two timestamp columns created at and updated at and they're both nullable.
Now looking at this migration and comparing to our ER diagram, we see that we did not account for the email verified at and remember token columns. The next two tables are not even in our ER diagram because these come by default with Laravel, but I would personally add these tables as well as the missing columns to the erd afterwards just to make sure that our ER diagram is complete and reflects our actual application. The password reset tokens table stores the temporary secure tokens generated when a user requests to reset their password via the forgot password feature which we're going to implement.
The sessions table is used when you configure your application to use the database session driver and it is set to that by default. You can change it in the env file using the session driver environment variable and you can also see the other session related configuration in the session.php config file. Moving on to the down method. This method defines the actions required to reverse the migration. It's executed when you run the PHP artisan migrate roll back command. In this migration file, it simply drops the three tables that were created in the up method using the schema drop if exists method.
In a production environment, you should generally avoid rolling back migrations as this can lead to data loss. If you make a mistake, it's safer to create a new migration to fix it because you should think of migrations as an always forward history. The primary use for the down method and the commands like roll back and fresh is for local development. Down method is optional and you could omit it entirely if you wanted to. All right, so let's fill in our task and category migrations. These were created together with the models when we ran the make model command with the dash m option.
But if you wanted to make just the migrations, you could use make migration artisan command followed by the migration name. So first let's fill in the tasks migration file. Larival guesses that you're probably creating a new table called tasks and it creates some boilerplate for you. So schema create tasks. We get the blueprint in the closure and it has the ID column which is the unsigned big integer auto increment primary key and the created at and updated at timestamps. Now before I create some of the other columns first I want to create and define the relationship related columns the foreign keys.
Laravel has excellent shortcuts on creating foreign key columns and one of them is foreign ID. So table foreign id user id constrained users. If we inspect the foreign ID, we see that it's creating the big integer column which is unsigned by default and it's passing the name of the column and setting auto increments to false because this is not the primary key. We can change some other methods on the constraint here and set things like cascade on delete or set null on delete or cascade on update and so on. In this case, we want to cascade on delete because if a user ever deletes their account or if we decide to delete user's account from the database, we want to delete all of their categories and tasks automatically.
It basically prevents orphaned data. So, we'll do cascade on delete. Now, if you expect your table to be huge where a user has thousands and thousands of uh tasks and categories, then you may hit some issues. uh in terms of performance or the database being locked up and so on. But most applications usually don't get to that scale and if it does you could solve that at that time. There are a few ways that you can do that without using things like cascade on delete. All right. Next task can belong to a category. So we have the category ID foreign key.
uh the task doesn't really have to belong to a category because as we mentioned before when building out the ER diagram that a task can belong to either a zero categories or a one category. So we basically want this relationship to be optional. So we'll start with table foreign ID category ID. We'll chain nullable right after to make it a nullable column and then finish it off with constrained and we can specify categories or we can leave it empty and I'll tell you why and set this to null on delete because if the category is deleted we can still keep the tasks uh for the user but simply set the category ID to null.
Now the reason I omitted the table name here is because Laravel will try to figure out the table and the column name based on the conventions because if you follow Laravel's conventions a lot of the things will be figured out for you. Uh like even here we don't need to pass the table users. Uh this will automatically work because Laravel will figure that out. You can inspect this method and dig into it if you want to know how that works. But if the table is null, then it will get the table name from here. And even if that is null, you will see that it will figure out the table here based on the column name.
So if the column is the user ID, it will get the string before the underscore which is the user and make it plural. So user ID will basically become users. And a lot of things in Laravel are by convention. So it will try to figure things out that way. Now, if you do have custom tables or custom columns that are not following the default conventions or maybe you decided to customize the column name and it's not the ID, you could specify that in here. So, you could pass the table name, the column name, even the index name if you wanted to.
All right. So, next let's add the title and description. So the title will be string and description will be text and it will be nullable because we want to have description as optional. Now we need to be able to mark tasks as recurring tasks so that we can easily find which tasks need to be created for the daily automation that we may want to create like create certain tasks on daily basis. So we'll create that as a boolean type and in MySQL boolean translates to tiny integer unsigned if I remember it right. So we'll set this to is recurring and set the default to false.
Then we need a task date column because we need to know if the task is for today or for tomorrow or in a week from now. I might want to plan out next week's tasks for example. So to do that, we're going to create the datetime column and we'll call it task date. And we also need a column to know when the task is finished, which is completed at. So we'll do date time again and call it completed at. And we'll of course keep the timestamps as well to know when the task was created and when it was updated.
Finally, I want to add explicit indexes to our foreign keys. Now, in MySQL, it will automatically add indexes on foreign keys if they don't exist, but I like to have them defined explicitly anyways. So, we'll do table index user ID and table index category ID. Now, let's fill in the category migration. So, let's open that. In here we have the ID, we have the timestamps. We need to add the foreign key to the users table and that is the user ID column. So we'll have foreign ID, user ID, constrained, cascade on delete. Then category has the name and we'll have timestamps.
I also would like to add the index here. So we'll do table index user ID. All right. So now we can run the PHP artisan migrate to migrate our tables and make sure that they get created. So let's open the terminal. We'll run PHP artisan migrate. And as you can see we are getting an error here. Now if we scroll up here we see that the other tables were created correctly but it's failing on creating the tasks table and it's saying that it is failing to open the reference table categories. And that makes sense because we are saying here that we want the category ID to be the foreign key which references the categories table but categories table hasn't been created yet because categories is after the tasks.
Now what we can do is that technically we could create categories first before tasks and then create tasks. The other option would be to basically skip that and add the category ID afterwards. But the easiest fix is to have the categories be created first. So what I'll do is I'm just going to rename this. As you can see, these tables are um prefixed with the timestamp on when the migration file was created. The default ones that come with Laravel are just prefixed with um ordered numbers so that they are always at the top. Um but we can make this be a little bit earlier than the create tasks.
So we'll do 158. And now if we run the migrations again, it should create the categories table and then try to create the tasks table. The problem is it has already created the tasks table. Uh but it is failing at adding the foreign key. So if we open the database here, so I'm going to add a data source MySQL. So we can inspect the database table. Let's open the tables. And as you can see, we have the tasks table. it just doesn't have that foreign key because it failed at that step. So what I can do is I can delete this table and then recreate and then rerun the migrations or we can run PHP artisan migrate fresh to basically remove all of these and start fresh.
So let's run that. And as you can see now it worked without any issues. If we reload this database, we see that we have the task table, the users, and the categories as well. Now, there are more migration related commands that are available that you can execute. You can look into them on your own um in the documentation. You can basically roll back migrations. You can roll back only certain migrations or certain steps. You can start fresh and basically reset your database like I just did and so on. Now over time your migrations directory can grow quite large which can slow down the processes like automated testing.
You can squash all the existing migrations into a single SQL schema file using PHP artisan schema dump-p prune command. And what this does is that this is going to take your entire database schema, put it into one file, and then the prune basically removes the migration files so that you don't have a lot of migration files that it needs to go through every time you're trying to run a PHP artisan migrate fresh for example or you're running tests. So if we run that we see that all the migrations are gone and now we have the schema here called MySQL schema and this contains basically all of our tables.
Now if I run PHP artisan migrate there is nothing to migrate because we've already migrated. But if I run PHP artisan migrate fresh, it is basically going to drop all the tables, then look into the schema if the schema exists, which it does, and loads that right here, executes it, and then sees if there are any other migrations that it needs to run. And if it does, it will execute. If not, as you can see, we'll get nothing to migrate message. All right, so with that, let's now move on to implementing models. Let's implement the task model first.
I'm going to close this out and close this out as well. Let's open the task model. And the first thing that I want to add here is the casts method. As you can see, my uh copilot is already suggesting me a lot of things which I'm not going to use for now. So, we're going to define the casts method. You could also define this as casts property, but I prefer the casts method. So we'll be casting is recurring to boolean. So we'll do is recurring to boolean and the task date and the completed at date to date time objects which is the carbon instances basically.
So we'll do completed at date time and task date time. Note that created and updated at columns are automatically cast to carbon objects. So I don't need to specify them in here. Next, let's define relationships. Now looking at our ER diagram, we see that user can have many tasks, but a task can belong to a single user. So since we're defining relationship within the task model, we need the belongs to relationship because task belongs to user. So since task belongs to user, the method name will be the user. That's the name of our relationship. And the copilot is right here that it needs to call the belongs to method and pass the related model as the first argument which in this case is the user model.
Let's add the return type in here that it returns belongs to object. And if we wanted to specify the columns, we could define the foreign key here called user ID. And then the owner key is the column on the related table which is the users table. So that is the id and then the relation if you want to define the relation but all of this are automatically figured out by eloquent uh if you follow conventions like the user ID the ID is all figured out by Laravel. If you don't follow the standard conventions or have some custom names that's when you would specify those names.
But if they do follow the convention, then you can basically skip it and let Laravel do its magic. If we inspect the belongs to uh method, if the foreign key is set to null, we see that it basically gets the relation name, which if it's null, it guesses the relation based on the convention again. So if we inspect that, we see that it gets the caller function name, which in this case would be the user. So if we have the user, it suffixes it with the underscore and then the key name which is the id.
So it becomes the user ID. Then the owner key if it's not set it's getting the key name of the related object which is the ID. The get key name basically gets the primary key uh column's name and that is the ID by default. And then once it has all the pieces it creates the belongs to object in here. Great. So what we have here is basically an inverse relation of the has many relation or one to many relation in laral. If we switch over to the user model, we can define the has many relationship here to the task model because user has many tasks.
Task can belong to a single user but the user can have many tasks and we can define that relationship on the user model as well. So we could do public function tasks which is the plural because it's has many and this will return has many object which is the relationship and we'll need to call has many method and pass the related models class name which in this case is task. Again you could omit uh the columns here like the foreign key the local key but you could specify them if you don't follow the conventions. The foreign key would be the user ID because that's the foreign key on the tasks table and the local key would be the primary key that that user ID references to which is the ID of the users table.
Now Laravel has other types of relationships as well like onetoone using has one and belongs to methods one to many using has many and belongs to which we just went over with. uh you can have has one through and has many through which use intermediary relation to access distant relations. You can define many to many using belongs to many relation which uses pivot tables. You have polymorphic relationships and so on. I would say most of the time you'll be working with onetoone or one to many relationships with has one and its inverse belongs to method or has many and its inverse belongs to.
Now when dealing with pivot tables you may also use many to many relationship with belongs to many relation. I rarely need to define distant relationships on models using has one through or has many through relations. They basically allow you to have a relationship between user and some distant model like task attachments. So task would be the intermediary relationship and then you'd be able to define belongs to many relationship on the user model and access attachments associated with the user tasks. All right. So let's now define the category relationships which are similar because user can have many categories as they can create many categories and categories can belong to a single user since we want users to manage their own categories.
So we're going to duplicate this here and call categories and simply change this to category model. Now on the task model we need to have belongs to category relationship because category has many tasks but task can only belong to one category. So we're going to clone this and call this category and again change this model to category. Now moving on to the category model, we can define the inverse relation for the user because category belongs to user. And since category can have many tasks, that would be the has many relation for tasks. So what I'm going to do is I'm going to copy this and put it here.
And I'm going to copy this and put it here. And I think this is it. We have our models set up properly. We don't need to add more properties uh to the category model or to the task model or even to the user model at this time. We may add more things later on as we start building things out. Now, I do want to take a quick detour and install an IDE helper package. It's basically a quick productivity tip that I think can come in handy and be useful, especially if you're working with IDE like PHP Storm.
As you use eloquent in controllers and in other places you will notice that it doesn't have some of the autocomplete like category has the columns right the categories table has the ID user ID name created and updated and the tasks has all these columns and the users has columns and so on. But when you create the category object like when you do something like category equals new category and then you try to access the ID property it it is not autocompleting and you cannot click into it which means that you would need to define the PHP doc here this way.
So we have the int id string name int user ID and now this is clickable and it will autocomplete. You could also do the same for the relationships because the way you access relationships on the models is not simply using this right if you do tasks it returns the object of belongs to but if you want to access the list of tasks you would need to access them this way and this doesn't autocomplete or is clickable. So you would have to define that property in here which would be an array of tasks this way. And now it autocompletes.
We're going to get to all of that in a little bit once we start building out controllers and uh start calling some of the methods and creating tasks and categories and all that. But to make this easier, there are some tools available and one of them is called Laravel ID helper by Barry and you can install that using composer. So, I'm going to remove this and remove this as well. And we're going to open the terminal and we'll do composer require-dev because we only need this for development. Laravel id helper. Hit enter. And this is going to install that.
Now, you can check the documentation. There are some really cool commands that you can use. Two commands that we're going to use are the ID helper generate and ID helper models. The ID helper generate is basically to generate autocomp completion for facads. So we'll do PHP artisan ID helper generate. This will create a new ID helper uh file in here. And it basically is a helper for the ids like PHPtorm to autocomplete things when using uh facads for example. You would need to make sure to add this file to your git ignore so that it doesn't get committed to your repository.
You don't need to commit this to repository because it's for your local development. The other command that I'm going to run is the one to add automatically the PHP docs to our models. So we'll do PHP artisan id helper models-rw. This way it will overwrite the php ducks. So if we change this to something like string and we run this again we come back we see that it gets overwritten. it properly type hinted the created at updated at that they are carbon instances and they're nullable and then we have the eloquent collection of uh tasks which I was type hinting it wrong using array because this actually doesn't return an array it returns a collection and we have the user relationship and some of the helper methods that you can call on the category model it also has this mix in eloquence so that you have some uh methods available from eloquent on the category model.
If we go to the task, we have the same thing here. So, I'm just going to import these. And the user will also have these as well. I'm just going to import them and then format them. And like I said, you can check the documentation for more options. But once you have this, you will notice that your ID gets a really nice upgrade that you can now click through stuff. Now, if you use VS Code, there is Laravel's firstparty VS Code extension. And if you use PHPtorm, Laravel idea plug-in recently became free that supercharges your ID as well.
So, at some point, some parts of this package may no longer be needed, but still wanted to cover it so you have additional tool in your toolbox. Uh, there may be some new features coming to this package that might still be useful. And even though AI is really cool, I still prefer to run the command and have all of these generated automatically. All right, so I think next step is to work on authentication because to create tasks and categories, we need an authenticated user. And we're going to spend quite some time building out the authentication for this application because if we look at the diagram, authentication is like one of the core items or core components of our application.
Now you might be wondering Gio Laravel has all kinds of starter kits uh ones with React VU that come with authentication also things like Breeze Jetream and so on. Why are we building authentication from scratch? Now while starter kits are fantastic for getting up and running quickly, building authentication from scratch teaches you the fundamentals. When you understand how authentication works under the hood, you will be better equipped to customize it, debug issues, and make informed decisions about security. Also, in many professional environments, you might inherit code bases that don't use starter kits. So, this knowledge, in my opinion, is invaluable.
Now, to clarify, when I say from scratch, I don't mean building the entire authentication from scratch, from the grounds up. What I mean by that is from scratch within the context of Laravel. Laravel does provide a lot of authentication functionality and it pieces together these authentication uh features in the starter kits to provide you full authentication system. What we're going to be doing is that we're going to be building the same thing that the starter kits provide you by default. So that's what I mean when I say we're going to be building it from scratch.
We're not actually building the entire authentication thing from scratch. We're just going to be piecing together some of the authentication related features that Laravel already offers to build the similar system that some of those starter kits provide. Now, Laravel's authentication system is built on several core concepts that work together seamlessly. These are guards, providers, and middleware. Guards determine how users are authenticated for each request. Providers define how users are retrieved from your persistent storage. Middleware acts as filters that can inspect and modify the requests before they reach your controllers. And I kind of want to dig into the guards and providers a bit and explain the overall authentication flow.
Think of guards as kind of like gatekeepers that determine is this request coming from an authenticated user. Laravel uses session guard by default which maintains authentication state using sessions and cookies. You can also create custom guards for different authentication methods like API tokens, JWT and so on. Providers define how users are retrieved from your persistent storage. Providers basically answer the question, how do I fetch user data from the database? The authentication configuration is located under the config.php file and this file contains well doumented options for tweaking the behavior of Laravel's authentication services. I do want to go over some of these settings.
So first we have the default authentication settings. This is the default guard to use the default password broker. Then we have the authentication guards here. Basically determining how users are authenticated for each request. So we have the web guard which is basically sessionbased authentication. As you can see the driver is set to sessions and the provider is set to users. You can add more guards here for different authentication methods like the API guards, admin guards, JWT etc. Then if we scroll down here we have the provider section and here we have the users provider defined.
In our case the default is set to users and the driver for that is set to eloquent as you can see here and the model is set to the user model. Then if we keep scrolling here we have the password reset configuration and we'll talk more about password resets in a little bit but in here again we have the users. The provider is set to users. the table that is used for password resets. This is the table that our migration has created which uh comes with Laravel installation and then we have things like the default token expiration minutes and these are in minutes.
So it's basically 60 minutes to expire the token and then the throttling is how many seconds it needs to wait between the password reset requests. Finally, we have the password timeout which is set to 3 hours by default. So as you can see, Laravel gives you the single web guard as the default uh which is perfect for our needs and we don't need to create any additional guards. Now let's go through and understand how authentication actually works under the hood when someone logs into your application through a web browser. So the first step typically is that user submits the login form, right?
User enters their email and password and hits submit. When that happens, Laravel typically receives this data in the controller. Of course, depending how you have your routes set up. The next step is the credential verification. Laravel uses the configured provider eloquent in our uh case to retrieve the user from the database and verify the password hash uh to make sure that it matches to whatever the user entered. The next step is the sessions. If credentials are correct, Laravel stores the authentication information in the user session. The session data is typically stored on the server in files, database or reddis.
And that can be configured via the session.php configuration file. As you can see, we have the default driver set to database. And it's also set to database in our ENV file. So if we scroll down here, we have the session driver set to database. The next step is the session cookie. Laravel basically issues a session cookie to the browser. This cookie contains only the session ID, not the actual authentication data. The cookie might look something like Laravel session equals and then the session ID. Then on subsequent requests, the browser automatically sends the session cookie and Laravel reads the session ID from the cookie, retrieves the session data from the server storage using that ID and then checks if authentication information exists in the session and considers the user authenticated if the session contains valid O data.
Now to keep things secure, we need to ensure that we regenerate session IDs anytime there is a change in the user's authentication state to help prevent session fixation attacks and user logging in does qualify as authentication state change. So we need to implement logic somewhere in our uh login method to regenerate the session ID. The section fixation attack that I just mentioned is an attack where a malicious user tricks a victim into using a session ID that the attacker controls. There are some other secure controls and configs that you can set up like you can set up the secure cookie flags like HTTP only secure same site and as I mentioned these are configurable in the session.php.
So if we scroll down here we see that we have the default session lifetime. We can set if we want to expire the session on close which is set to false by default. You can encrypt sessions if you wanted to. The table that's used for the sessions. Uh then in here we also have the session cookie. So the name of the cookie will start with the application name dash session. So in this case the default will be laral- session unless we set the app name to something else. The app name is also set to laravel by default in the env file.
But if we wanted to change this to something like daily task tracker, then daily task tracker would become the prefix of the cookie name. It would turn the name to a slug. So it would be daily task-tracker dash session. Then we have the secure option which is the session secure cookie. is uh make sure that session cookies are only sent back to the server if the browser has a secure HTTPS connection. I would recommend to set this to true in production. Uh then we have the HTTP only which is set to true by default. This means that it prevents JavaScript access to the session cookie.
Then we have the same site policy which is set to locks by default. And you can read more about what the different uh options uh really mean here. It helps prevent certain CSRF attacks and controls when cookies are sent on cross-sight requests. Now, I do have a separate video where I go over in detail about authentication and session security. So, if you have the time and have not completed the PHP series, definitely check that video up in my learn PHP the right way series. It's also important to understand that Laravel provides different authentication approaches for different use cases.
So for browser authentication, which is what we're building, it uses cookies and sessions, which is perfect for traditional web applications. It handles CSRF protection automatically. Sessions are stateful, meaning that Cyber remembers the authentication, and it uses the session guard by default. The other form of authentication is the API authentication, which uses API tokens instead of cookies and sessions. Tokens are sent with each request typically in headers and each request must be authenticated because it is stateless. Laravel has helpful packages like passport and sanctum that help with building APIs and its authentication with API tokens. Passport is an ooth to authentication provider while laral sanctum is the simplified version of tokenbased authentication without the ooth to complexity.
This type of authentication is perfect for mobile apps, SPA or external integrations. Now, something to mention is that you could technically use sessions for certain SPA depending on how you're building your single page applications. And Laravel Sanctum allows you to do that, but that's beyond the scope for this project. Now, Laravel does provide built-in authentication services that I mentioned before. It provides a lot of authentication related features out of the box and they can be accessed via the O and the session facads. They can be used to check if user is authenticated, get authenticated user, log user in, attempt to authenticate user with credentials, log user out, regenerate session IDs and handle session security and so on.
All right, so with all that theory out of the way, let's see how to implement this step by step. So, first we're going to set up our routes and then work on the controllers. So, I'm going to close this out. Let's open web.php and define our authentication routes. So, we're going to have the get login route which basically is /lo. That is what's going to display our login form. Then, we also need the post /lo which is the request that happens when user clicks the submit button. We also need the get register route to show the registration form to the users who don't have an account.
And we need post /register to handle the submissions to that form. Now all of these routes should only be accessible to users that are not logged in. So technically guest users. Laravel has a middleware called guest which automatically redirects authenticated users to the dashboard. And that's the great way to group the routes. So I'm going to group these routes by the middleware. So we're going to do route middleware guest and then group and then provide the closure within which we'll define all of these routes. So let's put this in here and let's define them one by one.
So, we're going to have route get slash login ocontroller class show login form. And we'll name the route login. And I think that's good enough. Next, let's create our protected routes. And these are the routes that only authenticated users can access. And whatever copilot is autocompleting here is technically correct. So we can group the routes again by the O middleware and O middleware basically automatically redirects unauthenticated users to the login page. So in here we have the guest middleware. So if you are unauthenticated user you can access this page you are guest but if you're authenticated user then and you try to access these routes you will be redirected to dashboard.
And here it's the other way around. If you're unauthenticated user and you try to access these routes, you will be redirected to the login page. But if you're authenticated, then you can access these routes. So we have the post request to the logout route to log the user out and this is good enough. And we have the get request to the dashboard which will call the index method on the dashboard controller and we'll call it dashboard. I also want to add the redirect for the root route and that's basically when user just visits the homepage.
In that case I can use the redirect method on the route facade. So we'll do route redirect and say when somebody visits our root route redirect them to dashboard. The power of this route organization is that Laravel's middleware system automatically handles the authentication logic for us. We don't need to check if users are logged in inside our controllers because the middleware takes care of that for us before the request even reaches the controller methods. Let's generate our O controller. We can use the artisan command for that. So we'll do PHP artisan make controller off controller.
And let's import this in here. And now let's go here and create the view methods first. the things that render forms. This will return view of login. The next method is show registration form and this will return off register. Now, of course, you could split these into different controllers if you wanted to have registration specific controller and then the login specific controller. For now, I'm going to keep everything under the O controller and then later we can refactor if we see the need for it. Before we jump into creating individual views, I want to take a moment to talk about blade components.
Think of components as reusable building blocks for your UI. Instead of copying and pasting the same HTML structure across multiple files, we can create a component once and reuse it everywhere. This keeps our code dry and makes maintenance much easier. Laravel offers class-based components and anonymous components. Anonymous components are just blade files that live in the resources views and then the components directory, while class-based components have an accompanying PHP class for more control. You could also create inline components, which are basically just PHP component classes that render the HTML within the render method of that component class, but for this project, we'll stick with the anonymous components because they're simpler and perfect for our needs.
If you watch the previous project videos, you should already be familiar with this. If not, I recommend spending a few minutes to either watch that section or check the documentation to familiarize yourself with blade components. So, let's start by creating our layout components. We'll need two different layouts. One for authenticated users and one for guests. This separation makes sense and you probably will see this in a lot of projects because authenticated users might have navigation menus, user dropdowns and other elements that guest users don't need. We can use the make component artisan command to create the component.
So we'll do PHP artisan make component and by default this will create class-based component but we can add d- view and this flag will basically tell laravel to create an anonymous component which is a component with only a blade template and no class. So let's set the component name here. We'll call it guest layout. And we'll do the same thing for the app layout. If we scroll down, we see that the new components directory was created under the views directory and we have app layout and guest layout. When you need to use blade components in your blade templates, you need to reference them using x-prefix followed by the whatever the component name is.
So for example, for the guest layout that would be x dash guest dash layout. Laravel basically maps X- whatever to the appropriate component. Notice how Laravel automatically converted the Pascal case in here to kebab case for the component file name. If we specify the directory here in Pascal case as well, Larl would convert that to kebab case and create the directory under it as well. So if we maybe had layouts instead like this, Laravel creates the directory as well. Uh let's create something like my layouts and this will be my dash layouts. We don't need to separate these like this for now.
I'll just keep them simple under the components directory. All right. So let's open the guest layout blade template. And I'm actually going to paste in a simple guest layout blade template from Laravel Breeze, which is one of Laravel's older starter kits, and it has the blade version, which I really like. So, I'm just going to copy uh the blade templates from there to not create them from scratch. That way, we're not spending or wasting a lot of time building the HTML markup. You can go to GitHub larvo/breeze and then go to stubs and you will see a bunch of options here.
So you have inertia API livewire etc. So we are going to be using default and within the default we have resources views and we can copy some of these uh components and build our UI in a similar way. So we'll go to layout guest blade and we'll just copy this over. Now some things that stand out here in case you don't know variables enclosed with double curly braces like here is how we basically echo the data to screen in a safer way. Blade here handles the escaping of content helping to protect us from excss attacks.
Config helper function loads the app config file and the name property from the app config file. And if it's not set, it will use largo by default. So it's essentially loading this app.ph. PHP and the name property. And if this wasn't set, it would use Laravel by default. We'll change this to daily task tracker just in case, but we've already set this in the ENV file, so we should be fine. I also want to be able to customize the title for different pages if needed. So, we can use the title variable here. Something like this.
And if that's not set, then load it from the config. Then we have the vit directive here and this directive tells Laravel to include our CSS and JavaScript assets. Vit is the build tool that handles asset compilation and hot reloading during development. Then this here basically loads the fig tree font family from Bunny Fonts. The pre-connect link helps improve performance by establishing early connections to the font server. The CSRF meta tag here can be used by libraries like jQuery to automatically add the CSRF token to all the request headers when making Ajax calls. Axios, which is a popular promisebased HTTP client for JavaScript, automatically does that and includes it in the header.
Speaking of CSRF, let's talk a little bit about CSRF attacks. CSRF stands for cross-sight request forgery. It's a type of attack where a malicious website tricks a user's browser into making unwanted requests to a site where the user is authenticated. It can basically trick you into performing actions on a website without you realizing it. For example, let's say you are logged into your social media account in one browser tab, scrolling through your feed. You click on a link someone shared about uh some breaking news that you can't wait to read about. When you click on it, it opens what might look like a legit news website with some content on there.
What happens behind the scenes actually is that this supposedly legit news website contains a hidden form that automatically submits a request to the same social media site to make a post from your account saying something like, "Check out this amazing weight loss supplement. Use my code for discount." And put some kind of suspicious link. Now, if that social media site does not have proper CSRF protections, it would think that it's receiving what looks like a legit request from you since your browser sends your login cookies along with it. So, it posts the spammy tweet to your timeline.
Your followers see this post and think that you're endorsing this product. By the time you realize that there has been an issue, the damage has already been done. Now, this could work because the site trusted that the request came from you because your browser automatically included your authentication cookies and the malicious site exploited this trust to make your account perform an action you never intended to make. So, how do we prevent such attacks? The solution is CSRF tokens. A CSRF token is basically a unique unpredictable secret that gets generated for each user session and regenerated whenever session is regenerated.
When your application makes a post, patch, put, or delete requests, it needs to include this token so that the server can check if the token matches what it expects. Since malicious websites can't know what your token is, they can't include it in their fake requests, and those requests get automatically rejected. Unless your site also has XSS vulnerability at which point you would have bigger issues. But luckily, Laravel helps with that as well. Laravel makes CSRF protection incredibly easy because first there is the CSRF blade directive that you can put in your forms which will add the hidden input field with the token value.
Laravel also provides this CSRF token helper function if you need to get the token programmatically which as you can see is used here within this meta tag and the meta tag can be used by JavaScript to get the CSRF token and use it in HTTP request. Laravel has middleware called verify CSRF token which is included in the web middleware group by default and will automatically run on all postput patch and delete requests. This middleware grabs the token from either the form data that is passed via the hidden input field with name underscore token or from the request headers uh like the X CSRF token or XXSRF token and then compares it with what's stored in the session and if they don't match it will throw the 419 status error and it will block the request.
If it matches, then Laravel knows that it's coming from the legit user and will let the request go through. Laravel stores the current CSRF token in an encrypted cookie called XSRF token and Axio along with some JavaScript frameworks and libraries automatically read this cookie and include it as an XXSRF token header in your requests. So you don't need to do anything extra. However, if you were using JavaScript's fetch API or jQuery's Ajax methods instead of Axios, you could pull the CSRF token from this meta tag and then add that to your request header. Now, let's say that you wanted to exclude the CSRF protection on some uh URLs.
You could do that by opening the app.php PHP from the Bootstrap directory and within the with middleware here you could call validate CSRF tokens and provide list of URLs to skip the CSRF protection check for example. You could also simply put such routes outside of web middleware group and that would basically have the same effect because if we go to the configuration middleware.php PHP and go to the list of middleares which are I think somewhere here middleware groups. So we have the web middleware group right here and the API middleware group. In the web middleware group we have that validate CSRF token which basically simply extends the verify CSRF token.
So if you wanted to skip the validate CSRF token entirely on certain routes, you could move them out of the web route if they don't need to be under the web middleware group. Maybe it's an API request. You would put it under the API middleware group and that doesn't have the validate CSRF token middleware. But be cautious which routes you're going to exclude from CSRF protection because you might be opening up yourself to CSRF attacks. All right, so let's get back to our layout. In body, we're setting the default font. This div container here basically just centers the content with logo on top and the content from the actual page that will use this layout will go within here within this slot.
That's what this slot variable does here. Basically, it is where the components content get injected. When we use this layout component, whatever we put between the opening and closing tags of this component will replace this slot variable. Now notice that we are using white background but the dark gray background on the dark mode. Tailwind makes it easy to design systems with light and dark modes and I have my system set to dark mode. So when I will open the browser, it is going to load this page in dark gray background and not white. Now we have this missing X application logo here which technically we don't need for this case.
At least for now we don't need it. So what I'm going to do is I'm going to comment this out and instead we're going to add like a a title which will be our application name which is daily task tracker. We could set the same gray color in here and we'll customize it later on if needed. All right, so now let's implement the login and register blades. I will again copy the templates from the Laravel breeze so we don't build it from scratch and then adjust them as needed. So let's create the O directory and we'll create the login blade template within it.
I'm going to paste in the template from Breeze and I'm going to also copy some of these components from Breeze as well so that it works as expected. We have the primary button. We have the input error. We also have the input label. And we have the text input. Let's close this out for now. And I think we have all the components here. We're missing the O session status. All right, I want to quickly explain some important blade component concepts here that you will see throughout the templates because if you're not familiar with them, the syntax might look a bit confusing.
The first thing that you'll notice is the props directive at the top of some of the components. This is how we declare what properties our component expects to receive. When you create an anonymous blade component, you need a way to tell Laravel which attributes should be treated as data variables that you can use in your components logic versus which attributes should be passed through as HTML attributes. The props directive is how you do that. Basically, when you write props messages at the top of the component, you're basically telling Laravel that, hey, when someone uses this component and passes the messages attribute, I want to access that as a PHP variable called messages inside the component like this.
You can also provide default values like if we open the text input here, we have the disabled prop and the default is set to false. The another thing that you will notice is the attributes variable here which is not defined in the props. The attributes is Laravel's way of collecting all the attributes that were not defined in your props directive and put them in sort of a bag of attributes. Think of it as a container that holds all the extra HTML attributes that someone might pass to your component. So if someone uses your text input component for example and adds ID or placeholder attributes or any other HTML attributes, those all get collected into the attributes variable or the attributes bag even though we didn't explicitly declare them as props.
The merge method here on the attributes bag allows us to provide default CSS classes while still letting us merge additional classes when we use the components. So here we're saying that use these default classes right here. But if the user passed their own classes, merge them together. The class attribute gets the special treatment when merging. Instead of overwriting classes, it combines them. For other attributes, it works more like a fallback system. So the values provided to the merge method will be considered the default values of the attribute and instead of being merged, they will be overwritten.
All right. So, let's copy…
Transcript truncated. Watch the full video for the complete content.
More from Program With Gio
Get daily recaps from
Program With Gio
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.









