NTM Solutions

Thứ Năm, 20 tháng 8, 2026

📘 Laravel 12 (2026) — BÀI 31 — ACCESSOR & MUTATOR TRONG ELOQUENT

Trong các bài CRUD trước, chúng ta đã làm việc trực tiếp với dữ liệu:

$user->name
$user->email
$user->role

Nhưng trong dự án thực tế, đôi khi chúng ta muốn biến đổi dữ liệu trước khi hiển thị hoặc xử lý dữ liệu trước khi lưu vào database.

Ví dụ:

  • Database lưu nguyen van a

  • Website muốn hiển thị Nguyen Van A

Hoặc:

  • Người dùng nhập NGUYEN VAN A

  • Database chỉ lưu nguyen van a

Đây chính là lúc AccessorMutator phát huy tác dụng.

Laravel 13 sử dụng cách khai báo hiện đại thông qua Illuminate\Database\Eloquent\Casts\Attribute.


📑 MỤC LỤC

  1. Accessor & Mutator là gì?

  2. Ví dụ thực tế

  3. Attribute trong Laravel 13

  4. Tạo Accessor cho User

  5. Accessor hoạt động như thế nào?

  6. Mutator là gì?

  7. Kết hợp Accessor và Mutator

  8. Ví dụ với Email

  9. Accessor cho dữ liệu tính toán

  10. Tại sao cần $attributes?

  11. Hiển thị Accessor trong Blade

  12. Accessor không phải là column database

  13. Đưa Accessor vào JSON

  14. Một ví dụ thực tế với User

  15. Accessor cho Role

  16. Accessor và Mutator khác $casts như thế nào?

  17. Khi nào dùng Accessor?

  18. Khi nào dùng Mutator?

  19. Accessor & Mutator trong CRUD

  20. Không nên nhồi mọi thứ vào Accessor

  21. Accessor & Mutator hiện đại

  22. Ví dụ hoàn chỉnh cho User Model

  23. Kết quả

  24. Tóm tắt

  25. Bài tập thực hành

  26. 🎯 Ghi nhớ Bài 31

1. Accessor & Mutator là gì?

Có thể hiểu đơn giản:

                    ELOQUENT MODEL
                          │
             ┌────────────┴────────────┐
             │                         │
        ACCESSOR                    MUTATOR
             │                         │
      Database → PHP            PHP → Database
             │                         │
        Khi đọc dữ liệu           Khi ghi dữ liệu
             │                         │
             ▼                         ▼
       Biến đổi để hiển thị      Biến đổi trước khi lưu

Accessor

Accessor dùng để thay đổi giá trị khi chúng ta lấy dữ liệu ra.

Database
   ↓
Accessor
   ↓
PHP / Blade

Mutator

Mutator dùng để thay đổi giá trị khi chúng ta gán dữ liệu vào Model.

PHP / Form
   ↓
Mutator
   ↓
Database

2. Ví dụ thực tế

Giả sử bảng users có:

id | name
1  | nguyen van a
2  | tran van b
3  | le van c

Khi hiển thị trên website, chúng ta muốn:

Nguyen Van A
Tran Van B
Le Van C

Thay vì phải viết:

ucwords($user->name)

ở rất nhiều nơi:

{{ ucwords($user->name) }}

Chúng ta có thể xử lý ngay trong Model bằng Accessor.

Khi đó Blade chỉ cần:

{{ $user->name }}

Eloquent sẽ tự động xử lý giá trị khi đọc thuộc tính.

3. Attribute trong Laravel 13

Trong Laravel hiện đại, chúng ta sử dụng:

use Illuminate\Database\Eloquent\Casts\Attribute;

Sau đó định nghĩa một method trong Model:

protected function name(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => ucwords($value),
    );
}

Đây là cách khai báo Accessor hiện đại được Laravel hỗ trợ.

4. Tạo Accessor cho User

Mở:

app/Models/User.php

Ví dụ:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    protected function name(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucwords($value),
        );
    }
}

Bây giờ database có:

nguyen van a

Nhưng:

$user->name

sẽ trả về:

Nguyen Van A

Trong Blade:

{{ $user->name }}

cũng sẽ hiển thị:

Nguyen Van A

5. Accessor hoạt động như thế nào?

Khi chúng ta viết:

$user->name

Eloquent nhìn thấy Accessor:

protected function name(): Attribute

và thực hiện phần:

get: fn (string $value) => ucwords($value)

Có thể hình dung:

Database
────────────────────
name = "nguyen van a"
          │
          ▼
      Accessor
          │
          ▼
      ucwords()
          │
          ▼
