DEV Community

Cover image for Laravel101: Implement RESTful Architecture in Controllers
Kazem
Kazem

Posted on • Updated on

Laravel101: Implement RESTful Architecture in Controllers

In our project, we have made progress in developing our task model and evaluating the submitted information. The purpose of this article and the upcoming ones is to introduce Restful architecture into our project. By the end of this tutorial, you will learn a programming pattern that not only improves code readability and cleanliness but also makes programming more interesting. believe me!


Restful architecture, short for Representational State Transfer, enhances code readability, as its name suggests. In Laravel, there is a predefined template that we will use to modify the existing code. First, let’s examine the table below, taken from the Laravel documentation:

Image description

The table categorizes the main characteristics of HTTP requests sent to the server into four types: GET, POST, PUT, and DELETE. GET is used to retrieve information, POST is used to save records, PUT is used to update records, and DELETE is used to delete records. In Laravel, the names of paths and functions are defined accordingly. Therefore, by simply looking at a defined path, you can easily understand the corresponding function in the controller and its purpose.

Now, let’s proceed and implement these principles step by step in our existing code:

Image description

I just simply define the function I don’t do anything inside newly added ones.

Then let’s back to routes and modify all tasks routes:

Route::get('/tasks', [TaskController::class, 'index'])->name('tasks.index');
Route::get('/tasks/create', [TaskController::class, 'create'])->name('tasks.create');
Route::post('/tasks', [TaskController::class, 'store'])->name('tasks.store');
Route::get('/tasks/{task}', [TaskController::class, 'show'])->name('tasks.show');
Route::get('/tasks/{task}/edit', [TaskController::class, 'edit'])->name('tasks.edit');
Route::patch('/tasks/{task}', [TaskController::class, 'update'])->name('tasks.update');
Route::delete('/tasks/{task}', [TaskController::class, 'destroy'])->name('tasks.destroy');
Enter fullscreen mode Exit fullscreen mode

I decide to move tasks page into new page an let home page a simple landing page for our project!

Now, let’s define each page and all the necessary components that we need to implement, page by page. Here I just add other blank blade pages for the rest of them! and the new routes which we will go through all of them in this article:

Image description

Let’s start with index page where we want list all tasks. The first thing is we have to change it view name inside TaskController from home to tasks.index and then transfer all content inside home into new directory tasks/index.blade.php :

Image description

That’s it!

Creating the function is also a simple task. We just need to define a view function and point it to tasks/create.blade.php. I did this and tried to open the page, but it threw an exception indicating that we need to change the route name used in the page.

Image description

Well, this is where RESTful design comes to our rescue. When you adhere to this design principle, you will know how to name the routes for each page. This way, you can avoid the inconvenience of encountering errors like the one we just experienced.

Now let’s try to store new task and see it works or not:

Image description

What? If you look at form you will see that the route is correct but the request is not found for post request. Please pay attention to the action part of the form.

Currently, we are on a different page, specifically http://localhost:8000/tasks/create. Therefore, when defining the action in the form, we need to start the path from the root, rather than starting from tasks, simply means we need to use “/” at the beginning or you can use route helper function:

Image description

The error has been fixed, it’s as simple as that… But here’s the thing: after creating a new task, we want to be redirected back to the tasks page. To achieve this, we used back which isn’t my option I like to use the redirect function instead! with this function we can define what exactly the redirect page have be!

You may have noticed that I made a change to the error section to improve its functionality and ensure that all errors received from the backend are displayed. The simple modification I made was to utilize the all() function, which get the errors object data as an array. This allows us to easily access and display all the error messages.

Now, let’s move on to the task show page. Since we’ve already implemented the route, we only need to follow two steps here. First, we navigate to the controller and define the show function to handle the request. Finally, we manage the display of information using Blade.

Let’s look to show route:

Route::get('/tasks/{task}', [TaskController::class, 'show'])->name('tasks.show');

Enter fullscreen mode Exit fullscreen mode

The key difference here is the dynamic path. Let’s take a closer look at the variable we’re sending to the server:

Image description

By sending any value to the server, we can retrieve it within the show function and display the appropriate output. Let’s complete the function and give it a try.

Image description

I just made a simple show page!

Everything seems to be working correctly, but what if we enter an incorrect ID? In such case, the user would encounter a server exception. It’s important to handle this scenario by displaying a 404 error page, indicating that the requested item doesn’t exist. Thankfully, Laravel provides solutions for this using Eloquent:

// findOrFail
$task = Task::findOrFail($id);

