Laravel Collection làm việc với tập dữ liệu lớn

Trong quá trình phát triển ứng dụng, tôi chắc chắn rằng bạn đã từng phải xử lý các tình huống tính toán phức tạp với một tập dữ liệu, ví dụ như tính toán tổng tiền thanh toán theo từng người đặt hàng, hay tính tổng số view các bài viết theo từng giờ trong ngày… Thực sự những bài toán liên quan nhiều đến các thuật toán như sắp xếp, tìm kiếm, tính toán… như vậy đã làm mất không biết bao nhiêu thời gian. Nhận thấy việc cần tạo ra một thư viện các hàm phục vụ cho thao tác với tập dữ liệu, Laravel Collection là một lớp với cực nhiều các hàm được tạo sẵn và bạn yên tâm các bài toán trên sẽ không còn mất nhiều thời gian thực hiện nữa.

1. Laravel Collection là gì?

Collection trong Laravel là một lớp trong lập trình hướng đối tượng, nó thay thế cho các mảng truyền thống. Giống như mảng, Collection có nhiều các item thành phần, tuy nhiên các item này có thể là các đối tượng thay cho các kiểu dữ liệu đơn giản như String, Integer… Collection có nhiều ưu điểm trong mảng truyền thống:

  • Collection được thiết kế có thể thay đổi kích thước (số lượng các phần tử) và sử dụng bộ nhớ động.
  • Cung cấp các cấu trúc dữ liệu hữu dụng giúp lập trình nhanh hơn.
  • Collection hỗ trợ các phần tử không đồng nhất về kiểu, trong khi mảng là tập hợp các phần tử cùng kiểu.
  • Collection có tính mở rộng cao, có tính thích ứng.
  • Collection có các phương thức được xây dựng sẵn như sắp xếp, tìm kiếm.

Laravel Collection là một mở rộng của PHP Collection với số lượng các helper method lớn Laravel Collection giúp làm việc với mảng dữ liệu rất hiệu quả. Illuminate\Support\Collection class trong Laravel được implement một số các interface PHP như:

  • ArrayAccess: cung cấp khả năng truy xuất vào các đối tượng như mảng.
  • IteratorAggregate
  • JsonSerializable

2. Tạo ra Laravel Collection

2.1 Sử dụng phương thức collect() hoặc tạo mới một thực thể từ class

Để tạo một Collection chúng ta sử dụng helper method collect hoặc tạo một instance của lớp Illuminate\Support\Collection:

$newCollection = collect([1, 2, 3, 4, 5]);
$newCollection = new Collection([1, 2, 3, 4, 5]);

Collection cung cấp rất nhiều các helper method có sẵn, chúng ta sẽ kiểm tra xem các phương thức này hoạt động như thế nào. Để thực hành nhanh chúng ta sẽ sử dụng Tinker console (Xem Các công cụ debug và kiểm thử trong Laravel để biết thêm về Tinker). Đầu tiên chúng ta chạy Tinker console bằng lệnh:

c:\xampp\htdocs\laravel-test>php artisan tinker
Psy Shell v0.8.3 (PHP 5.6.20 ΓÇö cli) by Justin Hileman
>>>

Sau đó chúng ta tạo một collection để thực hiện kiểm tra các phương thức của Collection:

>>> $newCollection = collect([1, 2, 3, 4, 5]);
=> Illuminate\Support\Collection {#682
     all: [
       1,
       2,
       3,
       4,
       5,
     ],
   }
>>>

Chúng ta đã tạo một newCollection.

2.2 Laravel Eloquent Collection

Laravel Eloquent ORM là phần thực hiện mapping database thành các Model trong lập trình và từ đó thực hiện các thao tác dữ liệu với database thông qua Model một cách đơn giản. Các truy vấn thông qua Eloquent Model sẽ trả về một Collection và bạn hoàn toàn có thể xử lý dữ liệu trả về này trên ứng dụng:

$users = User::where('active', 1)->get();
$hobbies $users->sum(function ($user) {
    return count($user['hobby']);
});

