9 min read

8 Bad Habits I See Laravel Developers Make

This blog post covers 8 common bad habits that Laravel developers often make and how to avoid them. The post covers best practices for writing cleaner, more maintainable, and more performant code, including separating validation logic from controllers, following the Single Responsibility Principle, using middlewares, testing code, using database migrations, using route model binding, following naming conventions, and using Eloquent properly. Whether you're new to Laravel or a seasoned developer, this post will help you improve your skills and write better code.

Are you a Laravel developer looking to improve your code? In this blog post, we’ll explore 8 common bad habits that Laravel developers often make and how to avoid them. From separating validation logic from controllers to following the Single Responsibility Principle, we’ll cover best practices for writing cleaner, more maintainable, and more performant code. We’ll also discuss the importance of testing, using database migrations, and naming conventions. Whether you’re new to Laravel or a seasoned developer, this post will help you improve your skills and write better code. Link to video version.

1. Not Separating Validation from Controllers

One common mistake that Laravel developers make is not separating validation logic from their controllers. While it may be convenient to include validation within a controller, this can make the code harder to read, understand, and maintain.

Fortunately, Laravel provides a way to separate validation logic using the Request class. The Request class allows you to define the validation rules for a particular request and then pass those rules to the controller. This makes it easier to update and modify the validation rules without affecting the rest of the code in the controller.

To use the Request class for validation, you first need to create a new request class through Artisan. In the CLI, enter the following command:

php artisan make:request StoreTaskRequest

This will generate a new request class in the Requests directory. Open the newly generated StoreTaskRequest and define the validation rules for that request using the rules() method. This method should return an array of validation rules.

public function rules(): array
{
  return [
      'name' => 'required|max:255',
      'description' => 'required',
      'priority' => 'required'
  ];
}

Once you have defined the validation rules, you need to change the argument in the controller’s method from Request to StoreTaskRequest. Laravel will automatically validate the request before executing the method. If the request is invalid, Laravel will return a response with the validation errors.

public function store(StoreTaskRequest $request)
{
    $task = Task::create([
        'user_id' => Auth::user()->id,
        'name' => $request->name,
        'description' => $request->description,
        'priority' => $request->priority
    ]);

    return new TasksResource($task);
}

By separating the validation logic from the controller, you can improve the maintainability, readability, and testability of your code. Remember to always test your code thoroughly and keep up with the latest developments in the Laravel ecosystem.

2. Not Following the Single Responsibility Principle (SRP)

Another bad habit is not following the Single Responsibility Principle (SRP). This principle states that a class should only have one reason to change. When you violate this principle, your code becomes harder to read, harder to maintain, and harder to debug. Make sure that each class or method has a clear and specific purpose.

When a class has multiple responsibilities, it becomes harder to maintain and modify, and it can also lead to bugs and errors. By following SRP, you can create more modular and flexible code that is easier to understand and maintain. The principle advocates for the separation of concerns, which means that each class or module should have only one responsibility or reason to change.

This can lead to better organization of your code, as each part of it is focused on a specific task. It can also make it easier to test your code, as each responsibility can be tested separately. By breaking down your code into smaller, more focused components, you can identify areas where you can make use of design patterns or common algorithms to improve your code’s efficiency and maintainability.

Let’s take a look at an example where the Single Responsibility Principle is not being implemented correctly. Often, Laravel developers make the mistake of including too much functionality within a single controller. For instance, an AuthController might handle both user authentication and user profile management, leading to a lack of separation of concerns.

To fix this issue, separate controllers should be created for authentication and user profile management. For example, the authentication controller would handle login and registration, while the user profile controller would handle updating and displaying user profile information.

To implement this separation of concerns, you can follow these steps:

Create a new controller for authentication using the command

php artisan make:controller Auth/AuthenticateUserController

Create a new controller for user registration using the command

php artisan make:controller Auth/VerifyEmailController

Create a new controller for user profile management using the command

php artisan make:controller ProfileController

Then, you should refactor your AuthController by using the right Controllers for the methods inside of it.

By separating the authentication and user profile management functionality into different controllers, you can make your code more modular and reusable. Additionally, it will be easier to test the code, as each controller will have a clear and well-defined purpose. Remember to always follow the Single Responsibility Principle to write cleaner, more maintainable, and more performant code.

