Practical Guide to Cleaner Laravel Controllers: Harnessing the Power of Accessors and Mutators
Tags: laravel, api, php, programming
Published: 2024-01-21 15:36:51

Laravel uses a Model-View-Controller architectural pattern and controllers play an important role. The main task of a controller in Laravel is to manage user http requests, render views and communicate with the application models for data modifications. When building any Laravel application, it is essential to adhere to best practices by implementing the MVC design pattern. Following this pattern ensures the maintainability and reusability of your code. For instance, the controller can be employed for writing business logic; however, this is generally not considered a best practice. Following best practices involves keeping controllers focused on handling http request and application flow, while business logic is better placed in the model or service for a cleaner and more organized codebase. Now, let's explore an actual case that demonstrates the functioning of controllers.

## Practical Illustration of a Controller in Action

In the accounting industry, a Corporate Controller holds a pivotal managerial role, overseeing financial functions within an accounting firm. This position entails the management of departments like Financial Reporting, Budgeting and Forecasting, Compliance, and Treasury Management. The Corporate Controller is tasked with ensuring precise financial reporting, upholding internal controls, adhering to regulations, making strategic financial decisions, and providing leadership and supervision to teams within the finance division.

The role of a Corporate Controller in the accounting industry can be compared with that of a Laravel Controller. The controller in Laravel is an important part that controls the flow and request handling inside the application. It acts as a hub through which different functions are managed analogous to how the Corporate Controller manages financial aspects.

For example, while a Corporate Controller oversees departments such as Financial Reporting, Budgeting and Forecasting, and Compliance, a Laravel Controller handles different aspects of an application, including data retrieval and processing views rendering. The Laravel Controller, like its corporate counterpart, acts as a key guardian, ensuring data accuracy. Given the facts we've emphasized about maintaining a tidy controller, how can we ensure the cleanliness of our controllers? This can be achieved through the use of accessors and mutators.

## Accessors

Accessors are also used to create fake attributes that do not belong to the database column. These fake attributes can be accessed as if they are stored columns in the database. Data obtained from the database can be manipulated using accessors. In object-oriented programming (OOP) in any language, accessors bear similarity to getters. Let us take a look at some possible situations where assessors can be applied to our applications.

## Scenario 1:
Picture yourself working on a user registration system, where the goal is to showcase each user's full name in a specific format without persistently storing it in the database. To accomplish this, you can employ an accessor to create a fullname attribute by combining the first and last names, capitalizing the initial letter of each name. This fullname attribute exists only in memory and is not in the database.

## Scenario 2:
You are building an API for frontend developers, and you want to display user information without exposing the exact user ID for security reasons. You can use accessors to hash the user ID before sending it to the front-end. 

## Naming conventions for accessors: 

Accessors follow a naming convention that requires the creation of methods in the model to format attribute values upon retrieval from the database. This convention dictates using the **get{AttributeName}Attribute** method on the model, where {**AttributeName**} represents the name of the target attribute. For instance, if dealing with an attribute named **first_name**, the corresponding accessor method would be **getFirstNameAttribute**. Please note that attributes of a model are the fields or columns in the associated database table.


![accessors Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgd3jlbz1g8wrcgr3hdl.png)

## Mutators
Like accessors, mutators are also used for data manipulation. The main difference with Mutators is that the manipulated data is stored in a database. They can be compared to setters in object-oriented programming (OOP). We are going to consider some possible scenarios where mutators can be implemented.

## Scenario 1:
Picture yourself developing a registration functionality for a web application, and your goal is to guarantee the security of user credentials by encrypting the password. You can use a mutator to hash the password before saving it to the database.

## Scenario 2:

Imagine you are building a system where users are allowed to save a phone number containing a plus symbol (+) and you want to remove the plus symbol before storing the number in the database, you can use a mutator to implement this feature.

## Naming conventions for mutators: 