Ví dụ trên đây $hobbies sẽ trả về tổng số sở thích của tất cả các user đang hoạt động, ví dụ:

  • User 1: Phim, Đá bóng
  • User 2: Đá bóng, Bơi lội, Đọc sách
  • User 3: Phim, Đọc sách

Kết quả trả về là 7.

3. Danh sách các phương thức Laravel Collection

Các phương thức trong Laravel Collection được chia thành các nhóm chức năng khác nhau, với cách phân chia theo nhóm bạn sẽ dễ dàng nắm bắt được ý nghĩa của từng phương thức.

3.1 Các phương thức tìm kiếm dữ liệu

Các phương thức trong nhóm chức năng tìm kiếm dữ liệu giúp bạn tìm ra một dữ liệu trong tập dữ liệu, các phương thức bao gồm: contains(), where(), search(), has() và một loạt các phương thức mở rộng khác như containsStrict(), whereIn(), whereNotIn(), whereInStrict(), whereNotInStrict()… Các phương thức này thường trả về kết quả dạng boolean đúng hoặc sai.

 

3.2 Các phương thức lọc dữ liệu

Với một tập dữ liệu cho trước chúng ta muốn lọc lấy các dữ liệu cần thiết có thể sử dụng filter(), all()

 

3.3 Các phương thức sắp xếp dữ liệu

sort(), sortBy(), sortByDesc()

 

3.4 Các phương thức nhóm dữ liệu

groupBy()

3.5 Các phương thức tách ghép, so sánh tập dữ liệu

take(), chunk(), collapse(), combine(), zip(), diff(), implode(), merge(), reverse(), split(), union()

3.6 Các phương thức tính toán và thống kê

average(), avg(), max(), min(), sum(), count(), every(), median() -> cực hay, random(), unique

3.7 Các phương thức lặp qua tập dữ liệu

map(), transform(), reduce(), each(), flatMap, mapWithKey(), reject()

3.8 Các phương thức chuyển đổi dạng dữ liệu

toArray(), toJson()

all()

Phương thức này trả về mảng các phần tử của collection

>>> $newCollection->all();
=> [
     1,
     2,
     3,
     4,
     5,
   ]
>>>

avg(), average()

Hai hàm này trả về giá trị trung bình các phần tử trong collection.

>>> $newCollection->avg();
=> 3
>>>

chunk()

Phương thức này chia collection thành nhiều các collection nhỏ hơn với kích thước collection được đưa vào trước.

>>> $chunks = $newCollection->chunk(2);
=> Illuminate\Support\Collection {#695
     all: [
       Illuminate\Support\Collection {#693
         all: [
           1,
           2,
         ],
       },
       Illuminate\Support\Collection {#692
         all: [
           2 => 3,
           3 => 4,
         ],
       },
       Illuminate\Support\Collection {#694
         all: [
           4 => 5,
         ],
       },
     ],
   }
>>> $chunks->all();
=> [
     Illuminate\Support\Collection {#693
       all: [
         1,
         2,
       ],
     },
     Illuminate\Support\Collection {#692
       all: [
         2 => 3,
         3 => 4,
       ],
     },
     Illuminate\Support\Collection {#694
       all: [
         4 => 5,
       ],
     },
   ]
>>>

Biến $chunks sau khi sử dụng phương thức chunk() sẽ chứa 3 collection con. Phương thức này rất hữu dụng khi sử dụng trong các view dùng với Bootstrap, ví dụ: bạn muốn in các sản phẩm ra màn hình theo kiểu lưới, mỗi hàng có 4 sản phẩm:

@foreach ($products->chunk(4) as $chunk)
	<div class="row">
		@foreach ($chunk as $product)
			<div class="col-md-3">
				<img src="{{ Asset($product->image_path) }}"><br>
				{{ $product->name }}<br>
				{{ number_format($product->price) }} VNĐ
			</div>
		@endforeach
	</div>
@endforeach

collapse()