3. Not Using Middlewares (or not correctly)

Middlewares in Laravel are a great way to handle cross-cutting concerns such as authentication, logging, and caching. However, many developers either don’t use them or don’t use them correctly. When used correctly, middlewares can help you keep your code clean and organized while also improving performance.

First, let’s discuss the bad habit of chaining multiple middleware calls in routes. While middleware can be a powerful tool for filtering incoming requests and processing data, it’s important to use it correctly and only when necessary. Chaining multiple middleware calls can result in code that is difficult to read and maintain.

To avoid this issue, you can group related routes together and apply middleware to the group as a whole using the Route::group() method. This can make the code easier to read and understand, as well as easier to modify and maintain. For example:

Route::group(['middleware' => 'auth'], function() {
    Route::resource('songs', SongController::class)->except('show');
    Route::resource('categories', CategoryController::class);
    Route::resource('articles', ArticleController::class)
        ->except('index', 'show');
    Route::get('categories', [CategoryController::class, 'admin_index'])
        ->name('category.admin');
});

In this example, all four routes require the auth middleware. By grouping them together and applying the middleware to the group, you can simplify the code and make it easier to read and understand.

Additionally, you can also add a prefix to the group by passing in a second key value pair:

Route::group(['middleware' => 'auth', 'prefix' => 'admin'], function() { }

This will apply the prefix of “admin” to all routes.

Remember, while middleware can be a useful tool for filtering incoming requests and processing data, it’s important to use it only when necessary. By grouping routes together and applying middleware to the group as a whole, you can make your code more modular, easier to read and understand, and easier to modify and maintain in the long term.

4. Not Testing Code

Testing is an essential part of software development, but many Laravel developers skip this step. Not testing your code can lead to bugs and other issues down the line. Laravel provides a built-in testing framework that makes it easy to write tests for your application. Make sure to test your code thoroughly before deploying it to production.

5. Not Using Database Migrations

Database migrations are an important part of Laravel development, but many developers don’t use them. Migrations allow you to version your database schema and make it easy to deploy changes to your production environment. Make sure to use migrations to manage your database schema and avoid manual updates.

6. Not Using Route Model Binding

Route model binding is a feature in Laravel that allows you to automatically inject a model instance into a route’s action, or a Controller. This can make your code more concise and easier to read, as it eliminates the need to manually retrieve the model from the database and pass it to the action.

To use route model binding, you need to define a route parameter that corresponds to a column in your database table. Laravel will then automatically retrieve the model instance that matches the value of the route parameter, and pass it to your route’s action.

Let’s look at an example, starting with the routes since we have it already open. In here, we’re going to show a specific task to a user, so the endpoint will be /tasks/ route parameter of id.

Route::get('/tasks/{task}', function (Task $task) {
    return view('tasks.show', ['task' => $task]);
});

By doing so, we could replace the Eloquent call to our database, since it has been done behind the scenes, and we could directly return the task collection.

This can simplify your code and make it more readable, as you don’t need to manually retrieve the model instance from the database in your route’s action. It also makes your code more robust, as Laravel will automatically handle cases where the model instance can’t be found or doesn’t exist.

Route model binding is not appropriate in all situations, however. For example, when you need to perform complex queries or joins that involve multiple tables, route model binding may not be the best solution.

7. Naming Conventions

Naming conventions are an important part of Laravel development, but many developers don’t follow them. Laravel has specific naming conventions for controllers, models, and other classes. Following these conventions can help you keep your code organized and easy to navigate.

8. Not Using Eloquent Properly

Eloquent is Laravel’s ORM (Object-Relational Mapping) system, and it’s a powerful tool for working with databases. However, many developers don’t use Eloquent properly, leading to slow queries and other issues. Make sure to use Eloquent’s query builder properly and avoid common mistakes like the N+1 problem.

Conclusion

In conclusion, these are some of the bad habits that I often see Laravel developers make. By avoiding these habits and following best practices, you can write cleaner, more maintainable, and more performant code. Remember to always test your code thoroughly and keep up with the latest developments in the Laravel ecosystem.

I hope this article has shown you 8 common mistakes I see Laravel developers make. If you enjoyed reading this post and would like me to write an article on a specific topic, please send an email to info@codewithdary.com.