Mutators adhere to a naming convention that entails generating methods in the model to modify the attributes before they are stored in the database. The convention specifies using the **set{AttributeName}Attribute** method on the model, where **{AttributeName}** corresponds to the name of the targeted attribute. For instance, with an attribute named **first_name**, the mutator method would be **setFirstNameAttribute**.


![mutators Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rbczbe3i54z1crw2swrz.png)

## Reasons for using accessors and mutators.

- **Maintenance**: They make it easier to modify the behavior of certain attributes in one centralized location, reducing the risk of introducing errors while making updates.

- **Readability**: Separating logic related to attribute manipulation from the main application logic improves the readability of the code.

- **Reusability**: By encapsulating logic within them, you can reuse these methods across different parts of your application, promoting code reuse and avoiding duplicating similar logic.

- **Testing**: They make it easier to unit test specific behavior associated with attribute retrieval or setting.

## Prerequisites

- Basic knowledge of getters and setters in object-oriented programming (OOP)

- Laravel and PHP Basics

- Mysql Database

- Composer and PHP installed

## Implementing Accessors and Mutators

Now that you have a solid understanding of accessors and mutators in Laravel, let's dive into some practical scenarios where these concepts can be implemented. We will explore step-by-step guides for implementing the few scenarios mentioned earlier:

## Step 1: Creating and setting up a new **[laravel application](https://laravel.com/docs/10.x/installation#creating-a-laravel-project)**
In your terminal, execute the following command to generate a new Laravel Project named **example-app**

```
composer create-project laravel/laravel example-app
```

Next, open the example-app project and locate the **.env** file to set up your database preferences. Input the desired database name in the **DB_DATABASE** field and, if applicable, enter a password in the **DB_PASSWORD** field.

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=*****
DB_USERNAME=root
DB_PASSWORD=****
```

Go to the database directory, then navigate to the migrations folder, find the migration file suffix named **_create_users_table.php**, and open it.


![users_table Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tb288c6tb8sf3gh0864.gif)

Next, replace the **up** method with the code below

```
public function up(): void
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('first_name');
        $table->string('last_name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}
```
Then run this command on your terminal

```
php artisan migrate
```

You should have these empty rows in your users table after running the migration command


![users table migration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xddwy2ncqtip8f8wljo.png)

## Step 2: Implementing Mutators

Go to  **app/models/** directory and locate the **User.php** model file. This is the location where we will implement all our business logic.


![Implementing Mutators Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1148rpmif1fam7vkyuw2.gif)

Replace the existing code in this file with the provided code below.

```
<?php

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
   protected $fillable = [
       'first_name',
       'last_name',
       'email',
       'password',
   ];
   protected $hidden = [
       'password',
       'remember_token',
   ];

   public function setPasswordAttribute($value)
   {
       $this->attributes['password'] = bcrypt($value);
   }
}

```

The code illustrates the creation of a fillable property, hidden property, and a mutator method.

- The **$fillable** property permits mass assignment solely for specified fields.

- The **$hidden** property safeguards sensitive data by omitting it from public representations when the model is converted to an array or JSON.

- The **setPasswordAttribute** method  hashes user passwords before they are stored in the database.

Our primary focus is on the **setPasswordAttribute** method. We use Laravel's **bcrypt** function to encrypt the provided password value, setting the password attribute to the hashed password value before storing it in the database.

Create a user controller by executing this command on your terminal:

```
php artisan make:controller UserController
```

Navigate to the **app/Http/Controllers/** directory and open the UserController file.

![Controllers directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y56c2zd2fp5ms7sl5llu.gif)

Replace the existing code in the UserController file with the provided code below.

```
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;

class UserController extends Controller
{
  public function showCreateUserForm()
  {
      return view('create-user');
  }

  public function createUser(Request $request)
  {
      $user = User::create([
          'first_name' => $request->input('first_name'),
          'last_name' => $request->input('last_name'),
          'email' => $request->input('email'),
          'password' => $request->input('password'),
      ]);

      return redirect('/create-user')->with('success', 'User created successfully');
  }
}
```

Go to **resources/views/** directory and create a new file named **create-user.blade.php**


![create-user Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jkx3t7mv0dnzgl9sieqa.gif)


Copy and Paste the code provided below in create-user.blade.php

```
@if(session('success'))
  <div style="color:green;text-align:center;">
      {{ session('success') }}
  </div>
