Tập tành chọt Where collection laravel, hãy cẩn thận

2nd Nov 2022
Tập tành chọt Where collection laravel, hãy cẩn thận
Table of contents

Đặt vấn đề

Trên cở sở dữ liệu mình có một bảng là hp_stores (lưu thông tin của dữ liệu của store), bảng gồm các trường như idname và slug, cột slug là kiểu varchar. Mỗi store sẽ có các slug khác nhau khi lấy các store mình sẽ dựa và cột slug chứ không phải là cột id. Trong bảng hp_stores hiện có hai hàng với hai slug là 6969 và 06969. Yêu cầu đặt ra là lấy các store với slug là 6969 thì mình có hai cách lấy như sau:

  1. HpStore::where('slug', '6969')->get();
  2. HpStore::get()->where('slug', '6969');

Theo các bạn hai cách trên có cho ra kết quả giống nhau không :)

Giải đáp

Với cách 1 thì kết quả sẽ ra một store với slug là 6969 thì không có vẫn đề gì đúng như kết quả chúng ta mong muốn.

Với cách thứ hai thì kết quả lại ra hai store với 1 store có slug = 06969 và một store có slug = 6969 woowww, tại sao lại như vậy? Có phải laravel có vấn đề! Kết quả là không. Với cách thử nhất chúng ta đang sử điều kiện where trên database còn với cách thứ hai chúng ta sử dụng where trên collection và lúc này where trên collection sẽ có điểm khác. Vậy điểm khác ở đâu thì chúng ta sẽ vào xem laravel định nghĩa hàm where collection như thế nào. Sau một hồi đào bới và tìm kiếm thì mình cũng thấy định nghĩa của nó.

public function where($key, $operator, $value = null)
{
	return $this->filter($this->operatorForWhere(...func_get_args()));
}

Hàm `filter` thì dễ hiểu rồi vậy còn hàm operatorForWhere, chúng ta tiếp tục tìm hàm đó xem laravel định nghĩa như thế nào và đây là kết quả:

protected function operatorForWhere($key, $operator, $value = null)
    {
        if (func_num_args() == 2) {
            $value = $operator;

            $operator = '=';
        }

        return function ($item) use ($key, $operator, $value) {
            $retrieved = data_get($item, $key);

            $strings = array_filter([$retrieved, $value], function ($value) {
                return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
            });

            if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
                return in_array($operator, ['!=', '<>', '!==']);
            }

            switch ($operator) {
                default:
                case '=':
                case '==':  return $retrieved == $value;
                case '!=':
                case '<>':  return $retrieved != $value;
                case '<':   return $retrieved < $value;
                case '>':   return $retrieved > $value;
                case '<=':  return $retrieved <= $value;
                case '>=':  return $retrieved >= $value;
                case '===': return $retrieved === $value;
                case '!==': return $retrieved !== $value;
            }
        };
    }

Thật phức tạp đúng không mọi người :). Để hiểu đơn giản thì các bạn chỉ cần nhớ hàm này trả về một Closure để phục vụ cho hàm filter ở bên ngoài và một điểm chú ý nữa là ở chỗ switch case chúng ta thấy nếu ($operator = null, =, ==) thì hàm sẽ dùng so sánh ==. Vậy vấn đề đã dần sáng tỏ, với cách hai chúng ta dùng where collection với điều kiện slug là 6969 chúng ta không truyền operator vào tức là mặc định nó là null mà là null thì sẽ vào case return $retrieved == $value. Mà như các bạn biết thì trong php chuỗi "6969" == "06969" sẽ trả về true => Lúc này sẽ có hai store được trả về.

Kết luận

Để tránh những trường hợp như trên thì các bạn hãy cận thận khi dùng điều kiện trên collection, thay vì where trên collection thì bạn có thể dùng điều kiện trên database sẽ cho kết quả chuẩn hơn và tốc độ xử lý trên database cũng nhanh hơn và chiếm ít bộ nhớ hơn (Cái này các bạn có thể sử dụng laravel debug bar để test).

Nếu trong các trường hợp đặc biệt bắt buộc các bạn phải where trên collection thì các bạn hay dùng whereStrict hoặc dùng where thì hãy chỉ định operator cụ thể.

Bạn thấy bài viết này như thế nào?
0 reactions

Add new comment

Image CAPTCHA
Enter the characters shown in the image.

Related Articles

Mỗi kết nối cơ sở dữ liệu được định nghĩa trong một mảng, với tên kết nối là khóa của mảng

Eager Loading là một kỹ thuật tối ưu hóa truy vấn cơ sở dữ liệu trong Laravel, giúp tăng tốc độ truy vấn và giảm số lượng truy vấn cần thiết để lấy dữ liệu liên quan đến một bản ghi.

Để sử dụng Eager Loading với điều kiện trong Laravel, bạn có thể sử dụng phương thức whereHas hoặc orWhereHas trong Eloquent Builder.

E hiểu đơn giản vầy nha. auth() hay Auth trong laravel là những function global hay class, nó cũng chỉ là 1 thôi

Xin chào các bạn, tuần này mình sẽ viết một bài về cách xử lý Real Time(thời gian thực) với Laravel và Pusher