Accessors và Mutators trong Laravel 5.3

Giới thiệu

Có rất nhiều lý do để người dùng lựa chọn sử dụng Eloquent và một trong những lý do đó là Eloquent hỗ trợ Accessors và Mutators. Nếu các bạn chưa nghe đến những cái tên này hay đã nghe qua mà chưa sử dụng thì cũng đừng lo lắng vì nó khá dễ hiểu, nhất là khi đọc qua bài viết của mình 😄 Accessors và Mutators là gì? Nó có nhiệm vụ là cho phép bạn format các attributes của Eloquent khi lấy ra từ một model hoặc là bạn cũng có thể set giá trị cho chúng. Tại sao phải làm vậy? Vì trong project của bạn, 1 thuộc tính có thể sẽ được sử dụng ở rất nhiều nơi, chẳng lẽ mỗi lần sử dụng bạn lại phải format nó. Giờ đây, việc cần làm là truy cập vào Model rồi viết 1 hàm set giá trị cho chúng, sau đó bạn chỉ cần gọi đến tên thuộc tính đó ở mọi nơi. Ví dụ thế này, bạn cần mã hóa mật khẩu cho người dùng khi truy cập vào trang web, thì việc bạn làm đơn giản là băm thuộc tính password rồi sau đó truy xuất nó từ bên trong Eloquent User model. Ngoài việc hỗ trợ tạo accessor và mutator riêng, Eloquent cũng tự động chuyển các trường date thành Carbon instance hoặc thậm chí chuyển trường text thành JSON.

Accessors & Mutators

Accessors

Mình xin phép vừa nêu định nghĩa vừa đưa ra ví dụ cụ thể luôn. Giả sử trong User model của mình có 2 thuộc tính là first_namelast_name, trong khi đó mình lại muốn in ra giá trị full_name bao gồm 2 giá trị first_namelast_name. Vậy nên ta cần sử dụng accessors như sau:

B1. Vì full_name không phải là thuộc tính của User model nên mình sẽ khai báo nó sử dụng appends (còn nếu thuộc tính ấy đã được khai báo trong Model rồi thì bạn hãy bỏ qua bước này):

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['full_name'];
}

Sau khi thêm vào appends thì thuộc tính full_name cũng sẽ được gọi như các thuộc tính khác trong User model.

B2. Tạo một hàm getFullNameAttribute trong model (là tên của thuộc tính viết theo kiểu camel), hàm này thực hiện việc lấy giá trị cho thuộc tính full_name

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get the user's full name.
     *
     * @return string
     */
    public function getFullNameAttribute()
    {
        return $this->attribute['fist_name ']. ' ' . $this->attribute['last_name'];
    }
}

Như ta đã thấy thì Accessors sẽ tự động được gọi bởi Eloquent khi lấy giá trị của full_name. B3. Lấy giá trị full_name:

$user = App\User::find(1);

$fullName = $user->full_name;

Vậy là xong! Rất đơn giản phải không ạ (hehe)

Mutators

Mutators thì hơi ngược với Accessors 1 xíu. Nếu Accessors lấy giá trị từ mảng attributes và thay đổi giá trị đó để lấy ra thì Mutators lại nhận giá trị mà bạn gán vào thuộc tính rồi sau đó mới lưu vào mảng attributes. Các bạn cùng mình thực hiện ví dụ sau nhé: Để khai báo một Mutator, khai báo một hàm setPasswordAttribute với Password là tên của thuộc tính theo kiểu "camel". Lần này, chúng ta cùng khai báo một Mutator cho thuộc tính password. Mutator này sẽ tự động được gọi khi chúng ta lấy giá trị password:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Set the user's password.
     *
     * @param  string  $value
     * @return string
     */
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = Hash::make($value);
    }
}

Và giờ chúng ta có thể thoải mái gán giá trị cho password và giá trị của chúng lưu trong DB sẽ được băm để không ai có thể đọc được

$user = App\User::find(1);

$user->password = '123456';

Ở ví dụ trên, hàm setPasswordAttribute sẽ được gọi với $value truyền vào là 123456.

Date Mutators

Mặc định, Eloquent sẽ convert created_atupdated_at thành instances của Carbon, một thư viện cung cấp rất nhiều hàm hữu ích và mở rộng class DateTime của PHP.

Bạn có thể tuỳ chỉnh trường nào sẽ được tự động mutate, và thậm chí có thể disable việc mutation này bằng cách ghi đè lên thuộc tính $dates của model như sau:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];
}

Khi một column là kiểu date, bạn có thể đặt giá trị của nó là một UNIX timestamp, date string (Y-m-d), date-time string và tất nhiên là một instance của DateTime / Carbon, và giá trị của date sẽ tự động được lưu vào trong database:

$user = App\User::find(1);

$user->created_at = Carbon::now();

$user->save();

Và khi lấy các attributes liệt kê trong thuộc tính $date, chúng sẽ tự động được cast thành instance của Carbon, cho phép bạn sử dụng các hàm Carbon trong attributes:

$user = App\User::find(1);

return $user->created_at->getTimestamp();

Mặc định, timestamps được format dưới dạng Y-m-d H:i:s. Nếu bạn muốn tuỳ chỉnh format của timestamp, thiết lập vào thuộc tính $dateFormat. Thuộc tính này xác định các cách date attributes được lưu như thế nào trong database, cũng như format chúng khi model được serialize thành một array hay JSON:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

Attribute Casting

Thuộc tính $casts cung cấp một phương thức convert attributes thành các kiểu dữ liệu khác nhau khá tiện lợi. Thuộc tính $casts là một mảng có key là tên của attribute được cast, còn giá trị là kiểu dữ liệu bạn muốn cast. Các kiểu dữ liệu để cast được hỗ trợ bao gồm: integer, real, float, double, string, boolean, object, array, collection, date, datetime, và timestamp. Ví dụ, hãy cast attribute is_admin, được lưu trong database là integer (0 hoặc 1) thành boolean:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be casted to native types.
     *
     * @var array
     */
    protected $casts = [
        'is_admin' => 'boolean',
    ];
}

Lúc này, attribute is_admin sẽ luôn luôn được cast thành boolean khi bạn truy cập nó, thậm chí nếu giá trị nó được lưu trong database là kiểu integer:

$user = App\User::find(1);

if ($user->is_admin) {
    //
}

Array Casting

Casts kiểu array hữu dụng khi làm việc với các columns được lưu thành JSON. Ví dụ, nếu database có một trường kiểu text có chứa serialized JSON, thêm vào array cast lên attribute sẽ tự động de-serialize attribute thành mảng PHP khi bạn truy cập trong Eloquent model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be casted to native types.
     *
     * @var array
     */
    protected $casts = [
        'options' => 'array',
    ];
}

Khi mà cast đã được khai báo, bạn có thể lấy giá trị options và nó sẽ tự dộng deserialize từ JSON thành PHP array. Khi bạn gán giá trị vào options, nó sẽ tự động được serialize trở lại thành JSON để lưu vào trong cơ sở dữ liệu:

$user = App\User::find(1);

$options = $user->options;

$options['key'] = 'value';

$user->options = $options;

$user->save();

Kết thúc bài viết của mình ở đây. Thanks các bạn đã đón đọc (lay)

Tham khảo

Eloquent: Mutators: https://laravel.com/docs/5.3/eloquent-mutators