+32

Tìm hiểu về Trait trong PHP

Hôm nay mình sẽ giới thiệu một khái niệm đã được PHP giới thiệu từ PHP 5.4 trở đi. Đây là một khái niệm theo mình nghĩ là hay, và để biết nó hay như thế nào thì chúng ta cùng tìm hiểu qua bài này nhé 😄!

Trait là gì ?

PHP hay Ruby đều là những ngôn ngữ hướng đối tượng chỉ hỗ trợ single inheritance(đơn kế thừa). Để khắc phục những giới hạn của đơn kế thừa trong việc sử dụng lại source code, từ PHP 5.4 trở đi PHP hỗ trợ Traits là cơ chế giúp cho lập trình viên có thể sử dụng lại các phương thức từ các class khác nhau một cách dễ dàng hơn. Một trait tương tự như là 1 class nhưng chỉ nhằm mục đích nhóm chức năng lại. Và trait không thể khởi tạo giống class và trait sinh ra để bổ sung cho kế thừa truyền thống. Thay vì phải kế thừa 1 class hay interface để sử dụng lại 1 nhóm chức năng, thì với trait bạn không cần phải kế thừa vẫn có thể sử dụng được

Đọc xong khái niệm chắc còn mơ hồ lắm đúng không nào. Ok, chúng ta tìm hiểu cách sử dụng Trait qua các ví dụ sau đây nhé:

Ví dụ 1: Minh họa về Trait

Giả sử ta có các class sau:

class Database
{
    public function listUsers()
    {
        return "List users!";
    }
}

class Users
{
}

class Report
{

}

Đặt vấn đề :

Nếu ta muốn sử dụng lại method listUsers trong class Database cho class Users hoặc class Report thì làm thế nào ? Trong trường hợp này thì chắc chắn các bạn sẽ nghĩ đến từ khóa extends đúng không? Và sẽ viết như thế này:

class Database
{
    public function listUsers()
    {
        return "List users!";
    }
}

class Report extends Database
{
    public function reportUsers()
    {
        $this->listUsers();
    }
}

class Users extends Database
{
    public function index()
    {
        $this->listUsers();
    }
}

Nếu trường hợp nào cũng viết thế này được thì Trait dùng chỗ nào cho hợp lý !!?? À, đây các bạn, với vấn đề mà mình đã đặt ra ngay từ đầu thì mình có thể dùng Trait để xử lý, nhanh, gọn, lẹ, đỡ tốn tài nguyên. Vậy code thế nào, mình xin giới thiệu đây ạ.

trait Database
{
    public function listUsers()
    {
        return "List users!";
    }
}

class Users
{
    use Database;

    public function reportUsers()
    {
        $this->listUsers();
    }
}

class Report
{
    use Database;

    public function index()
    {
        $this->listUsers();
    }
}
  • Qua ví dụ này chúng ta thấy không cần extends nhưng chúng ta vẫn sử dụng được các method listUsers() ở các class như , Users, ... đúng không các bạn!! Đấy, các bạn thấy sự tiện lợi chưa nào 😃.

  • Để sử dụng trait trong PHP thì các bạn chỉ cần dùng từ khóa use để gọi trait bạn muốn sử dụng trong code của bạn. Sau đó bạn có thể sử dụng các phương thức trong trait mà bạn đã use

  • Vấn đề tiếp theo, vậy tôi có nhiều trait thì tôi phải sử dụng có được không, và sử dụng thế nào !? Chúng ta cùng đi tiếp nhé 😃

*** Sử dụng nhiều trait trong 1 class ?***

trait Database
{
    public function listUsers()
    {
        return "List users!";
    }
}

trait Authenticate
{
    public function authorize()
    {
        return "Authorized!";
    }
}
class Users
{
}

Đặt vấn đề :

Nếu ta muốn sử dụng lại method listUsers trong trait Database và sử dụng method authorize trong trait Authenticate cho class Users thì làm thế nào ? Các bạn không phải lo lắng về điều đó, PHP hỗ trợ hết cho các bạn. Hãy theo dõi ví dụ dưới đây để hiểu rõ hơn về trường hợp này nào !! 😃

trait Database
{
    public function listUsers()
    {
        return "List users!";
    }
}

trait Authenticate
{
    public function authorize()
    {
        return "Authorized!";
    }
}

class Users
{
    use Database, Authenticate;

