Building a TODO Application with Laravel, Livewire, and Volt
Learn how to create a simple yet powerful TODO app using Laravel, Livewire, Volt, and Laravel Breeze for authentication
In this guide, we'll walk through the process of building a TODO application using Laravel, Livewire, and Volt.
We'll use Laravel Breeze for authentication and Neon Postgres as our database.
By the end of this tutorial, you'll have a simple yet fully functional TODO application that allows users to create, update, and delete tasks.
Prerequisites
Before we begin, ensure you have the following:
- PHP 8.1 or higher installed on your system
- Composer for managing PHP dependencies
- Node.js and npm for managing front-end assets
- A Neon account for database hosting
- Basic knowledge of Laravel and Livewire
Setting up the Project
Let's start by creating a new Laravel project and setting up the necessary components for our TODO application.
Install the Laravel Installer
The Laravel installer is a command-line tool that simplifies the process of creating new Laravel projects. If you don't have it installed, run the following command:
To verify the installation, run:
This should display the Laravel installer version, confirming that the installation was successful.
Creating a New Laravel Project
Open your terminal and run the following command to create a new Laravel project using the Laravel installer:
Follow the on-screen instructions to create the project by selecting the following options:
- Starter Kit: 'Laravel Breeze'
- Breeze stack: 'Livewire (Volt Class API) with Alpine'
- Dark mode: Based on your preference
- Testing framework: Pest
- Database: PostgreSQL
This command creates a new Laravel project named laravel-todo-volt
with the selected options and installs the necessary dependencies like Breeze, Livewire, and Volt.
After the project is created, navigate to the project directory:
This can also be done directly via composer
instead of using the Laravel installer, but the Laravel installer provides an interactive setup process that simplifies the initial project setup rather than running multiple commands manually.
Setting up the Database
Update your .env
file with your Neon database credentials:
Make sure to replace your-neon-hostname
, your_database_name
, your_username
, and your_password
with your actual Neon database details.
Once you've updated the .env
file, run the following command to create the default tables:
This will create the necessary tables in your Neon database.
Compiling Assets
Laravel Breeze uses Vite for asset compilation. Run the following commands to install dependencies and compile assets:
Keep the Vite development server running in the background as you continue with the next steps.
Creating the TODO Model and Migration
A model in Laravel represents a database table and allows you to interact with the table's data using Eloquent ORM. The migration file defines the structure of the database table and is used to create or modify the table.
The standard convention in Laravel is to create the model with singular naming and the migration with plural naming. For example, a Todo
model would correspond to a todos
table in the database, User
model to users
table, and so on.
Now, let's create the Todo
model along with its migration:
Open the newly created migration file in database/migrations
and update it to include the necessary columns for the todos
table:
This migration creates a todos
table with columns for the todo title, description, completion status, and a foreign key to the users
table.
The onDelete('cascade')
method ensures that todos are deleted when the corresponding user is deleted so that we don't have orphaned records left in the database.
Update the app/Models/Todo.php
model file to include the relationship with the user and the fillable fields:
The $fillable
property specifies which fields can be mass-assigned when creating or updating a todo task. This helps protect against mass-assignment vulnerabilities and ensures that only the specified fields are allowed to be updated.
Also, update the app/Models/User.php
file to include the relationship with todos:
This method defines a one-to-many relationship between the User
and Todo
models, allowing us to retrieve all todos associated with a user. For example, Auth::user()->todos
will return all todos created by the authenticated user.
Now, run the migrations to create the todos
table in your Neon database:
Creating the Volt Component
Volt is a new addition to Livewire that allows you to define Livewire components using a class-based API.
This makes it easier to organize and manage your components, especially for larger applications with many components.
Let's create a Volt component for our TODO list:
This command creates a new Volt component file at resources/views/livewire/todolist.blade.php
. This single file will contain both the component's logic and its template compared to the traditional Livewire components where the logic is in a separate PHP file.
This Volt component combines the component's logic and template in a single file. Let's break down the key parts:
- We use
state()
to define reactive properties likenewTodoTitle
. - The
computed()
function is used to create a dynamic property for fetching todos. - The
action()
defines methods that can be triggered from the template. - The template section uses Livewire directives like
wire:submit
andwire:click
to interact with the component's logic.
This approach allows for a more self-contained component definition, making it easier to understand and maintain your Livewire components rather than having the logic and template in separate files.
Integrating the TODO List into the Dashboard
Now that we have the Volt component ready, let's integrate our TODO list into the main dashboard.
Open resources/views/dashboard.blade.php
and replace its content with:
This integrates our TodoList
component into the Breeze dashboard by using the livewire:todolist
directive. When you visit the dashboard, you should see the TODO list component displayed on the page.
As we have used the auth()->user()
method in the TodoList
component to fetch the user's todos, each user will see their own list of todos when they visit the dashboard, however we have not yet implemented any authorization to ensure that users can only manage their own todos. We'll cover this in the next section.
Adding Authorization
To ensure users can only manage their own todos, let's implement some basic authorization.
In addition to the authentication provided by Laravel Breeze, Laravel provides an authorization system that allows you to define policies for your models. These policies define the rules for accessing and managing resources, such as todos in our case.
Create a new policy for the Todo
model using the following command:
Open app/Policies/TodoPolicy.php
and update it to define the authorization rules for updating and deleting todo items:
Here, we define two methods: update
and delete
. These methods check if the authenticated user is the owner of the todo by comparing the user's ID with the todo's user_id
. If the user is the owner, the method returns true
, allowing the user to update or delete the todo. Otherwise, it returns false
and denies access.
Now, update the TodoList
component to use these policies when toggling completion status or deleting todos:
In these updated actions:
- We use
auth()->user()->cannot('update', $todo)
andauth()->user()->cannot('delete', $todo)
to check if the current user is authorized to perform the respective actions based on theTodoPolicy
rules. - If the user is not authorized, the function returns early without performing the action preventing unauthorized access.
- If the user is authorized, the action proceeds as before allowing the user to toggle completion status or delete the todo.
This way you can make sure that users can only toggle completion status or delete todos that they own, as defined in the TodoPolicy
.
Writing Tests for Your TODO Application with Pest
Testing is an important part of the development process. There are a few different ways to write tests in Laravel, including PHPUnit and Pest. Choosing the right testing framework depends on your preference and the requirements of your project.
In this section, we'll cover writing tests using Pest, a more expressive and minimalistic testing framework for PHP.
Pest provides a Livewire plugin, which allows you to write tests for Livewire components in a more readable and concise way. To install the Pest plugin for Livewire, run the following command:
Setting Up the Test Environment
For this example, we will use an in-memory SQLite database for testing. This ensures that tests run quickly and do not affect your production database.
However, to learn more about testing in Laravel along with Neon branding, check out the Testing Laravel Applications with Neon's Database Branching. This guide will help you set up a separate database branch for testing, allowing you to test your application with real data rather than an in-memory database.
To get started, ensure your .env.testing
file is configured to use an in-memory SQLite database for testing:
This will allow us to use the RefreshDatabase
trait to reset the database before each test, ensuring a clean slate for testing.
It is important to note that the
RefreshDatabase
trait will clear the database before each test, so make sure to use a separate database for testing to avoid data loss as the database will be reset for each test meaning that any data that you have in the database will be lost.
TodoFactory
Creating and Using a Factories in Laravel generate sample data for models, useful for testing and database seeding with realistic data thanks to the Faker library.
Let's create a factory for our Todo
model which will generate random todo items for testing:
Update database/factories/TodoFactory.php
to add the necessary fields and states for generating todo items:
This factory generates random todo items with titles, descriptions, and completion status. We've also defined two states: completed
and incomplete
to create todos with specific completion statuses.
Creating Feature Tests
Let's create a feature test file for our TODO list functionality using the following command:
This command creates a new test file at tests/Feature/TodoListTest.php
. Open this file and replace its contents with the following:
These tests cover the following scenarios:
- A user can view their todos when visiting the dashboard.
- A user can delete a todo that belongs to them.
- A user cannot delete a todo that belongs to another user.
The actingAs()
function is used to authenticate the user before interacting with the Livewire component. This ensures that the user is authorized to perform the actions.
The livewire()
function is used to interact with the Livewire component and make assertions based on the component's state.
Running the Tests
Again, before you run the tests, note that the RefreshDatabase
trait will clear the database before each test, so make sure to use a separate database for testing to avoid data loss like an in-memory SQLite database or a Neon database branch.
You can run these tests using the following command:
This will execute the tests and provide feedback on the results. Writing tests helps ensure that your application behaves as expected and catches bugs early in the development process.
Conclusion
In this tutorial, we've built a simple yet functional TODO application using Laravel, Livewire, and Volt. We've covered:
- Setting up a new Laravel project with Breeze, Livewire, and Volt
- Creating a
Todo
model and migration - Implementing a Volt component for managing todos
- Integrating the TODO list into the dashboard
- Adding basic authorization to ensure users can only manage their own todos
- Writing tests for the TODO application using Pest
This application provides a solid foundation for a TODO list, showing the power and simplicity of Laravel, Livewire, and Volt. From here, you could expand the functionality by adding features such as:
- Due dates for todos
- Categorization or tagging of todos
- Sorting and filtering options
- Sharing todos with other users
- Assigning todos to specific users