"Nguyen Van A"

Điều quan trọng:

Accessor không thay đổi dữ liệu trong database.

Database vẫn là:

nguyen van a

Chỉ khi lấy dữ liệu ra thì nó mới được biến đổi.

6. Mutator là gì?

Mutator hoạt động theo chiều ngược lại.

Ví dụ người dùng nhập:

   NGUYEN VAN A

Chúng ta muốn database lưu:

nguyen van a

Có thể sử dụng:

protected function name(): Attribute
{
    return Attribute::make(
        set: fn (string $value) => strtolower(trim($value)),
    );
}

Khi thực hiện:

$user->name = '   NGUYEN VAN A   ';

Mutator sẽ xử lý:

"   NGUYEN VAN A   "
          │
          ▼
        trim()
          │
          ▼
"NGUYEN VAN A"
          │
          ▼
      strtolower()
          │
          ▼
"nguyen van a"
          │
          ▼
      Database

Laravel gọi Mutator khi giá trị thuộc tính được gán.

7. Kết hợp Accessor và Mutator

Đây mới là cách sử dụng rất hay.

Chúng ta có thể vừa xử lý khi ghi vừa xử lý khi đọc.

protected function name(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => ucwords($value),

        set: fn (string $value) => strtolower(trim($value)),
    );
}

Khi lưu:

$user->name = '   NGUYEN VAN A   ';

Database:

nguyen van a

Khi đọc:

$user->name

Kết quả:

Nguyen Van A

Như vậy:

              User nhập dữ liệu
                     │
                     ▼
                 MUTATOR
                     │
                     ▼
              Database
              "nguyen van a"
                     │
                     ▼
                 ACCESSOR
                     │
                     ▼
              "Nguyen Van A"
                     │
                     ▼
                 Website

8. Ví dụ với Email

Một trường hợp rất thực tế là email.

Người dùng có thể nhập:

   ADMIN@GMAIL.COM

Chúng ta muốn database lưu:

admin@gmail.com

Có thể viết:

protected function email(): Attribute
{
    return Attribute::make(
        set: fn (string $value) => strtolower(trim($value)),
    );
}

Khi:

$user->email = ' ADMIN@GMAIL.COM ';

Database sẽ nhận:

admin@gmail.com

Điều này giúp dữ liệu trong hệ thống thống nhất hơn.

9. Accessor cho dữ liệu tính toán

Accessor không nhất thiết phải xử lý một column có sẵn.

Chúng ta có thể tạo ra một thuộc tính ảo.

Ví dụ User có:

first_name
last_name

Database:

first_name = Nguyễn
last_name  = Văn A

Chúng ta muốn:

$user->full_name

trả về:

Nguyễn Văn A

Có thể viết:

protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn ($value, array $attributes) =>
            $attributes['first_name'] . ' ' . $attributes['last_name'],
    );
}

Sau đó:

{{ $user->full_name }}

Laravel sẽ trả về:

Nguyễn Văn A

full_name không cần tồn tại trong database.

10. Tại sao cần $attributes?

Trong Accessor:

get: fn ($value, array $attributes) => ...

Laravel cung cấp:

$attributes

chứa các giá trị gốc của Model.

Ví dụ:

$attributes['first_name']
$attributes['last_name']

Do đó chúng ta có thể tạo dữ liệu mới từ nhiều column.

Ví dụ:

protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn ($value, array $attributes) =>
            trim(
                $attributes['first_name'] . ' ' .
                $attributes['last_name']
            ),
    );
}

11. Hiển thị Accessor trong Blade

Sau khi định nghĩa:

protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn ($value, array $attributes) =>
            $attributes['first_name'] . ' ' .
            $attributes['last_name'],
    );
}

Trong Blade:

<h2>{{ $user->full_name }}</h2>

Không cần:

<h2>
    {{ $user->first_name }}
    {{ $user->last_name }}
</h2>

Model sẽ chịu trách nhiệm xử lý dữ liệu.

12. Accessor không phải là column database

Ví dụ:

protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn ($value, array $attributes) =>
            $attributes['first_name'] . ' ' .
            $attributes['last_name'],
    );
}

Chúng ta có:

$user->full_name

nhưng bảng users không nhất thiết phải có:

full_name

Đây là computed attribute.

Database
──────────────────────
first_name
last_name

        │
        ▼

      Accessor

        │
        ▼

    full_name

Rất hữu ích khi xây dựng API hoặc giao diện.

13. Đưa Accessor vào JSON