    public function __construct()
    {
        $this->authorize();
    }

    public function index()
    {
        $this->listUsers();
    }
}
  • Qua ví dụ trên hẳn là các bạn đã hiểu được cách sử dụng đúng không nào, chúng ta vẫn dùng từ khóa use với tên các trait được ngăn cách bằng dấu phẩy.
  • Nhìn có vẻ ổn đấy, nhưng thử suy nghĩ thêm một tí nữa, chẳng hạn trong các trait lại có cùng tên method, mình muốn sử dụng method A của class A. Nhưng, trong trait B cũng có method A. Vậy giải quyết như thế nào đây ? Đừng lo lắng các bạn à, PHP cũng tính toán trường hợp này rồi 😃. Để hiểu được cách giải quyết thì các bạn hãy theo dõi ví dụ sau nhé:
trait Admin
{
    public function checkLogin()
    {
        return "Check login";
    }

    public function isAdmin()
    {
        return "Ok";
    }
}

trait Authenticate
{
    public function checkLogin()
    {
        return "Check login";
    }

    public function isAdmin()
    {
        return "Ok";
    }
}

class Users
{
}

Đặt vấn đề :

  1. Trong class Users tôi muốn sử dụng cả 2 trait.
  2. Tôi muốn sử dụng method checkLogin trong trait Authenticate
  3. Tối muốn sử dụng method isAdmin trong trait Admin Để giải quyết các vấn đề trên thì tôi phải làm thế nào? Nhất là khi các method ở 2 trait đều giống nhau! Hãy theo dõi ví dụ dưới đây các bạn sẽ lần lượt solve các problem trên 😄
trait Admin
{
    public function checkLogin()
    {
        return "Check login";
    }

    public function isAdmin()
    {
        return "Ok";
    }
}

trait Authenticate
{
    public function checkLogin()
    {
        return "Check login";
    }

    public function isAdmin()
    {
        return "Ok";
    }
}

class Users
{
    use Authenticate, Admin
    {
        Authenticate::checkLogin insteadof Admin;
        //Sử dụng method checkLogin ở trait Authenticate thay cho  method checkLogin ở trait Admin
        Admin::isAdmin insteadof Authenticate
        //Sử dụng method isAdmin ở trait Admin thay cho method isAdmin ở trait Authenticate
    }

    public function __construct()
    {
        $this->checkLogin();
        $this->isAdmin();
    }
}
  • Qua ví dụ lần này thì các bạn đã hiểu về cách solve conflict method giữa các trait khi sử dụng nhiều trait trong 1 class rồi đúng không nào.
  • Chúng ta chỉ cần dùng từ khóa insteadof để giải quyết trường hợp này 😃
  • Xong vấn đề rồi đúng không nào. Ok, nhưng để pro hơn về trait thì mình giới thiệu về một đặc điểm nữa của trait trong PHP đó là

*** Trait lồng trait ***

Đặt vấn đề :

  • Giả sử tôi có 3 trait: trait Authenticate, Admin, Permisson.
  • Nếu tôi sử dụng cả 3 trait này vào 1 class mà tôi muốn dùng thì theo cách thông thường thì dùng từ khóa use đúng không nào.
  • Câu hỏi đặt ra là có cách gì đơn giản hơn không, lỡ tôi muốn dùng 10 trait thì không lẽ use từng trait vào class. Nhìn rất chuối đúng không? Vậy có cách nào đơn giản hơn không ? Dĩ nhiên là có rồi, cách giải quyết như sau 😄
trait Admin
{
    public function checkLogin()
    {
        return "Check login";
    }
}

trait Permission
{
    public function checkPermission()
    {
        return "permission";
    }
}

trait Authenticate
{
    use Admin, Permission;
    //chỉ cần gọi các trait vào bằng từ khóa use là giải quyết xong problem
}
class Users
{
    use Authenticate;

    public function __construct()
    {
        $this->checkLogin();
        $this->isAdmin();
    }
}

Ngoài ra còn một số vấn đề nữa liên quan tới trait như:

  • khai báo abstract, static method hay properties trong trait
  • Thay đổi tính Visibility của method

Những vấn đề này trong bài viết kế tiếp mình sẽ giới thiệu, đồng thời mình cũng sẽ giới thiệu thêm các vấn đề như:

  • Trait khác gì với Abstract và Interface.
  • Giới thiệu các trait trong Laravel

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.