Nếu một collection có các phần tử là các mảng, nếu sử dụng collapse() nó sẽ tạo một mảng duy nhất nằm trong collection

>>> $newCollection = collect([[1,2,3], [4,5,6], [7,8,9]]);
=> Illuminate\Support\Collection {#718
     all: [
       [
         1,
         2,
         3,
       ],
       [
         4,
         5,
         6,
       ],
       [
         7,
         8,
         9,
       ],
     ],
   }
>>> $collapsed = $newCollection->collapse();
=> Illuminate\Support\Collection {#713
     all: [
       1,
       2,
       3,
       4,
       5,
       6,
       7,
       8,
       9,
     ],
   }
>>> $collapsed->all();
=> [
     1,
     2,
     3,
     4,
     5,
     6,
     7,
     8,
     9,
   ]
>>>

combine()

Phương thức này sử dụng để combine key với giá trị trong collection từ hai mảng khác nhau:

>>> $keyCollection = collect(['name', 'version']);
=> Illuminate\Support\Collection {#706
     all: [
       "name",
       "version",
     ],
   }
>>> $combined = $keyCollection->combine(['Laravel', 'Laravel 5.4']);
=> Illuminate\Support\Collection {#716
     all: [
       "name" => "Laravel",
       "version" => "Laravel 5.4",
     ],
   }
>>> $combined->all();
=> [
     "name" => "Laravel",
     "version" => "Laravel 5.4",
   ]
>>>

contains()

Phương thức này để kiểm tra xem collection có chứa một phần tử nào đó không.

>>> $frameworks = collect([
		['name' => 'Laravel', 'version' => '5.4'],
		['name' => 'Symfony', 'version' => '3.2'],
		['name' => 'CodeIgniter', 'version' => '3.1.4'],
		['name' => 'Yii', 'version' => '2.0.11']
	]);
=> Illuminate\Support\Collection {#731
     all: [
       [
         "name" => "Laravel",
         "version" => "5.4",
       ],
       [
         "name" => "Symfony",
         "version" => "3.2",
       ],
       [
         "name" => "CodeIgniter",
         "version" => "3.1.4",
       ],
       [
         "name" => "Yii",
         "version" => "2.0.11",
       ],
     ],
   }
>>> $frameworks->contains(['name' => 'Laravel', 'version' => '5.4']);
=> true
>>> $frameworks->contains(['name' => 'PHPCake', 'version' => '3.1']);
=> false
>>>

containsStrict()

Giống như phương thức contains() ở trên nhưng khi thực hiện so sánh có yêu cầu khắt khe hơn. Ví dụ:

>>> $frameworks->contains(['name' => 'Laravel', 'version' => '5.400000']);
=> true
>>> $frameworks->containsStrict(['name' => 'Laravel', 'version' => '5.400000']);

=> false
>>>

Nó cũng giống như khi so sánh chúng ta sử dụng == và != sẽ khác với === và !==.

count()

Phương thức này trả về số phần tử trong Collection.

>>> $frameworks->count();
=> 4
>>>

diff()

Phương thức này so sánh các phần tử của hai collection dựa trên giá trị của phần tử.

>>> $collection = collect([1, 2, 3, 4, 5]);
=> Illuminate\Support\Collection {#691
     all: [
       1,
       2,
       3,
       4,
       5,
     ],
   }
>>>
>>> $diff = $collection->diff([2, 4, 6, 8]);
=> Illuminate\Support\Collection {#715
     all: [
       0 => 1,
       2 => 3,
       4 => 5,
     ],
   }
>>>
>>> $diff->all();
=> [
     0 => 1,
     2 => 3,
     4 => 5,
   ]
>>>

diffKeys()

So sánh hai collection dựa trên key.

>>> $collection = collect([
     'one' => 10,
     'two' => 20,
     'three' => 30,
     'four' => 40,
     'five' => 50,
 ]);
=> Illuminate\Support\Collection {#682
     all: [
       "one" => 10,
       "two" => 20,
       "three" => 30,
       "four" => 40,
       "five" => 50,
     ],
   }