Mặc định, một Accessor tính toán không nhất thiết xuất hiện trong kết quả toArray() hoặc JSON.

Ví dụ:

return $user->toArray();

Nếu muốn thêm thuộc tính:

full_name

vào dữ liệu JSON/array, Laravel 13 hỗ trợ khai báo #[Appends(...)].

Ví dụ:

use Illuminate\Database\Eloquent\Attributes\Appends;

#[Appends(['full_name'])]
class User extends Authenticatable
{
    protected function fullName(): Attribute
    {
        return Attribute::make(
            get: fn ($value, array $attributes) =>
                $attributes['first_name'] . ' ' .
                $attributes['last_name'],
        );
    }
}

Khi chuyển Model sang JSON, full_name sẽ được thêm vào.

14. Một ví dụ thực tế với User

Giả sử users có:

name
email
role
is_active

Chúng ta có thể tạo Accessor cho trạng thái:

protected function statusText(): Attribute
{
    return Attribute::make(
        get: fn ($value, array $attributes) =>
            $attributes['is_active']
                ? 'Đang hoạt động'
                : 'Đã khóa',
    );
}

Trong Blade:

{{ $user->status_text }}

Nếu:

is_active = 1

sẽ hiển thị:

Đang hoạt động

Nếu:

is_active = 0

sẽ hiển thị:

Đã khóa

15. Accessor cho Role

Chúng ta cũng có thể biến:

admin
user
editor

thành:

Quản trị viên
Người dùng
Biên tập viên

Ví dụ:

protected function roleText(): Attribute
{
    return Attribute::make(
        get: fn ($value, array $attributes) => match ($attributes['role']) {
            'admin' => 'Quản trị viên',
            'editor' => 'Biên tập viên',
            'user' => 'Người dùng',
            default => 'Không xác định',
        },
    );
}

Blade:

{{ $user->role_text }}

Kết quả:

admin  → Quản trị viên
editor → Biên tập viên
user   → Người dùng

16. Accessor và Mutator khác $casts như thế nào?

Đây là điểm rất quan trọng.

Laravel có một cơ chế khác là Attribute Casting.

Ví dụ:

protected function casts(): array
{
    return [
        'is_active' => 'boolean',
    ];
}

Khi đó:

$user->is_active

sẽ được chuyển thành kiểu:

true

hoặc:

false

Laravel cung cấp rất nhiều kiểu cast như:

boolean
integer
string
array
json
date
datetime
decimal
encrypted
hashed
enum

và nhiều kiểu khác.

17. Khi nào dùng Accessor?

Dùng Accessor khi cần:

Database
    ↓
Biến đổi
    ↓
Hiển thị / sử dụng

Ví dụ:

name
full_name
role_text
status_text
price_formatted
avatar_url

Ví dụ:

$user->status_text

hoặc:

$post->formatted_price

18. Khi nào dùng Mutator?

Dùng Mutator khi muốn chuẩn hóa dữ liệu trước khi lưu.

Ví dụ:

trim()
strtolower()
strtoupper()
chuẩn hóa dữ liệu

Ví dụ:

protected function email(): Attribute
{
    return Attribute::make(
        set: fn (string $value) => strtolower(trim($value)),
    );
}

Khi người dùng nhập:

 ADMIN@GMAIL.COM

Database lưu:

admin@gmail.com

19. Accessor & Mutator trong CRUD

Trong CRUD Users ở Bài 28, Controller có thể vẫn rất đơn giản:

$user->update($request->validated());

Không cần xử lý:

trim()
strtolower()
ucwords()

trong Controller nếu những quy tắc đó thuộc về Model.

Ví dụ:

$user->email = $request->email;

Mutator tự xử lý:

trim()
strtolower()

Sau đó:

$user->save();

Điều này giúp Controller sạch hơn.

20. Không nên nhồi mọi thứ vào Accessor

Accessor rất tiện nhưng không nên lạm dụng.

Ví dụ không nên đưa những logic quá nặng:

protected function something(): Attribute
{
    return Attribute::make(
        get: fn () => DB::table(...)->get(...),
    );
}

Nếu Accessor thực hiện query database phức tạp, khi hiển thị danh sách hàng trăm User có thể gây ra vấn đề hiệu năng.

Accessor nên ưu tiên:

Xử lý đơn giản
Tính toán đơn giản
Format dữ liệu
Tạo giá trị hiển thị

21. Accessor & Mutator hiện đại

Trong Laravel hiện đại, chúng ta ưu tiên:

protected function name(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => ucwords($value),
        set: fn (string $value) => strtolower(trim($value)),
    );
}