@endif

<form method="POST" action="{{ url('/create-user') }}">
  @csrf

  <label for="first name">First Name:</label>
  <input type="text" name="first_name" required>
  <br>

  <label for="last name">Last Name:</label>
  <input type="text" name="last_name" required>
  <br>

  <label for="email">Email:</label>
  <input type="email" name="email" required>
  <br>

  <label for="password">Password:</label>
  <input type="password" name="password" required>
  <br>

  <button type="submit">Create User</button>
</form>
```

Open the **web.php** in the routes directory and replace the existing code with the code provided below.


```
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

Route::get('/', function () {
  return view('welcome');
});

Route::get('/create-user', [UserController::class, 'showCreateUserForm']);
Route::post('/create-user', [UserController::class, 'createUser']);
```

Execute the command below to serve the application

```
php artisan serve
```

Next, copy the base URL from the terminal and paste it into your browser. Then append the **/create-user** route to the base URL. Your view should resemble this.


![localhost](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8w8kl6jetqoadqu9iso4.png)

Create a new user by entering the necessary information into the input fields. Keep in mind that no validation has been implemented in this application, so ensure accurate information is provided.

Open your database management tool and check the user table.


![DB Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xulrsbtb3pzysr299vho.png)

You can observe that the user password in the password column has been hashed. It's worth noting that we chose not to implement password encryption in the controller, which is a common practice among developers. This method of using a mutator to hash the password helps maintain a clean and straightforward controller.

## Step 3: Implementing Accessors

We won't create a new Laravel application to implement accessors; instead, we'll utilize the existing **example-app** used for implementing mutators. Let's proceed with implementing the accessors method. In this instance, we will create a username using the first name, last name, and id attribute.

Copy the code provided below and paste it into the user model, positioning the:

- **getUsernameAttribute**: below the **setPasswordAttribute** method.
- **$appends:** below or top of the protected properties.

The **$appends** is used to add extra information to the JSON output of your model without having to modify the database schema. It's useful for including manipulated or formatted data that you want to send to your frontend or other parts of your application.

```
protected $appends = ['username'];

public function getUsernameAttribute()
{
   $firstName = $this->attributes['first_name'];
   $lastName = $this->attributes['last_name'];
   $id = $this->attributes['id'];
   $username = $firstName . '-' . $lastName . '-' . $id;
     
   return strtolower($username);
}
```

Next, navigate to the **UserController**, then copy the code provided below and paste the code below the **CreateUser** method. This method will retrieve all the users stored in the database.

```
public function getUserName()
{
   $users = User::all();
   return view('create-user', ['users' => $users]);
}
```

Add the router method below in the **web.php** file located in the routes directory.


```
Route::get('/create-user', [UserController::class, getUserName]);
```

Finally, copy the provided code below and paste it in the **create-user.blade.php** file located in the **resources/views/** directory. Make sure to insert it below the closing HTML form tag.

```
<h1>All Users</h1>

<ul>
  @foreach ($users as $user)
      <li> <b>First Name:</b> {{ $user->first_name }} <br>
      <b>Last Name:</b> {{ $user->last_name }} <br>
      <b>User Name:</b> {{ $user->username }}</li>
  @endforeach
</ul>
```
Go to your browser and refresh the page; you should observe that a new username that is not stored in the database is being displayed.

![last Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f05mv2g3izovjyr8qcki.png)

## Conclusion
This practical guide has explored the art of creating cleaner Laravel controllers by leveraging the capabilities of accessors and mutators. By following best practices and incorporating these potent tools, you can substantially improve the quality of your  codebase.

## References

- What You Need to Know About Controllers: https://www.roberthalf.com/us/en/insights/career-development/all-you-need-to-know-about-controller-salary-levels-jobs

- Laravel Documentation: https://laravel.com/docs/10.x/eloquent-mutators