>>>
>>> $diff = $collection->diffKeys([
     'two' => 2,
     'four' => 4,
     'six' => 6,
     'eight' => 8,
 ]);
=> Illuminate\Support\Collection {#711
     all: [
       "one" => 10,
       "three" => 30,
       "five" => 50,
     ],
   }
>>>
>>> $diff->all();
=> [
     "one" => 10,
     "three" => 30,
     "five" => 50,
   ]
>>>

each()

Phương thức này lặp qua các phần tử của collection và đưa các phần tử này vào làm tham số của một hàm callback.

>>> $collection = collect([1, 2, 3, 4, 5]);
=> Illuminate\Support\Collection {#701
     all: [
       1,
       2,
       3,
       4,
       5,
     ],
   }
>>> $collection = $collection->each(function ($value, $key) {
     if($value > 3){
             echo 'item > 3';
     } else {
             echo 'item < 3';
     }
 });
item < 3item < 3item < 3item > 3item > 3ΓÅÄ
=> Illuminate\Support\Collection {#701
     all: [
       1,
       2,
       3,
       4,
       5,
     ],
   }
>>>

every()

Phương thức này sử dụng để kiểm tra tất cả các phần tử của Collection

>>> $collection = collect([1, 2, 3, 4, 5]);
=> Illuminate\Support\Collection {#698
     all: [
       1,
       2,
       3,
       4,
       5,
     ],
   }
>>> $collection = $collection->every(function ($value, $key) {
     return $value > 3;
 });
=> false
>>>

Kiểm tra xem có phải các phần tử của collection có lớn hơn 3 hay không, câu trả lời là false.

except()

Trả về các phần tử ngoại trừ các phần tử được liệt kê trong một collection

>>> $collection = collect(['product_id' => 1, 'price' => 100, 'discount' => fals
e]);
=> Illuminate\Support\Collection {#689
     all: [
       "product_id" => 1,
       "price" => 100,
       "discount" => false,
     ],
   }
>>>
>>> $filtered = $collection->except(['price', 'discount']);
=> Illuminate\Support\Collection {#693
     all: [
       "product_id" => 1,
     ],
   }
>>>
>>> $filtered->all();
=> [
     "product_id" => 1,
   ]
>>>

filter()

Phương thức này sử dụng để lọc các phần tử trong collection

>>> $collection = collect([1, 2, 3, 4]);
=> Illuminate\Support\Collection {#687
     all: [
       1,
       2,
       3,
       4,
     ],
   }
>>> $filtered = $collection->filter(function ($value, $key) {
     return $value > 2;
 });
=> Illuminate\Support\Collection {#702
     all: [
       2 => 3,
       3 => 4,
     ],
   }
>>> $filtered->all();
=> [
     2 => 3,
     3 => 4,
   ]
>>>

Chú ý, các phần tử nào khi qua hàm callback có giá trị là false sẽ bị loại bỏ, xem ví dụ tiếp theo bạn sẽ thấy điều này rất rõ:

>>> $collection = collect([1, 2, 3, null, false, '', 0, []]);
=> Illuminate\Support\Collection {#707
     all: [
       1,
       2,
       3,
       null,
       false,
       "",
       0,
       [],
     ],
   }
>>> $collection->filter()->all();
=> [
     1,
     2,
     3,
   ]
>>>

first()

Phương thức này trả về phần tử đầu tiên của collection vượt qua được kiểm tra

>>> collect([1, 2, 3, 4])->first(function ($value, $key) {
     return $value > 2;
 });
=> 3
>>>

4. Lời kết

Nếu bạn đã từng dùng Lodash khi viết Javascript thì Laravel Collection là một thư viện tương tự cho PHP, thật tuyệt vời phải không, sử dụng hợp lý các phương thức này giúp bạn giảm được thời gian thiết kế thuật toán, viết code và đặc biệt ứng dụng của bạn đã được tối ưu cực tốt với các phương thức sẵn có này.

Add Comment