// or firstOrFail
$task = Task::where('id', $id)->firstOrFail();
Enter fullscreen mode Exit fullscreen mode

These two approaches work fine, but let’s use Laravel’s built-in binding, as it’s simpler.

By specifying the variable type we receive as input in the show function, Laravel automatically recognizes it and performs a process called binding to find the corresponding record for us. Moreover, if it doesn’t find a record through the model, it will return the same 404 error:

public function show(Task $task)
{
    return view('tasks.show', compact('task'));
}
Enter fullscreen mode Exit fullscreen mode

It’s as simple as that!

Great! Now, we have only two things left: editing and deleting a task.

To edit a task, first we need to load the data of the record we want to edit and populate a form, similar to the task creation form, with the initial data, so our edit function is look like this:

public function edit(Task $task)
{
    return view('tasks.edit', compact('task'));
}
Enter fullscreen mode Exit fullscreen mode

Now, let’s just copy creation form into edit page. The first change we need to make is to use the passed data in our input fields. However, we want to ensure that if the update request fails and the form is returned with errors, the previously entered values are filled in the input fields. To handle this, we can utilize the old() method. Here's an example for the input field representing the task title: ""

<input ... value="{{ old("title") ?? $task->title }}" />
Enter fullscreen mode Exit fullscreen mode

This code checks if there is any previously entered value (old("title")) and if not, it falls back to the original task title value ($task->title).

Additionally, we need to update the route for the edit form, you have to update it also:

<form method="POST" action={{ '/tasks/' . $task->id }} > ...
Enter fullscreen mode Exit fullscreen mode

Or just use route helper function which provides a cleaner and more readable approach. In this case, you can specify the route name tasks.update as the first parameter and pass the task ID as the second parameter:

<form method="POST" action={{ route('tasks.update', $task->id) }} > ...
Enter fullscreen mode Exit fullscreen mode

The final step is to change the request type to PUT/PATCH for the edit form. However, within HTML forms, we can only use the POST or GET methods. To overcome this limitation, Blade provides a helpful function called @method :

<form method="POST" action={{ route('tasks.update', $task->id) }}>
    @method('PUT')
    <!-- Rest of the form -->
</form>
Enter fullscreen mode Exit fullscreen mode

By including @method('PUT') within the form, Blade will generate a hidden input field that overrides the form method and sets it to PUT. This way, when the form is submitted, it will be treated as a PUT request.

Image description

Finally the delete action, as before we need to update destroy function as bellow and use another eloquent helper function called delete:

public function destroy(Task $task)
{
    $task->delete();
    return redirect("/tasks");
}
Enter fullscreen mode Exit fullscreen mode

But how and where we should call this action!

To overcome this one we could add another simple form inside task.index and like edit page define delete method inside the form:

Image description


Great job! You’ve successfully implemented all the necessary features to work with models in Laravel while following the RESTful structure. As you can see, the code has become much shorter and easier to read. Pretty impressive, huh?

Now, let’s look at routes/web.php again. Imagine having a massive project with dozens of models. Writing duplicate path functions for each model can be long list. But don’t worry! Laravel has a solution for this.

Route::resource('tasks', TaskController::class);
Enter fullscreen mode Exit fullscreen mode

By using this method, we actually define paths that are equivalent to all the paths we defined earlier in one go. It saves you from the hassle of writing repetitive code for every single model.

It’s also important to note that resource automatically generates all the RESTful functions by default. in most cases like you won’t require all of these functions.

To address this, Laravel provides a helpful method called only that allows you to selectively include only the desired functions.

Route::resource('tasks', TaskController::class)->only(['create', 'store']);
Enter fullscreen mode Exit fullscreen mode

There is another function that works exactly the opposite of this function called except which can exclude any unnecessary functions.

Route::resource('tasks', TaskController::class)->except('delete');
Enter fullscreen mode Exit fullscreen mode

It’s super useful!

There’s another cool thing I’d like to mention. Laravel artisan command offers some nifty options that can make your life even easier when generating controllers. For example, with a simple command, you can create all the necessary functions we discussed earlier in the controller, right from the start:

php artisan make:controller TestController -r -m Task
Enter fullscreen mode Exit fullscreen mode

This -r option here say to artisan to create my controller by implementing restful scheme and That -m is just to assign its model which here is task!


I hope you found this tutorial enjoyable and informative. You’ve done an excellent job so far! If you have any more questions or need further assistance, feel free to ask. Happy coding!

Top comments (0)