Thay vì kiểu cú pháp cũ:

public function getNameAttribute($value)
{
    return ucwords($value);
}

public function setNameAttribute($value)
{
    $this->attributes['name'] = strtolower(trim($value));
}

Cú pháp Attribute::make() rõ ràng hơn và phù hợp với Laravel hiện đại.

22. Ví dụ hoàn chỉnh cho User Model

Một Model có thể viết:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'role',
        'is_active',
    ];

    protected function name(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucwords($value),

            set: fn (string $value) =>
                strtolower(trim($value)),
        );
    }

    protected function email(): Attribute
    {
        return Attribute::make(
            set: fn (string $value) =>
                strtolower(trim($value)),
        );
    }

    protected function roleText(): Attribute
    {
        return Attribute::make(
            get: fn ($value, array $attributes) => match ($attributes['role']) {
                'admin' => 'Quản trị viên',
                'editor' => 'Biên tập viên',
                'user' => 'Người dùng',
                default => 'Không xác định',
            },
        );
    }

    protected function statusText(): Attribute
    {
        return Attribute::make(
            get: fn ($value, array $attributes) =>
                $attributes['is_active']
                    ? 'Đang hoạt động'
                    : 'Đã khóa',
        );
    }
}

Trong Blade:

{{ $user->name }}

{{ $user->email }}

{{ $user->role_text }}

{{ $user->status_text }}

23. Kết quả

Giả sử database:

name          = "nguyen van a"
email         = " ADMIN@GMAIL.COM "
role          = "admin"
is_active     = 1

Khi đọc:

$user->name

Kết quả:

Nguyen Van A

Khi đọc:

$user->email

Kết quả:

 ADMIN@GMAIL.COM 

Nếu chỉ khai báo Mutator cho email thì dữ liệu sẽ được chuẩn hóa khi lưu. Sau khi lưu, giá trị database sẽ là:

admin@gmail.com

Role:

$user->role_text

Kết quả:

Quản trị viên

Status:

$user->status_text

Kết quả:

Đang hoạt động

24. Tóm tắt

Có thể nhớ Accessor và Mutator bằng một câu:

ACCESSOR = Đọc dữ liệu → biến đổi → sử dụng
MUTATOR = Ghi dữ liệu → biến đổi → lưu
Thành phầnThời điểmMục đích
AccessorKhi đọcFormat / tính toán dữ liệu
MutatorKhi ghiChuẩn hóa dữ liệu
CastKhi đọc/ghiChuyển đổi kiểu dữ liệu

Ví dụ:

                 MODEL
                   │
       ┌───────────┴───────────┐
       │                       │
     ACCESSOR               MUTATOR
       │                       │
       ▼                       ▼
    Khi đọc                  Khi ghi
       │                       │
       ▼                       ▼
  Format dữ liệu         Chuẩn hóa dữ liệu
       │                       │
       ▼                       ▼
    Blade                  Database

25. Bài tập thực hành

Trong Model User, hãy tạo:

Bài tập 1

Mutator cho email:

ADMIN@GMAIL.COM

admin@gmail.com

Bài tập 2

Accessor cho name:

nguyen van a

Nguyen Van A

Bài tập 3

Accessor:

$user->role_text

Hiển thị:

admin  → Quản trị viên
user   → Người dùng
editor → Biên tập viên

Bài tập 4

Accessor:

$user->status_text

Hiển thị:

is_active = 1 → Đang hoạt động
is_active = 0 → Đã khóa

Bài tập 5

Tạo Accessor:

$user->display_name

Nếu name có dữ liệu thì hiển thị name, nếu không thì hiển thị:

Chưa cập nhật tên

🎯 GHI NHỚ BÀI 31

Accessor
    ↓
Xử lý khi lấy dữ liệu

Mutator
    ↓
Xử lý khi gán dữ liệu

Attribute::make()
    ↓
Cú pháp hiện đại trong Laravel

$casts
    ↓
Chuyển đổi kiểu dữ liệu

Model
    ↓
Có thể chịu trách nhiệm format
và chuẩn hóa dữ liệu

Sau Bài 31, chúng ta đã biết cách để đưa logic xử lý thuộc tính vào Eloquent Model thay vì nhồi tất cả vào Controller hoặc Blade.

Đây là một bước quan trọng để source Laravel bắt đầu sạch và có cấu trúc hơn.

Bài tiếp theo — Bài 32: Local Scope & Global Scope.

Không có nhận xét nào:

Đăng nhận xét

Facebook Youtube RSS