Lesson 3
Create New ToDo Items
Create New ToDo Items

Welcome! In our previous lesson, you enhanced your ToDo application by enabling the retrieval of individual ToDo items using their ID. Now, it's time to take a step further by adding the functionality to create new ToDo items. This ability is a core feature in any dynamic application, allowing users to contribute and manage data effectively. By the end of this lesson, you'll have a user-friendly interface for adding new tasks to your ToDo list.

What You'll Learn

In this lesson, you will learn how to allow users to create new ToDo items in your Laravel application. You will achieve this by:

  1. Updating the view to include a form where users can enter new ToDo items.
  2. Modifying the TodoController to handle form submissions and validate user input.
  3. Enhancing the TodoService to save the new ToDo items in a persistent manner.

Let's start with the view, that is used to capture user input for creating new ToDo items.

HTML, XML
1<!-- app/resources/views/todos/index.blade.php --> 2 3<form action="/todos" method="post"> 4 @csrf 5 <input type="text" name="title" placeholder="Title" required> 6 <input type="text" name="description" placeholder="Description"> 7 <button type="submit">Add ToDo</button> 8</form>

This form will capture the user's input - a title and an optional description - and submit it to the ``/todos` endpoint. The @csrf directive generates a hidden input field with a CSRF token, which is required to protect your application from cross-site request forgery (CSRF) attacks.

You'll update your TodoController to look like this:

php
1// app/app/Http/Controllers/TodoController.php 2 3<?php 4namespace App\Http\Controllers; 5use App\Services\TodoService; 6 7class TodoController extends Controller 8{ 9 protected $todoService; 10 11 public function __construct(TodoService $todoService) 12 { 13 $this->todoService = $todoService; 14 } 15 16 public function index() 17 { 18 $todos = $this->todoService->findAll(); 19 return view('todos.index', ['title' => 'ToDo List', 'todos' => $todos]); 20 } 21 22 public function show($id) 23 { 24 $todo = $this->todoService->findOne($id); 25 return view('todos.show', ['title' => 'Todo Details', 'todo' => $todo]); 26 } 27 28 public function store(Request $request) 29 { 30 $request->validate([ 31 'title' => 'required|string|max:255', 32 'description' => 'nullable|string|max:255', 33 ]); 34 35 $this->todoService->create($request->title, $request->description); 36 return redirect('/todos'); 37 } 38}

We have a new method, called store, that handles the form submission. Let's see how it works:

  1. The method receives a Request object, which contains the user's input - the title and description of the new ToDo item.
  2. We use the validate method to ensure that the user's input is valid. The title is required and must be a string of up to 255 characters, while the description is optional and can be up to 255 characters long. The rules are defined using Laravel's validation syntax, which provides a convenient way to validate user input.
  3. If the input is valid, we use the TodoService to create a new ToDo item passing the provided title and description.
  4. Finally, we redirect the user back to the ToDo list page, where they can see the newly added item.

Let's now see how actual data persistence is handled in the TodoService:

php
1// app/app/Services/TodoService.php 2 3<?php 4namespace App\Services; 5use Illuminate\Support\Facades\File; 6 7class TodoService 8{ 9 protected $filePath; 10 11 public function __construct() 12 { 13 // Set the file path to store the todos (in storage folder) 14 $this->filePath = storage_path('app/todos.json'); 15 } 16 17 public function findAll() 18 { 19 // Check if the file exists using File facade 20 if (File::exists($this->filePath)) { 21 // Read and decode the file content as objects 22 $todos = json_decode(File::get($this->filePath), false); 23 } else { 24 // If the file doesn't exist, initialize with an empty array 25 $todos = []; 26 } 27 28 return $todos; 29 } 30 31 public function findOne($id) 32 { 33 $todos = $this->findAll(); 34 return collect($todos)->firstWhere('id', $id); 35 } 36 37 public function create($title, $description = null) 38 { 39 // Get the current todos 40 $todos = $this->findAll(); 41 42 // Create a new todo with incremented ID 43 $newTodo = (object)[ 44 'id' => count($todos) + 1, 45 'title' => $title, 46 'description' => $description 47 ]; 48 49 // Append the new todo to the list 50 $todos[] = $newTodo; 51 52 // Save the updated todos list back to the file using File facade 53 File::put($this->filePath, json_encode($todos, JSON_PRETTY_PRINT)); 54 55 return $newTodo; 56 } 57}

You might notice, that the logic for creating and stoting ToDo items has been changed. We were using hardcoded data in the previous courses, but right now we are storing the data in a JSON file. This is because arrays in PHP are not persistent and will be lost when the application is restarted. By storing the data in a file, we can ensure that the ToDo items persist across requests and server restarts.

To accomodate this change, we have updated the TodoService to read and write ToDo items from a JSON file:

  • The constructor sets the file path to store the ToDo items in the storage/app directory.
  • The findAll method reads the ToDo items from the file and returns them as an array of objects using the json_decode function.

Now that you understood how the data is stored, let's see how the create method works:

  • The method receives the title and an optional description of the new ToDo item.
  • The current ToDo items are loaded using the findAll method.
  • The new ToDo item is created as an object with an incremented ID, title, and description.
  • The new ToDo item is appended to the list of ToDo items.
  • The updated list of ToDo items is saved back to the file using the File::put method.
  • Finally, the new ToDo item is returned to the caller.

Let's now see what new route we need to add to the web.php file:

php
1// app/routes/web.php 2 3<?php 4use App\Http\Controllers\TodoController; 5 6Route::get('/todos', [TodoController::class, 'index']); 7Route::get('/todos/{id}', [TodoController::class, 'show']); 8Route::post('/todos', [TodoController::class, 'store']);

We have added a new route that listens for POST requests to the /todos endpoint. When a user submits the form to create a new ToDo item, the request will be handled by the store method in the TodoController. Pay attention, that for the POST requests we use the Route::post method instead of Route::get.

That's it! You have successfully added the functionality to create new ToDo items in your application. Users can now add tasks to their ToDo list using the form you provided. This feature enhances the interactivity and usability of your application, making it more dynamic and engaging for users.

Why It Matters

The ability to create new items is crucial in designing interactive and useful applications. It empowers users to add tasks as they see fit, making the application dynamic and adaptable to their needs. Whether you're building a task manager, a shopping list, or any other app requiring user input, this functionality provides the building blocks for user engagement and data management.

Creating new items also teaches you how to handle form data, validate input, and update persistent storage — skills that are universally applicable across web development projects. Excited to add this new feature to your app? Let's move to the practice section and implement it together!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.