Nếu bạn từng làm việc với Query Builder của Laravel hay lớp Model tương tự trong các MVC framework khác thì hẳn bạn đã quen với những dòng code sau:
$users = DB::table('users') ->select(DB::raw('count(*) as user_count, status')) ->where('status', '<>', 1) ->groupBy('status') ->get();
Có bao giờ bạn thắc mắc tại sao chúng lại có thể gọi nối tiếp nhau như thế? Hãy thử nghiên cứu nội dung của method where
trong đoạn code trên:
public function where($column, $operator = null, $value = null, $boolean = 'and') { if ($column instanceof Closure) { $query = $this->model->newQueryWithoutScopes(); $column($query); $this->query->addNestedWhereQuery($query->getQuery(), $boolean); } else { $this->query->where(...func_get_args()); } return $this; }
Bạn có thể thấy method where
trả về giá trị $this
. Điều này cho phép bạn gọi nối tiếp những method khác của object này mà không cần phải kết thúc câu lệnh.
Method chaning là gì?
Method chaining, also known as named parameter idiom, is a common syntax for invoking multiple method calls in object-oriented programming languages. Each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results.[1]Local variable declarations are syntactic sugar because of the difficulty humans have with deeply nested method calls.[2][3] A method chain is also known as a train wreck due to the increase in the number of methods that come one after another in the same line that occurs as more methods are chained together[4] even though line breaks are often added between methods.
Đoạn trên mình copy y nguyên từ Wikipedia. Giải thích một cách đơn giản thì chaning method sẽ trả về một object (thường là chính object Class chứa method đó) khiến ta có thể gọi method nối tiếp mà không cần phải xuống dòng.
Ví dụ đơn giản về method chaning
Một class bình thường không chứa chaning method sẽ như sau:
class Person { protected $name; protected $age; public function setName($name) { $this->name = $name; } public function setAge($age) { $this->age = $age; } public function sayHello() { return "Hello, my name is " . $this->name . " and I am " . $this->age . " years old."; } }
Khi đó cách sử dụng class này sẽ là:
$person = new Person; $person->setName("Peter"); $person->setAge(21); $person->sayHello(); // Hello, my name is Peter and I am 21 years old
Áp dụng method chaning ta thêm return $this
vào cuối hai method setName
và setAge
:
class Person { protected $name; protected $age; public function setName($name) { $this->name = $name; return $this; } public function setAge($age) { $this->age = $age; return $this; } public function sayHello() { return "Hello, my name is " . $this->name . " and I am " . $this->age . " years old."; } }
Việc này rút gọn cách sử dụng xuống một dòng:
$person = (new Person())->setName("Peter")->setAge(21)->sayHello(); // Hello, my name is Peter and I am 21 years old
Khi nào thì sử dụng method chaning?
Theo kinh nghiệm của tôi thì bạn không nên dùng method chaning nếu không thực sự cần thiết (cho phần lớn trường hợp). Việc trả về object
làm mất đi tính solid
của method và nếu dùng không cẩn thận sẽ gây khó hiểu, kiểu như “tại sao hàm này lại trả về object
?” Method chaining dùng trong các hàm set thuộc tính của object, build query cho lớp Model là hợp lý và tiết kiệm code (tận dụng các hàm set vì thường chúng sẽ không return
gì cả).
Tham khảo:
https://en.wikipedia.org/wiki/Fluent_interface
https://en.wikipedia.org/wiki/Method_chaining
https://stackoverflow.com/questions/1103985/method-chaining-why-is-it-a-good-practice-or-not/
https://softwareengineering.stackexchange.com/questions/150165/when-using-method-chaining-do-i-reuse-the-object-or-create-one